我想这是一个非常基本的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
其中Node
和Child
应该代表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
注释中给出了编译器错误。
答案 0 :(得分:4)
您对Id
的定义具有以下相同的语义:
type T = int | string
这种类型只是其他两种类型的组合,通常被称为“非歧视联盟”。公平地说,有些语言支持无差别的联合(例如TypeScript),但是F#并不是其中一种。 F#是ML语言家族的一部分,支持 discriminated 联合。那里的“区分的”部分意味着需要为工会的各个部分赋予一个区分的属性,即“区分子”,这通常被称为“构造函数”。例如:
type T = X of int | Y of string
在这里,X
和Y
是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
犯了这个错误。
如果您要按照定义Id
和i1
的方式构造i2
的值,即Id1(1)
,则需要制作{{ 1}}有一个名为Id
的构造函数,并以Id1
作为参数的有区别的联合。从您的其余代码中,我可以猜测:另一个构造函数应为int
并采用Id2
:
string
现在,值得注意的是,尽管上面的示例类型type Id = Id1 of int | Id2 of string
无法编译,但您的类型T
可以正常编译。为什么会这样?
原因是Id
和int
的大小写错误(联合用例标识符必须为大写),但是在这方面,string
和Id1
很好。当您将类型定义为Id2
时,这是完全合法的,但是那些type Id = Id1 | Id2
和Id1
与先前定义的类型Id2
和Id1
没有关系。它们恰好具有相同的名称,并且在F#中允许使用与以前的定义相同的名称。这叫做“阴影”。
您的类型Id2
最终有两个构造函数Id
和Id1
,这两个构造函数都没有参数。这就是为什么当您尝试在编写Id2
时将1
作为参数传递给Id1
时编译器抱怨的原因:“ Id1不是函数,无法应用“