我想这是非常基本的 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)
哪里Node和Child应该代表复合 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)
注释中给出了编译器错误。
您的定义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)
在这里,X和Y是类型的构造函数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编译得很好。这是为什么?
其原因是,int与string有错误的市值(工会个案标识必须是大写),但Id1和Id2在这方面的罚款。当您将类型定义为 时type Id = Id1 | Id2,这是完全合法的,但那些Id1和Id2与之前定义的类型Id1和Id2. 它们只是碰巧具有相同的名称,并且在 F# 中允许与以前的定义具有相同的名称。它被称为“阴影”。
您的类型Id最终有两个构造函数,Id1并且Id2都没有参数。这就是为什么编译器抱怨,当你试图通过1作为参数传递给Id1你写Id1(1):“ ID1为不是一个功能,不能适用”