F# - 构造嵌套类型

Boj*_*vic 2 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
Run Code Online (Sandbox Code Playgroud)

哪里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
Run Code Online (Sandbox Code Playgroud)

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

Fyo*_*kin 5

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

type T = int | string
Run Code Online (Sandbox Code Playgroud)

这种类型只是其他两种类型的混合,通常被称为“非歧视联合”。公平地说,有些语言支持非歧视联合(例如 TypeScript),但 F# 不是其中之一。F# 是 ML 语言家族的一部分,它支持可区分联合。那里的“discriminated”部分意味着联合的部分需要被赋予一个区别属性,一个“discriminator”,通常被称为“constructor”。例如:

type T = X of int | Y of string
Run Code Online (Sandbox Code Playgroud)

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

let t = X 42
Run Code Online (Sandbox Code Playgroud)

并且在模式匹配期间使用它们来判断预期的变体:

let f t = match t with
    | X n -> printfn "it's a number: %d" n
    | Y s -> printfn "it's a string: %s" s
Run Code Online (Sandbox Code Playgroud)

您实际上在您自己的代码中拥有其中之一- Node,-所以我有点困惑为什么您会犯这个错误Id,而不是Node.

如果你想Id按照你在i1and的定义中所做的方式构造值i2- 即Id1(1), - 那么你需要Id与命名的构造函数之一建立一个可区分的联合,Id1并采用一个int作为参数。从您的其余代码中,我可以猜测另一个构造函数应该是Id2并采用string

type Id = Id1 of int | Id2 of string
Run Code Online (Sandbox Code Playgroud)

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

其原因是,intstring有错误的市值(工会个案标识必须是大写),但Id1Id2在这方面的罚款。当您将类型定义为 时type Id = Id1 | Id2,这是完全合法的,但那些Id1Id2与之前定义的类型Id1Id2. 它们只是碰巧具有相同的名称,并且在 F# 中允许与以前的定义具有相同的名称。它被称为“阴影”。

您的类型Id最终有两个构造函数,Id1并且Id2都没有参数。这就是为什么编译器抱怨,当你试图通过1作为参数传递给Id1你写Id1(1):“ ID1为不是一个功能,不能适用