Are function declarations and function expressions implemented differently in Go? If yes, why?

huangapple go评论73阅读模式
英文:

Are function declarations and function expressions implemented differently in Go? If yes, why?

问题

我刚刚开始使用Go编程(对低级语言没有任何经验),我注意到函数表达式与函数声明不被视为相同(go1.18.5 linux/amd64)。

例如,这段代码可以正常工作:

package main

import "fmt"

func main() {
  fmt.Println("Do stuff")
}

但是这段代码会输出错误:

package main

import "fmt"

var main = func() {
  fmt.Println("Do stuff")
}
./prog.go:3:8: imported and not used: "fmt"
./prog.go:5:5: cannot declare main - must be func

Go build failed.

即使像var main func() = func() {}这样指定类型也对最终结果没有任何影响。Go似乎首先评估main标识符是否在任何声明中使用,而忽略类型。与此同时,JavaScript开发人员似乎选择更可读的方式,好像它们之间没有根本的区别。

问题:

  • 函数声明和函数表达式在底层实现上是否有所不同,还是这种行为是硬编码的?
  • 如果是,这些实现之间的区别是否关键?
  • Go在某种程度上是否以某种方式更好地完成了它的工作?
英文:

I just got into programming again with Go (with no experience whatsoever in low-level languages), and I noticed that function expressions are not treated the same as function declarations (go1.18.5 linux/amd64).

For instance, this works (obviously):

package main

import "fmt"

func main() {
  fmt.Println("Do stuff")
}

But this outputs an error:

package main

import "fmt"

var main = func() {
  fmt.Println("Do stuff")
}
./prog.go:3:8: imported and not used: "fmt"
./prog.go:5:5: cannot declare main - must be func

Go build failed.

Even specifying the type as in var main func() = func() {} does nothing for the end result. Go seems to, first of all, evaluate if the main identifier is being used in any declaration, ignoring the type. Meanwhile, Javascript folks seem to choose what is more readable, like there's no underlying difference between them.

Questions:

  • Are function declarations and function expressions implemented differently under the hood, or is this behavior just hard-coded?
  • If yes, is the difference between these implementations critical?
  • Is Go somewhat better in any way for doing it the way it does?

答案1

得分: 3

规范中可以看到:

主要包必须[...]声明一个不带参数且不返回值的函数main

当你写var main时,你就违反了上述要求,因为你创建的是一个存储函数字面量的引用的变量,而不是一个函数声明

函数声明将一个标识符(函数名)绑定到一个函数。

函数字面量表示一个匿名函数。

所以:

函数声明和函数表达式在底层实现上有所不同吗?

是的。

Go在任何方面是否以某种方式更好?

一般来说,不是。这与它作为一种类型化语言有关,这有各种优缺点,取决于使用情况。

如果是的,这些实现之间的差异是否关键?

按照什么标准?例如,引用函数字面量的变量可能为nil,这通常不是你想调用的内容。而对于函数声明来说,这种情况是不会发生的(假设未使用unsafe包)。

英文:

From the spec:

> The main package must [...] declare a function main that takes no arguments and returns no value.

The moment you write var main you prevent the above requirement from being met, because what you are creating is a variable storing a reference to a function literal as opposed to being a function declaration.

> A function declaration binds an identifier, the function name, to a function.

> A function literal represents an anonymous function.

So:

> Are function declarations and function expressions implemented differently under the hood?

Yes.

> Is Go somewhat better in any way for doing it the way it does?

Generally, no. It comes with the territory of being a typed language, which has all sorts of pros and cons depending on use.

> If yes, is the difference between these implementations critical?

By what standard? As an example, a variable referencing a function literal could be nil, which isn't usually something you'd like to call. The same cannot happen with function declarations (assuming package unsafe is not used).

答案2

得分: 2

代码

func main() {
  fmt.Println("Do stuff")
}

将一个函数绑定到标识符 main

代码

var main = func() {
  fmt.Println("Do stuff")
}

将一个类型为 func() 的变量绑定到标识符 main,并将该变量初始化为函数表达式的结果。

函数声明和函数表达式在底层实现上是否有所不同,还是这种行为是硬编码的?

函数表达式求值为函数值。函数声明将函数值绑定到名称。在这些函数值的实现上没有区别(但表达式的结果还可以闭包函数作用域的变量)。

如果是的话,这些实现之间的区别是否关键?

是的。

  • 这个问题指出了一个场景,其中区别很重要(程序执行从包 main 中的函数 main 开始)。
  • 编译器无法内联通过变量调用的函数。
  • 函数不能在另一个函数内声明,但函数表达式可以在函数中分配给变量。

Go 在以这种方式进行操作方面是否有所改进?

其他语言区分绑定函数的标识符和绑定带有函数类型的变量的标识符。在标识符绑定方面,Go 与这些语言相比既不更好也不更差。

OP 在评论中说:

问题是关于 JavaScript 和 Go 之间的区别,但你也回答说“这是一种类型化语言的特性”。

这个区别与是否是类型化语言无关。在 Go 中,标识符可以绑定到常量、类型、变量和函数。我可能有点冒险,但在 JavaScript 中,非保留标识符总是绑定到变量。

英文:

The code

func main() {
  fmt.Println("Do stuff")
}

binds a function to the identifier main.

The code

var main = func() {
  fmt.Println("Do stuff")
}

binds a variable of type func () to the identifier main and initializes the variable to the result of a function expression.

> Are function declarations and function expressions implemented differently under the hood, or is this behavior just hard-coded?

A function expression evaluates to a function value. A function declaration binds a function value to a name. There is no difference in the implementation of these function values (but the result of an expressions can additionally close over function scoped variables).

> If yes, is the difference between these implementations critical?

Yes.

  • The question points out one scenario where the difference is important (program execution starts at the function main in package main)
  • The compiler cannot inline a function called through a variable.
  • Functions cannot be declared inside another function, but a function expression can be assigned to a variable in a function.

> Is Go somewhat better in any way for doing it the way it does?

Other languages make a distinction between an identifier bound a function and and identifier bound to a variable with a function type. Go is no better or worse in than those languages with regards to the binding of identifiers.

The OP says in a comment:

> The question was about the difference between javascript and go, though, but you also answered it with "It comes with the territory of being a typed language".

The difference is unrelated to being a typed language. Identifiers can be bound to constants, types, variables and functions in Go. I may be going out on a limb here, but non-reserved identifiers in Javascript are always bound to variables.

huangapple
  • 本文由 发表于 2022年9月3日 03:17:05
  • 转载请务必保留本文链接:https://go.coder-hub.com/73587093.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定