Go 要加個(gè)箭頭語法,這下更像 PHP 了!
大家好,我是煎魚。
在六一兒童節(jié)前夕在摸煎魚時(shí),看到一個(gè)很神奇的 Go2 的技術(shù)提案,想要加一個(gè)更簡單、更輕量的匿名函數(shù)語法。
今天就由煎魚和大家一起看看。
新提案
新的 Go 提案目的是添加輕量級的匿名函數(shù)語法,業(yè)內(nèi)別名又叫 “箭頭語法”,是由 @Damien Neil 所提出的,提案的來源是《proposal: Go 2: Lightweight anonymous function syntax[1]》,褒貶都有:
我們由此進(jìn)行展開。
如下例子:
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))
}
上述代碼主要是實(shí)現(xiàn)了多個(gè)匿名的閉包函數(shù),實(shí)際上業(yè)務(wù)邏輯沒有什么。認(rèn)為由于閉包簽名繁雜,導(dǎo)致代碼可讀性不高。
為了避免這種情況,許多語言允許省略匿名函數(shù)的參數(shù)和返回類型,因?yàn)樗鼈兛赡苁菑纳舷挛呐缮?,能夠直接被?fù)用。
如下 Scala 的例子:
compute((x: Double, y: Double) => x + y)
compute((x, y) => x + y) // Parameter types elided.
compute(_ + _) // Or even shorter.
Rust 的例子:
compute(|x: f64, y: f64| -> f64 { x + y })
compute(|x, y| { x + y }) // Parameter and return types elided.
因此這個(gè) Go 提案就是希望針對匿名閉包增加這個(gè)輕量級的語法,讓代碼看起來更加的簡潔,讓代碼可讀性提高。
PHP 的例子:
$x = 1;
$fn = fn() => $x++; // 不會(huì)影響 x 的值
$fn();
var_export($x); // 輸出 1
更有那味了。
真實(shí)案例
Cap'n Proto
Go 開源庫 Cap'n Proto(capnproto/go-capnproto2[2])是一種極其快速的數(shù)據(jù)交換格式,類似于Protocol Buffers,但速度快得多。
以下是其代碼使用片段:
s.Write(ctx, func(p hashes.Hash_write_Params) error {
err := p.SetData([]byte("Hello, "))
return err
})
假設(shè)我們是 Rust,效果如下::
s.Write(ctx, |p| {
err := p.SetData([]byte("Hello, "))
return err
})
errgroup
這個(gè) errgroup 庫相信大家不會(huì)陌生,常用于多個(gè) goroutine 的異步場景中的 err 處理和同步。
以下是其使用片段:
g.Go(func() error {
// perform work
return nil
})
假設(shè)我們是 Scala,效果如下:
g.Go(() => {
// perform work
return nil
})
只從代碼數(shù)量來對比看,確
只從代碼數(shù)量來對比看,確實(shí)是簡潔一些。
討論
這個(gè)提案引起了社區(qū)不小的轟動(dòng)和討論,有多種不同的觀點(diǎn)。
語法格式
先從 Go 的語法角度來看。語法格式為:
[ Identifier ] | "(" IdentifierList ")" "=>" ExpressionList
例子會(huì)變成:
s.Write(ctx, p => p.SetData([]byte("Hello, "))
g.Go(=> nil)
更更更短了。
降低了可讀性
許多小伙伴認(rèn)為這反而降低了代碼可讀性,更難懂了,還得在腦子里轉(zhuǎn)換幾道,才能知道是什么意思...
你想想,隨便在公司上抓一只煎魚。假設(shè)他沒有提前了解過這個(gè)語法,他能讀得懂這段代碼是什么意思嗎?
如下:
g.Go(=> nil)
顯然,他沒法 100% 確定。但沒有這語法時(shí),只是正常的匿名閉包,是可以讀懂的。因?yàn)檎Z法基本是通識,而箭頭語法并不是。
早期設(shè)計(jì)被拒絕
在 Go 早期的設(shè)計(jì),其實(shí)對 “箭頭語法”,也就是本提案進(jìn)行過研究。
當(dāng)時(shí)的語法是:
func f (x int) -> float32
因?yàn)樗荒芎芎玫靥幚矶鄠€(gè)(非元組)返回值;一旦出現(xiàn) func 和參數(shù),箭頭就多余了,會(huì)變得很復(fù)雜。
雖然這么做會(huì)看起來更 “漂亮”,但 “漂亮”(就像在數(shù)學(xué)上看起來一樣)可能仍然是多余的。它看起來也像是屬于一種“不同”語言的語法。
官方也認(rèn)為必須非常小心,不要為閉包創(chuàng)建特殊語法。因?yàn)楝F(xiàn)在 Go 所擁有的是簡單而規(guī)律的語法和邏輯。
最終放棄了添加箭頭語法的想法。
用省略符替代
從代碼示例來看,引起繁雜的主要是類型聲明和結(jié)構(gòu)。因此也有人提出使用省略符來實(shí)現(xiàn)類似效果。
如下代碼:
s.Write(ctx, func(p _) _ { return p.SetData([]byte("Hello, ")) })
這樣的好處是不需要語法改變。
總結(jié)
原提案作者的本意,可能是需要讓匿名閉包更加的簡潔,降低代碼復(fù)雜度。但其實(shí)這本質(zhì)上,節(jié)約的只是明面上的復(fù)雜度。
一旦引入這類 “箭頭” 語法,可能會(huì)更大的加劇腦子轉(zhuǎn)換的開銷。看代碼時(shí),得想想對對,會(huì)加重底下的腦力開銷。
當(dāng)然,說不定我也是錯(cuò)的。你覺得呢?是否支持 Go 新增輕量級的匿名閉包語法,也就是業(yè)內(nèi)俗稱的 “箭頭” 語法。
參考資料
[1]proposal: Go 2: Lightweight anonymous function syntax: https://github.com/golang/go/issues/21498
[2]capnproto/go-capnproto2: https://github.com/capnproto/go-capnproto2?