F#-构造嵌套类型

时间:2019-09-23 21:56:31

标签: f# composite

我想这是一个非常基本的F#问题:

类型为:

type Id1 =
    | Id1 of int

type Id2 =
    | Id2 of string

type Id =
    | Id1
    | Id2

type Child = {
    Id : Id;
    Smth : string list
    }

type Node =
    | Child of Child
    | Compos of Node * Node

其中NodeChild应该代表Composite OOP设计模式的替代物。

问题是我无法以这种方式实例化类型:

let i1 : Child = {Id = Id1(1); Smth = []}   //Id1 value is not a function and cannot be applied
let i2 : Child = {Id = Id1(1); Smth = []}   //same
let i3 : Node = Compos(i1, i2)  //Expression expected Node but has Child

注释中给出了编译器错误。

1 个答案:

答案 0 :(得分:4)

您对Id的定义具有以下相同的语义:

type T = int | string

这种类型只是其他两种类型的组合,通常被称为“非歧视联盟”。公平地说,有些语言支持无差别的联合(例如TypeScript),但是F#并不是其中一种。 F#是ML语言家族的一部分,支持 discriminated 联合。那里的“区分的”部分意味着需要为工会的各个部分赋予一个区分的属性,即“区分子”,这通常被称为“构造函数”。例如:

type T = X of int | Y of string

在这里,XYT类型的构造函数。在构造值以指示要构造哪个变体时使用它们:

let t = X 42

它们在模式匹配期间用于告诉您期望哪个变体:

let f t = match t with
    | X n -> printfn "it's a number: %d" n
    | Y s -> printfn "it's a string: %s" s

您实际上在自己的代码中有一个-Node,所以让您感到困惑的是为什么您使用Id而不是Node犯了这个错误。

如果您要按照定义Idi1的方式构造i2的值,即Id1(1),则需要制作{{ 1}}有一个名为Id的构造函数,并以Id1作为参数的有区别的联合。从您的其余代码中,我可以猜测:另一个构造函数应为int并采用Id2

string

现在,值得注意的是,尽管上面的示例类型type Id = Id1 of int | Id2 of string 无法编译,但您的类型T可以正常编译。为什么会这样?

原因是Idint的大小写错误(联合用例标识符必须为大写),但是在这方面,stringId1很好。当您将类型定义为Id2时,这是完全合法的,但是那些type Id = Id1 | Id2Id1与先前定义的类型Id2Id1没有关系。它们恰好具有相同的名称,并且在F#中允许使用与以前的定义相同的名称。这叫做“阴影”。

您的类型Id2最终有两个构造函数IdId1,这两个构造函数都没有参数。这就是为什么当您尝试在编写Id2时将1作为参数传递给Id1时编译器抱怨的原因:“ Id1不是函数,无法应用