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
其中 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
编译器错误在注释中给出。
您对 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
这里,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
犯了这个错误。
如果你想像在 i1
和 i2
的定义中那样构造 Id
的值 - 即 Id1(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
和 Id2
没有任何关系。它们恰好具有相同的名称,并且在 F# 中允许与以前的定义具有相同的名称。它被称为"shadowing"。
您的类型 Id
最终有两个构造函数,Id1
和 Id2
,它们都没有参数。这就是为什么当您尝试将 1
作为参数传递给 Id1
时编译器会抱怨的原因,因为您编写 Id1(1)
:“Id1 不是函数,无法应用"
我想这是一个非常基本的 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
应表示复合 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
这里,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
犯了这个错误。
如果你想像在 i1
和 i2
的定义中那样构造 Id
的值 - 即 Id1(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
和 Id2
没有任何关系。它们恰好具有相同的名称,并且在 F# 中允许与以前的定义具有相同的名称。它被称为"shadowing"。
您的类型 Id
最终有两个构造函数,Id1
和 Id2
,它们都没有参数。这就是为什么当您尝试将 1
作为参数传递给 Id1
时编译器会抱怨的原因,因为您编写 Id1(1)
:“Id1 不是函数,无法应用"