【问题标题】:OCaml pipelining and type constructor argumentsOCaml 流水线和类型构造函数参数
【发布时间】:2021-08-17 22:00:44
【问题描述】:

学习 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 帖子,但我无法将这两种情况联系起来:ocaml type constructor arguments

感谢您的宝贵时间 - 感谢您的帮助。

【问题讨论】:

    标签: ocaml


    【解决方案1】:

    OCaml 构造函数不是函数,这正是该语言的工作原理。从语法上讲,一元构造函数在其后需要一个表达式。所以你不能将构造函数本身用作函数,你需要完全按照你说的将它包装在一个lambda中。

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

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

    【讨论】:

    • 非常感谢。
    【解决方案2】:

    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]
    

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

    错误:此表达式的类型为 (float -> float) w 但是需要一个类型为 (int -> int) w 的表达式 float 类型与 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 以获得更详尽的解释)。

    【讨论】:

    • 非常感谢您的详尽回答。我想我需要一点时间才能理解你在这里介绍的一切;我有兴趣了解为什么构造函数不能像 ocaml 中的函数那样工作。
    • 从某种意义上说,问题在于构造函数比函数工作得更好,因为它们保证是纯的(也就是无效果的)。
    【解决方案3】:

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

    如果我们看一下Ticker

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

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

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

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

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

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

    【讨论】:

      猜你喜欢
      • 2012-09-22
      • 2010-12-31
      • 2013-12-01
      • 2010-12-05
      • 2016-06-01
      • 2017-01-14
      • 2016-01-07
      • 1970-01-01
      • 2020-10-17
      相关资源
      最近更新 更多