OCaml 流水线和类型构造函数参数

OCaml pipelining and type constructor arguments

学习 OCaml,想知道为什么编译器认为我向单参数类型构造函数提供零参数。

这个 OCaml:

open Core

module Json = Yojson.Safe.Util

type ticker = Ticker of string

let ticker_of_yojson s = 
  s |> Json.to_string |> Ticker |> Result.Ok

产生此错误:

The constructor Ticker expects 1 argument(s)
but is applied here to 0 argument(s).

如果我把它改成这样,用 lambdas 包装构造函数调用,

let ticker_of_yojson s = 
  s |> Json.to_string |> (fun x -> Ticker x) |> (fun x -> Result.Ok x)

它编译。如果我只将 Ticker 构造函数调用包装在 lambda 中,我会得到与 Result.Ok.

相同的零参数错误

编译器在这里缺少什么,用 lambda 表达式替换可以解决?是否还有其他我可以添加的东西,以便没有 lambda 的原始版本可以工作?

我看到了另一个 SO post,这似乎是一个类似的问题,但我无法将两种情况联系起来:ocaml type constructor arguments

感谢您的宝贵时间 - 任何帮助表示感谢。

OCaml 构造函数不是函数,语言就是这样工作的。从语法上讲,一元构造函数后面需要一个表达式。所以你不能把一个构造函数本身当作一个函数来使用,你需要按照你说的那样把它包装在一个lambda中。

二元(或三元等)构造函数需要在其后加括号的逗号分隔表达式列表。所以你也不能在 OCaml 中部分应用构造函数。

其他语言(特别是 Haskell)将构造函数视为任何其他(柯里化)函数,并且效果非常好。但此时更改 OCaml 可能为时已晚。

Ticker 是一个构造函数,通常用于定义创建特定类型值的方法。游览 Variants in OCaml.

如果我们看一下 Ticker

的定义
# type ticker = Ticker of string;;
type ticker = Ticker of string

本质上,我们正在定义一个名为 ticker 的类型,其值可以使用右侧的构造函数 Ticker 创建,它采用一个字符串类型的值。构造函数本身是一个表达式,其类型将是我们刚刚为其定义变体的类型。

但是,如果我们查看 流水线操作符的类型(|>) ...它是一个函数类型。

(|>);;
- : 'a -> ('a -> 'b) -> 'b = <fun>

并且它需要一个函数类型作为它的第二个参数。

然而,在它被破坏的情况下,传递的类型实际上是一个类型代码,它不是一个函数类型,因此是一个类型不匹配......并且编译器大喊加载,我们可以在产生的错误中看到。

OCaml 区分了函数应用程序和构造函数应用程序。

变体构造函数应用程序的语法是C (x_1, ..., x_n)。 因此表达式

s |> Ticker

被解析为

(|>) (s) (Ticker)

其中 Ticker 缺少参数。

有人可能想知道为什么 OCaml 不像 Haskell 那样统一函数和构造函数应用程序。对这种差异的一种部分解释是,这是允许突变的结果。

事实上,在存在突变的情况下(因此具有(宽松的)值限制),变体构造函数应用程序与 OCaml 中的函数应用程序不具有完全相同的语义。

例如,如果我将包装器类型定义为

type 'a w = W of 'a
let int_minus = (~-)
let float_minus = (~-.)

下面的例子:

let ok =
  let x = W Fun.id in
  [x; W int_minus], [x; W float_minus]

打字很好。但是,如果我替换构造函数应用程序 通过构造函数

let w x = W x

在这个例子中

let error =
  let x = w Fun.id in
  [x; w int_minus], [x; w float_minus]

由于值限制,这会触发错误:

Error: This expression has type (float -> float) w but an expression was expected of type (int -> int) w Type float is not compatible with type int

主要区别在于泛型函数可能使用内部可变引用。因此

的类型
let x = w Fun.id

('_weak1 -> '_weak1) w,其中 _weak1 是一个弱多态类型变量(又名具体类型的占位符)。这个占位符类型在我写[x; w int_minus]的时候和int是统一的。然后,稍后尝试构建 [x; w float_minus] 会产生错误,因为我正在尝试将 x 与类型 (float->float) w.

一起使用

相反,变体构造函数应用程序不能进行任何计算,因此不受此限制和类型

let x = W Fun.id

是预期的完全多态类型 ('a -> 'a) w。因此,我可以在

中为 'a 重用两种不同类型的 x
[x; W int_minus], [x; W float_minus]

(请参阅 https://ocaml.org/manual/polymorphism.html#s:weak-polymorphism 以获得更详尽的解释)。