1

Hello everyone, I am fried fish.

On the eve of Children's Day on the eve of Children's Day, I saw a very magical Go2 technical proposal and wanted to add a simpler and lighter anonymous function syntax.

Today, let's take a look at the fried fish with you.

new proposal

The purpose of the new Go proposal is to add lightweight anonymous function syntax. The industry alias is also called "arrow syntax". It was proposed by @Damien Neil. The source of the proposal is " proposal: Go 2: Lightweight anonymous function syntax ", praise and criticism Both have:

We proceed from this.

The following example:

 import (
    "fmt"
    "math"
)

func compute(fn func(float64, float64) float64) float64 {
    return fn(3, 4)
}

func main() {
    hypot := func(x, y float64) float64 {
        return math.Sqrt(x*x + y*y)
    }
    fmt.Println(hypot(5, 12))

    fmt.Println(compute(hypot))
    fmt.Println(compute(math.Pow))
}

The above code mainly implements multiple anonymous closure functions. In fact, there is no business logic. It is believed that the code readability is not high due to the complex signature of the closure.

To avoid this, many languages allow to omit the parameters and return types of anonymous functions, since they may be derived from the context and can be reused directly.

The following Scala example:

 compute((x: Double, y: Double) => x + y)
compute((x, y) => x + y) // Parameter types elided.
compute(_ + _) // Or even shorter.

Rust example:

 compute(|x: f64, y: f64| -> f64 { x + y })
compute(|x, y| { x + y }) // Parameter and return types elided.

Therefore, this Go proposal is to add this lightweight syntax to anonymous closures to make the code look more concise and improve the readability of the code.

Example in PHP:

 $x = 1;
$fn = fn() => $x++; // 不会影响 x 的值
$fn();
var_export($x);  // 输出 1

It's more flavorful.

real case scenario

Cap'n Proto

The Go open source library Cap'n Proto ( capnproto/go-capnproto2 ) is an extremely fast data exchange format, similar to Protocol Buffers, but much faster.

Here is a snippet of its code usage:

 s.Write(ctx, func(p hashes.Hash_write_Params) error {
  err := p.SetData([]byte("Hello, "))
  return err
})

Assuming we are Rust, the effect is as follows:

 s.Write(ctx, |p| {
  err := p.SetData([]byte("Hello, "))
  return err
})

errgroup

I believe everyone is familiar with this errgroup library, which is often used for err processing and synchronization in asynchronous scenarios with multiple goroutines.

Here is a snippet of its use:

 g.Go(func() error {
  // perform work
  return nil
})

Assuming we are Scala, the effect is as follows:

 g.Go(() => {
  // perform work
  return nil
})

Only from the comparison of the number of codes, it is indeed simpler.

discuss

This proposal has caused quite a stir and discussion in the community, with many different viewpoints.

syntax format

Let's start with Go's syntax. The syntax format is:

 [ Identifier ] | "(" IdentifierList ")" "=>" ExpressionList

The example would become:

 s.Write(ctx, p => p.SetData([]byte("Hello, "))

g.Go(=> nil)

even shorter.

reduced readability

Many friends think that this reduces the readability of the code and makes it more difficult to understand. You have to switch a few words in your mind to know what it means...

Think about it, just grab a fried fish at the company. Assuming he doesn't know the syntax in advance, can he read what this code means?

as follows:

 g.Go(=> nil)

Obviously, he can't be 100% sure. But without this syntax, it's just a normal anonymous closure, which can be read. Because grammar is basically general knowledge, and arrow grammar is not.

early designs rejected

In the early design of Go, the "arrow syntax", that is, this proposal, was actually studied.

The syntax at the time was:

 func f (x int) -> float32

Because it doesn't handle multiple (non-tuple) return values well; once func and arguments are present, the arrows are redundant and get complicated.

While doing so will look more "pretty", "pretty" (as it looks mathematically) may still be redundant. It also looks like a grammar belonging to a "different" language.

Officials also believe that you must be very careful not to create special syntax for closures. Because what Go has now is simple and regular syntax and logic.

Eventually gave up on the idea of adding arrow syntax.

replace with ellipsis

From the code example, the main source of complexity is type declarations and structures. Therefore, it has also been proposed to use ellipsis to achieve a similar effect.

The following code:

 s.Write(ctx, func(p _) _ { return p.SetData([]byte("Hello, ")) })

The benefit of this is that no syntax changes are required.

Summarize

The original intention of the author of the original proposal may be to make anonymous closures more concise and reduce code complexity. But in fact, in essence, the saving is only the complexity on the surface.

Once such "arrow" syntax is introduced, it may further increase the overhead of brain conversion. When looking at the code, you have to think about it, which will increase the mental overhead.

Of course, I might be wrong too. What do you think? Whether to support Go's new lightweight anonymous closure syntax, which is commonly known as the "arrow" syntax in the industry.

Welcome to leave a message and exchange in the comment area.

The article is updated continuously, you can read it by searching on WeChat [Brain fried fish]. This article has been included in GitHub github.com/eddycjy/blog . If you are learning Go language, you can see the Go learning map and route . Welcome to Star to urge you to update.

Go Book Series


煎鱼
8.4k 声望12.8k 粉丝