在函数调用中省略参数标签时类型推断失败可怕

鉴于以下功能

let get_or ~default =
  function | Some a -> a
           | None -> default

如果使用标记的参数调用此函数,它将按预期工作:

let works = get_or ~default:4 (Some 2)

但如果省略标签,它就会失败:

let fails = get_or 4 (Some 2)

它变得更奇怪,但是,编译器在此处给出的错误消息是:

This expression has type int but an expression was expected of type (‘a -> ‘b) option

编译器不仅错误地推断它是一个选项,而且由于某种原因,它还从众所周知的魔术师的帽子中提取了一个函数类型!所以我很自然地想知道:它究竟来自哪里?并且对我的好奇心不那么重要,为什么不在这个特定情况下省略标签工作呢?

有关交互式示例,请参见this reason playground.

这个谜题的信用在Reason Discord上传到@nachotoday.

这是标记的参数,currying和一级函数之间的破坏性干扰的情况.函数get_or有类型

val get_or: default:'a -> 'a option -> 'a

带有标签参数的规则是当应用程序为total时可以省略标签.乍一看,这意味着如果将get_or应用于两个参数,那么它就是一个完整的应用程序.但get_or的返回类型是多态的(即’a)这一事实会带来麻烦.考虑例如:

let id x = x
let x : _ -> _ = get_or (Some id) id ~default:id

这是有效的代码,其中get_or应用于三个参数,并且默认参数在第三个位置提供!

更进一步,令人惊讶的是,它仍然有效:

let y : default:_ -> _ -> - = get_or (Some id) id id

它为y产生了一个非常复杂的类型.

这里的通用规则是,如果函数的返回类型是多态的,那么类型检查器永远不会知道函数应用程序是否是完全的;因此永远不会遗漏标签.

回到你的例子,这意味着类型检查器读取

get_or (4) (Some 2)

>首先,get_or的类型为默认值:’a – > ‘一个选项 – > ‘一个.
尚未提供默认标签,
所以结果将是默认类型:’r – > “R
>在get_or 2(约4)中查看r,get_or有类型
‘一个选项 – > ‘a,因此get_or x:’a
>然后get_or x:’a应用于y;因此’a =’b – > ‘C
>换句话说,我应该有x:(‘b – >’c)选项
但我知道x:int.

这导致类型检查器报告的矛盾,2预计是一个函数选项(‘a – >’b)选项,但显然是一个int.

相关文章
相关标签/搜索