记录类型没有类型构造函数?

ane*_*eal 7 ocaml

我正在将以下Haskell代码转换为OCaml:

data NFA q s = NFA
{ intialState :: q
, isAccepting :: q -> Bool
, transition  :: q -> s -> [q]
}
Run Code Online (Sandbox Code Playgroud)

最初我尝试了一个非常直译:

type ('q,'s) nfa = NFA of { initialState: 'q;
                            isAccepting:  'q -> bool;
                            transition:   'q -> 's -> 'q list }
Run Code Online (Sandbox Code Playgroud)

...当然,这会产生语法错误,因为不允许使用类型构造函数部分"NFA of".它一定要是:

 type ('q,'s) nfa = { initialState: 'q;
                      isAccepting:  'q -> bool;
                      transition:   'q -> 's -> 'q list }
Run Code Online (Sandbox Code Playgroud)

这让我想知道为什么会这样.为什么不能像记录类型那样拥有记录类型的类型构造函数(如下所示)?

type ('q, 's) dfa = NFA of ('q * ('q->bool) * ( 'q -> 's -> 'q list) )
Run Code Online (Sandbox Code Playgroud)

gas*_*che 16

为什么你想要一个记录类型的类型构造函数,除非因为这是你在Haskell中的习惯?

在Haskell中,记录并不完全是一流的结构:它们更像是元组之上的语法糖.您可以定义记录字段名称,将它们用作访问者,并进行部分记录更新,但这可以通过普通元组中的位置进行访问.因此,构造函数名称必须在desugaring之后告诉另一个记录:如果你没有构造函数名称,那么两个具有不同字段名但具有相同字段类型的记录将会变成等效类型,这将是一件坏事.

在OCaml中,记录是一种原始概念,它们有自己的特征.因此,它们不需要头构造函数来区分它们与元组或相同字段类型的记录.我不明白你为什么要添加一个头部构造函数,因为这更加冗长而没有提供更多信息或帮助表达性.

为什么不能像记录类型那样拥有记录类型的类型构造函数(如下所示)?

小心 !您显示的示例中没有元组,只有具有多个参数的sum类型.Foo of bar * baz不是同样的事情Foo of (bar * baz):前一个构造函数有两个参数,而后者的构造函数只有一个参数,这是一个元组.这种区分是出于性能原因(在内存中,两个参数与构造函数标记一起打包,而元组创建一个间接指针).使用元组而不是多参数稍微更灵活:您可以匹配为两者,Foo (x, y) -> ...或者Foo p -> ...后者不可用于多参数构造函数.

元组和记录之间没有不对称性,因为它们在sum类型构造中都没有特殊的状态,它只是任意arity构造函数的总和.也就是说,使用元组作为构造函数的参数类型更容易,因为不必声明使用元组类型.例如.有些人要求写作的能力

type foo =
  | Foo of { x : int; y : int }
  | Bar of { z : foo list }
Run Code Online (Sandbox Code Playgroud)

而不是当前

type foo = Foo of foo_t | Bar of bar_t
and foo_t = { x : int; y : int }
and bar_t = { z : foo list }
Run Code Online (Sandbox Code Playgroud)

您的请求是此问题的特定(并且不是非常有趣)案例.然而,即使有这样的简写语法,构造函数和数据之间仍然会有一个间接指针,这使得这种风格对于注重性能的程序没有吸引力 - 可能有用的是具有命名构造函数参数的能力.

PS:我不是说Haskell选择将记录变成元组是一件坏事.通过将一个功能转换为另一个功能,可以减少概念的冗余/重叠.也就是说,我个人认为将元组组合成记录会更自然(使用数字字段名称,如在例如Oz中所做的那样).在编程语言设计中,通常没有"好"和"坏"的选择,只有不同的妥协.