函数类型`type1->type2->type3`与`type1->(type2->type3)`有何区别?
问题拆解
- 函数类型表达式
type1 -> type2 -> type3和type1 -> (type2 -> type3)的本质区别是什么? - 为什么以下两个函数的类型推导结果一致,都是
a:int -> b:int -> int:let f1 a b = a + blet f2 a = fun b -> a + b - 但等价逻辑的以下两个函数,类型推导结果却是
a:int -> (int -> int):let f3 a = let f = fun b -> a + b flet f4 a = let f b = a + b f - 类型表达式里包裹
int -> int的外层括号有什么实际含义? - 两种类型书写形式在实际程序运行中,会不会出现行为差异?
核心原理解答
所有支持柯里化的静态函数式语言(OCaml、F#、Haskell等),对函数类型构造符
->统一采用右结合的语法规则。
- 从语法定义层面,
type1 -> type2 -> type3会被编译器自动等价解析为type1 -> (type2 -> type3),二者不存在任何本质区别。 - 编译器输出类型时给部分场景加的括号,本质是可读性优化,不具备语义强制力:
- 当函数的返回值本身是函数类型时,编译器会主动给返回值的函数类型加上括号,降低读者的拆解成本——不用所有人都时刻记住右结合规则,看到括号就能立刻定位“这部分是返回值的类型”。
- 之前例子里的
a:int -> int -> int和a:int -> (int -> int)是完全等价的类型,没有任何差别:只是前者是编译器对顶层直接定义函数的类型输出风格,后者是编译器对“返回局部绑定函数”的输出风格,核心类型完全一致。
- 四个示例函数的实际运行行为、类型签名没有任何区别:
f1是双参数定义的语法糖,本质和f2完全一致:接收参数a,返回一个捕获了a、接收参数b计算a+b的闭包。f3和f4只是把原本直接返回的闭包,先绑定到局部变量f再返回;类型推导走到这一步时,因为识别到f的类型是int -> int,就主动给这个返回值加上了括号做视觉分隔,没有改变类型本身。
什么场景下括号会产生真正的差异
只有当括号改变了->的默认结合顺序,把参数部分的表达式包裹为函数类型时,才会出现和之前讨论的类型完全不同的结果,比如对比三个本质不同的类型:
int -> int -> int:和右结合解析的int -> (int -> int)完全等价,接收1个int参数,返回一个接收int、返回int的函数。(int -> int) -> int:和上面的类型完全不同,它要求输入一个“int转int”的函数,最终返回一个int值,无法和之前的加法闭包函数互相传参,会触发类型错误。int -> (int -> int):和第一种类型完全等价,括号只是标注了返回值的范围,没有改变结合顺序。
内容的提问来源于stack exchange,提问作者Valery Belov
相关产品推荐
相关产品推荐

