golang go语言_在Go语言中无需反思即可使用Lodash的好处

golang go语言

by Tal Kol

通过塔尔科尔

在Go语言中无需反思即可使用Lodash的好处 (The benefits of using Lodash in the Go language without reflection)

Working with Node.js, I’ve grown to rely on Lodash as an invaluable tool. It completes the JavaScript standard library with a set of handy functional operators over collections. I can’t recall a single JavaScript project I’ve worked on in recent years that hasn’t used it.

使用Node.js,我已经越来越依赖Lodash作为一种宝贵的工具。 它通过集合上的一组方便的函数运算符完善了JavaScript标准库。 我不记得最近几年没有使用过的一个JavaScript项目。

My experience of switching to Go has been very pleasant. Go resolves many of the issues I’ve had with Node.js over the years and yet remains as productive. I’ve been sorely missing one thing though — a library like Lodash.

我切换到Go的经历非常愉快。 Go解决了我多年来使用Node.js遇到的许多问题,但仍然保持了很高的生产率。 我一直很想念一件事-像Lodash这样的图书馆。

Lodash有什么了不起的? (What’s so great about Lodash?)

JavaScript isn’t a purely functional language, and most of the code I write in JavaScript tends to be imperative. Nevertheless, some principles of functional programming (like chaining operators and immutability) come in very handy when working with collections.

JavaScript并不是纯粹的功能语言,我用JavaScript编写的大多数代码都势在必行。 但是,在使用集合时,一些函数式编程原理(例如链接运算符和不变性)会非常方便。

Let’s say I have an array with some duplicates I want to remove. All it takes is one line with Lodash:

假设我有一个要删除的重复项数组。 Lodash只需要一行即可:

Let’s say that I only want to keep colors that have names that are longer than 4 letters. This takes one line as well:

假设我只想保留名称长于4个字母的颜色。 这也需要一行:

And now assume I want to capitalize the first letter of every color… yep, not more than one line. I can even do all of the above together:

现在,假设我想将每种颜色的首字母大写……是的,最多一行。 我什至可以同时完成以上所有操作:

Lodash is probably the handiest tool for working with collections in JavaScript.

Lodash可能是使用JavaScript集合的最方便的工具。

您将如何在Go中做到这一点? (How would you do that in Go?)

Let’s start with the first task: getting a unique slice. Googling for a best practice solution in Go yields this blog post as a first result. This is the code, credits to Kyle Banks:

让我们从第一个任务开始:获取唯一的切片。 对于谷歌搜索在Go最佳实践的解决方案产生了这个博客帖子的第一个结果。 这是代码,贷记给Kyle Banks

You can imagine that if we had to filter and capitalize the first letter of every color as well, we would spend way too much time on the mechanics of these actions. This would be a bit exhausting. Where are my one liners?

您可以想象,如果我们也必须过滤和大写每种颜色的首字母,那么我们将花费太多时间在这些动作的机制上。 这会有点累。 我的一支班轮在哪里?

救援图书馆 (Libraries to the rescue)

Surely, there are some libraries that do these convenient collection actions for us. Surprisingly, there aren’t many popular ones in Go. Why is that?

当然,有一些库可以为我们执行这些便捷的收集操作。 令人惊讶的是,Go中没有很多流行的。 这是为什么?

To make such a library useful, it would have to support many types of collections. This is because you may have a slice of strings, a slice of integers, or a slice of structs. Supporting a generic type of slice isn’t straightforward in Go.

为了使这样的库有用,它必须支持多种类型的集合。 这是因为您可能有一个字符串切片,一个整数切片或一个结构切片。 在Go中,支持通用类型的slice并不容易。

In most strictly typed languages, this is achieved with a language construct called generics. Unfortunately, Go doesn’t currently support it.

在大多数严格类型的语言中,这是通过称为generics的语言构造实现的。 不幸的是,Go当前不支持它

So how can we implement such a library without generics? The Go guru Rob Pike shows an example implementation of the function filter in this Github repo. Notice the heavy use of reflection. Indeed, it’s not difficult to expand this technique and implement the various Lodash utility methods. You can see an example project that tries to do exactly that here.

那么,如何在没有泛型的情况下实现这样的库呢? Go大师Rob Pike在此Github存储库中展示了函数filter的示例实现。 注意反射的大量使用。 确实,扩展这项技术并实现各种Lodash实用程序方法并不难。 您可以在此处看到一个示例项目,尝试完全做到这一点

