【问题标题】:Type inference fails horribly when omitting argument label on a function call在函数调用中省略参数标签时,类型推断严重失败
【发布时间】:2017-09-24 20:23:06
【问题描述】:

给定以下函数

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)

它变得更奇怪了,然而,编译器在这里给出的错误信息是:

此表达式的类型为 int,但表达式的类型应为 ('a -> 'b) 选项

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

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

这个谜题的功劳归于@nachotoday,Reason Discord

【问题讨论】:

  • 可选标签参数实际上有一个option类型
  • @Bergi 虽然这不是可选的

标签: label parameter-passing ocaml type-inference


【解决方案1】:

这是标记参数、柯里化和第一类函数之间破坏性干扰的情况。函数get_or 用于类型

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

带标签参数的规则是,当应用程序是全部时,标签可以省略。乍一看,这意味着如果一个人将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 具有 default:'a -> 'a option -> 'a 类型。 尚未提供默认标签, 所以结果将具有类型default:'r -> 'r
  • 查看r,在get_or 2 (Some 4)get_or 有类型 'a option -> 'a,因此get_or x:'a
  • 然后get_or x:'a应用于y;因此'a = 'b -> 'c
  • 换句话说,我应该有x: ('b -> ' c) option 但我知道x:int

这导致类型检查器报告的矛盾,2 应该是一个函数选项 ('a -> 'b) option,但显然是一个 int。

【讨论】:

  • 谢谢,很好的回答!我不太了解推理过程的细节,您所说的“for type”是什么意思以及get_or x 真正指的是什么,但我想我至少了解为什么会发生这种情况:)
  • 重新表述类型推断:由于我们没有完整的应用程序(由于多态性),对“标记”部分的搜索被推迟,我们可以假设 get_or 的类型是 @ 987654348@。因为我们将结果应用于我们拥有'a = 'b->'c 的值,并且编译器检查参数2 : int 的类型为('b->'c) option 失败。
猜你喜欢
  • 2018-12-13
  • 1970-01-01
  • 1970-01-01
  • 2021-01-03
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-03-12
  • 1970-01-01
相关资源
最近更新 更多