F# - 构造嵌套类型

F# - constructing nested types

我想这是一个非常基本的 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 应表示复合 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

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

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

type T = int | string

这种类型只是其他两种类型的混合,通常称为 "non-discriminated union"。公平地说,有些语言支持非歧视联合(例如 TypeScript),但 F# 不是其中之一。 F# 是 ML 语言家族的一部分,它支持 discriminated 联合。那里的"discriminated"部分是指union的部分需要给一个区分的属性,一个"discriminator",更常见的是"constructor"。例如:

type T = X of int | Y of string

这里,XY 构造函数 T 类型。在构造值时使用它们来判断正在构造哪个变体:

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 犯了这个错误。

如果你想像在 i1i2 的定义中那样构造 Id 的值 - 即 Id1(1) - 那么你需要Id 一个有区别的联合,其中一个构造函数名为 Id1 并以 int 作为参数。从您的其余代码中,我可以 猜测 另一个构造函数应该是 Id2 并取 string:

type Id = Id1 of int | Id2 of string

现在,需要注意的一件有趣的事情是,虽然我上面的示例类型 T 无法编译,但您的类型 Id 编译得很好。这是为什么?

原因是intstring大小写错误(联合大小写标识符必须大写),但Id1Id2在这方面没问题.当您将类型定义为 type Id = Id1 | Id2 时,这是完全合法的,但是那些 Id1Id2 与先前定义的类型 Id1Id2 没有任何关系。它们恰好具有相同的名称,并且在 F# 中允许与以前的定义具有相同的名称。它被称为"shadowing"。

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