为什么我们更希望避免反思? (Why should we prefer to avoid reflection?)

Reflection examines types in runtime. By working our way around Go’s lack of generics with reflection, we’re moving the heavy lifting of dealing with multiple collection types from compile time to run time.

反射检查运行时中的类型。 通过解决Go缺少具有反射的泛型的方法,我们将处理多种集合类型的繁重工作从编译时转移到了运行时。

Basic utility functions that work with collections should be efficient. I haven’t done proper benchmarking, but relying on reflection for such a common task feels plain wrong.

与集合一起使用的基本实用程序功能应该是高效的。 我没有进行适当的基准测试,但是依靠反射来完成这样的常见任务感觉很不对劲。

So, as a thought experiment, what can we do instead? Can we create a truly efficient implementation of Lodash in Go that will rival the plain old for loop approach performance-wise?

那么,作为思想实验,我们能做什么呢? 我们是否可以在Go中创建一个真正有效的Lodash实现,使其在性能方面可以与普通的循环方法相媲美?

编译时间码生成 (Compile time code generation)

Generating code as part of the development toolchain isn’t a foreign concept in Go. It is used by the Protobuf compiler to generate Go access methods for protocol definitions. It has even been introduced as an official Go toolchain feature with go generate since version 1.4.

在开发工具链中生成代码并不是Go的外来概念。 Protobuf编译器使用它来生成用于协议定义的Go访问方法。 自1.4版以来,它甚至已作为go generate的官方Go工具链功能引入。

What if we wish to move the heavy lifting of dealing with generic collection types back to compile time? The best way to do this (currently) is to generate type-specific code for every one of the types we need in our project!

如果我们希望将处理通用集合类型的繁重工作移回编译时该怎么办? 最好的方法(当前)是为项目中需要的每种类型生成特定于类型的代码!

Consider the implementation from before of uniq over a string slice:

考虑uniq之前在string切片上的实现:

How would the implementation differ if we needed support for an int slice?

如果我们需要对int slice的支持,实现会有什么不同?

As you can see, it’s pretty much identical, except every occurrence of string has been replaced with int.

如您所见,它几乎是相同的,除了每次出现的string都已被int代替。

Can we do this automatically somehow?

我们可以以某种方式自动执行此操作吗?

Lodash在Go中没有思考 (Lodash in Go without reflection)

Let’s design our API first. We can turn for inspiration to the original Lodash implementation in JavaScript. There, the library is used with the underscore character _. For example, _.uniq().

让我们先设计我们的API。 我们可以从JavaScript的原始Lodash实现中获取灵感。 在那里,该库与下划线字符_ 。 例如, _.uniq() . _.uniq()

We can pay homage by keeping the same convention. The difference is that in our case, the underscore will be followed by a type. For example, _int.Uniq() for integers and _string.Uniq() for strings. We must specify the type explicitly since we’re going to get a completely new and dedicated implementation of the entire library for the specific type we need. This will guarantee that our runtime is as efficient as possible.

我们可以通过保持相同的惯例表示敬意。 区别在于,在我们的例子中,下划线后跟一个类型。 例如, _int.Uniq()用于整数, _string.Uniq()用于字符串。 我们必须明确指定类型,因为我们将针对所需的特定类型获得整个库的全新且专用的实现。 这将确保我们的运行时尽可能高效。

Usage is very straightforward. Just import the implementation you want:

用法非常简单。 只需导入所需的实现即可:

There are dozens of potential types. Does that mean that our go-dash/slice library has to come with every single one? Not really, as this wouldn’t be practical. We’re going to generate the required implementations dynamically at compile time!

有数十种潜在类型。 这是否意味着我们的go-dash/slice库必须与每个库一起提供? 并非如此,因为这不切实际。 我们将在编译时动态生成所需的实现!

To achieve that, we’ll introduce an interesting command line tool: _gen, the code generator for our Lodash library implementation (notice how it starts with underscore as well).

为此,我们将引入一个有趣的命令行工具: _gen ,这是我们的Lodash库实现的代码生成器(请注意,它也如何以下划线开头)。

When _gen is run in the source root of any project, it goes over all the source files in the project and looks for github.com/go-dash/slice/_TYPE imports. In the example above, it will find one for _string and one for _int. The generator will then generate an implementation for these specific types dynamically and add it to the library found in the Go workspace under the path$GOPATH/src/github.com/go-dash/slice.

