为啥类型推断算法会因为“Fun.flip Option.bind”而混淆?

Posted

技术标签:

【中文标题】为啥类型推断算法会因为“Fun.flip Option.bind”而混淆?【英文标题】:Why type inference algorithm confuses because of 'Fun.flip Option.bind'?为什么类型推断算法会因为“Fun.flip Option.bind”而混淆? 【发布时间】:2020-08-23 10:58:52 【问题描述】:

模块中函数声明的常见签名是最后一个参数的类型是主状态 (Module.t)。就像在“列表”模块中一样。此表单打开了使用“|>”运算符的能力,例如:

[1;2;3] |> List.filter ((>)2)
        |> List.map ((-)1)
        |> List.fold_left 0 (+)

但“选项”模块中的“绑定”函数不遵循这种形式。它有'Option.t'参数作为第一个

val bind : 'a option -> ('a -> 'b option) -> 'b option

但是好的,我可以改变它。我用相反的参数顺序声明了函数'opt_bind'。

let opt_bind = Fun.flip Option.bind

但是这个不行。并且下面的代码编译时出现如下错误

type a = A of int
type b = B of int 

let f x = Some (A x)
let g (A x) = Some (B x)  
let opt_bind = Fun.flip Option.bind 

let result = 
  (Some 42) |> opt_bind f
            |> opt_bind g
         |> opt_bind g
                     ^                     

错误:此表达式具有类型 a -> b 选项,但表达式应为 > type int -> a 选项。 a 类型与 int 类型不兼容

同样的情况

let result = 
  let x = opt_bind f (Some 42) in
  let x = opt_bind g x in
  x 

即使在我注意到所有类型之后,我仍然遇到同样的问题。

let f : int -> a option = fun x -> Some (A x)
let g : a -> b option = fun (A x) -> Some (B x)  
let opt_bind : ('a -> 'b option) -> 'a option -> 'b option = 
  Fun.flip Option.bind 

let result : b option = 
  let x : a option = opt_bind f (Some 42) in
  let x : b option = opt_bind g x in
  x ;;

但是

let result = 
  let x = Option.bind (Some 42) f in
  let x = Option.bind x g in
  x 

工作正常。

为什么“opt_bind”对“g”有错误的类型期望,好像“opt_bind”不是通用的? 如何使用带有 '|>' 符号的 'bind'?

【问题讨论】:

类型注解不能绕过值限制。在这种情况下,它们并不代表您的想法——opt_bind 仍然不是多态的;相反,'a'b 与弱类型变量统一。 (注释中类型变量的这种误导性语义可以说是 OCaml 的错误特征。)您需要使用至少一个参数“扩展”定义而不是注释,如 Jeffrey 的回答所示。这避免了价值限制。 另请参阅,ocamlverse.github.io/content/weak_type_variables.html 【参考方案1】:

您的问题是您对opt_bind 的定义不够多态。因为你将它定义为一个应用程序(Fun.flip 到 Option.bind),由于值的限制,它不能被做成多态的。

如果你这样定义:

let opt_bind a b = Fun.flip Option.bind a b

或者,等效地,像这样:

let opt_bind a b = Option.bind b a

然后一切都会好起来的。

如果您询问opt_bind 定义的类型,您会看到问题:

# let opt_bind = Fun.flip Option.bind;;
val opt_bind :
  ('_weak3 -> '_weak4 option) -> '_weak3 option ->
  '_weak4 option = <fun>

“弱”类型变量告诉您​​生成的函数不是多态的。

本质区别在于Fun.flip Option.bind 在语法上是一个应用程序(一个函数调用)。这样的表达式不能多态。两种替代形式将bind_opt定义为一个lambda(一个函数值),在值限制的术语中是一个语法“值”。

需要值限制以确保多态函数是健全的(即它们不允许对值进行不适当的操作)。

我选择的值限制参考(尤其是在 OCaml 中实现的)是这篇论文:Relaxing the Value Restriction, Jacques Garrigue

【讨论】:

以上是关于为啥类型推断算法会因为“Fun.flip Option.bind”而混淆?的主要内容,如果未能解决你的问题,请参考以下文章

为啥 TypeScript 在使用 concat 减少数组时会推断出“从不”类型?

为啥 TypeScript 中的方法链接会导致泛型类型推断失败?

为啥 Rust 不能推断出 Iterator::sum 的结果类型?

Hindley Milner类型推断相互递归函数

为啥 Go 编译器不能推断结构实现了接口 [关闭]

为啥不能快速推断闭包类型