似乎printPerson函数中的p参数不能被推断为Person,但是intelisense显示我正在传递的两个printPerson调用p:Person。请帮我理解我做错了什么?
type Person (name:string) =
member e.Name = name
type EmployeeNode =
| Leader of Person * int * list<EmployeeNode>
| Employee of Person * int
let printPerson level p =
printfn "%s %s" <| String.replicate (level) "#" <| p.Name
let rec print node =
match node with
| Employee(p, level) -> printPerson level p
| Leader(p, level, nodes) ->
printPerson level p
List.iter print nodes
答案 0 :(得分:6)
多个类型可以拥有成员this.Name
,即使他们不在此示例中,因此编译器也不知道您的意思是Person
。比如说你有
type Person (name : string) =
member this.Name = name
type School (name : string, address : string) =
member this.Name = name
member this.Address = address
let printName x = printfn "%s" x.Name // This is a type error.
编译器无法确定您的意思 - Person
或School
。如果你没有定义具有相同名称的成员的另一种类型,那么编译器仍然不会采用该类型并不重要,因为类型扩展之类的东西可以在编译之后将成员添加到类型上。 / p>
Intellisense知道您在调用函数时尝试传递的类型,但这与编译器强制类型安全性不同。通常,任何访问类方法或成员的函数都需要该类的类型注释。
要修复您的示例,您只需要更改为
let printPerson level p =
到
let printPerson level (p : Person) =
答案 1 :(得分:5)
在printPerson
中,有关编译器p
的仅信息是它有Name
成员。由于可能存在除Person
之外的其他类型,因此无法将p
推断为Person
。
在printPerson
的来电中,p
的类型是根据模式确定的,而不是来自通话。
答案 2 :(得分:5)
如果您正在使用F#中的简单不可变数据容器,您会发现编写惯用代码更容易,使用类型推断,使用Records而非标准.NET类。
您可以按如下方式更改Person
的定义:
type Person = {Name : string}
如果使用记录,则不需要使用类型注释,并且可以保持代码不变:
let printPerson level p =
printfn "%s %s" <| String.replicate (level) "#" <| p.Name
我会推荐这种方法,特别是因为记录可以免费提供额外的奖励,例如自动结构平等和比较。
如果您使用标准.NET类,则必须提供类型注释以消除您所引用的特定类型的歧义,该类型公开Name
属性:
type Person (name:string) =
member e.Name = name
let printPerson level (p : Person) =
printfn "%s %s" <| String.replicate (level) "#" <| p.Name