_gen在任何项目的源根目录中运行时,它将遍历该项目中的所有源文件,并寻找github.com/go-dash/slice/_TYPE导入。 在上面的示例中,它将为_string_int找到一个。 然后,生成器将动态生成这些特定类型的实现,并将其添加到Go工作区中位于路径$GOPATH/src/github.com/go-dash/slice

The implementation of go-dash/slice on Github is actually very lean. It only contains a templated implementation for a single generic type. The code generator relies on this template to create the specific type implementations according to your requirements when you compile your project.

Github上执行go-dash/slice实际上非常精简。 它仅包含单个泛型类型的模板实现。 在编译项目时,代码生成器依靠此模板根据您的要求创建特定的类型实现。

那自定义类型呢? (What about custom types?)

Assume your project defines a custom complex type like Person:

假设您的项目定义了一个自定义复杂类型,例如Person

It would be quite convenient if we could use our Lodash library on a Person slice as well. Well, this actually can work in the exact same way. Simply import an implementation of go-dash/slice that works on Person and the code generator will take care of the rest:

如果我们也可以在Person切片上使用Lodash库,那将非常方便。 好吧,这实际上可以完全相同的方式工作。 只需导入对Person起作用的go-dash/slice的实现,代码生成器将负责其余工作:

工作原型 (A working prototype)

A working proof of concept of the go-dash/slice library, that provides several useful functions like uniq, filter and chain, is available on Github.

Github上提供了go-dash/slice库的概念工作证明,该库提供了uniqfilterchain等几个有用的功能。

The project also includes a working command line generator.

该项目还包括一个工作命令行生成器

The command line generator is conveniently installable on Mac via Homebrew: brew install go-dash/tools/gen

命令行生成器可通过Homebrew在Mac上方便地安装: brew install go-dash/tools/gen

Going back to our first example: let’s say we have a slice of string color names which we want to keep unique and filter for colors with length longer than 4 letters. We can finally implement this in one line:

回到我们的第一个示例:假设我们有一片字符串颜色名称,我们希望它们保持唯一性,并过滤长度超过4个字母的颜色。 我们最终可以在一行中实现:

If you like this direction and want to come help port the rest of the useful Lodash functions to Go, please contribute.

如果您喜欢这个方向,并希望帮助将Lodash的其他有用功能移植到Go中,请贡献力量。

关于语法的一些最终想法 (Some final thoughts about syntax)

The main thing that still bothers me with our chosen syntax is that we have an import per type. Is it possible to combine them somehow to a single implementation that will route to the correct place by type?

我们选择的语法仍然困扰着我,主要是每个类型都有一个导入。 是否可以将它们以某种方式组合到单个实现中,该实现将按类型路由到正确的位置?

Consider the following:

考虑以下:

This code combines all of the implementations into a unified Uniq that takes interface{}. While it doesn’t use reflection per se, it still has a two runtime dynamic casts and the switch which will affect performance. We should probably benchmark to see how much of an impact this adds. We should also probably benchmark the reflection implementation and see if our suspicion about runtime performance was indeed grounded in the first place.

此代码将所有实现组合到采用interface{}的统一Uniq中。 尽管它本身不使用反射,但是它仍然具有两个运行时动态强制转换和会影响性能的开关。 我们可能应该进行基准测试,看看这会带来多大的影响。 我们可能还应该对反射实现进行基准测试,看看我们对运行时性能的怀疑是否确实是基于第一位的。

Nevertheless, this was a fun thought experiment.

但是,这是一个有趣的思想实验。

Tal is a founder at Orbs.com — a public blockchain infrastructure for large scale consumer applications with millions of users. To learn more and read the Orbs white papers click here. [Follow on Telegram, Twitter, Reddit]

Tal是Orbs.com的创始人-Orbs.com是一个公共区块链基础架构,用于拥有数百万用户的大规模消费者应用程序。 要了解更多信息并阅读Orbs白皮书, 请单击此处 [关注电报TwitterReddit ]

Note: if you’re interested in blockchain — come contribute! Orbs is a fully open source project where anyone can participate.

注意:如果您对区块链感兴趣,请贡献力量! Orbs是一个完全开源的项目,任何人都可以参与。

翻译自: https://www.freecodecamp.org/news/the-benefits-of-using-lodash-in-the-go-language-without-reflection-1d64b5115486/

golang go语言

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值