There is a few cases when F# records behavior is strange to me:
No warning on ambiguity
type AnotherPerson = {Id: int; Name: string}
type Person = {Id: int; Name: string;}
// F# compiler will use second type without any complains or warnings
let p = {Id = 42; Name = "Foo";}
Run Code Online (Sandbox Code Playgroud)
Warning on records deconstruction instead of records construction
Instead of getting a warning on records construction in previous case, F# compiler issued a warning on records "deconstruction":
// Using Person and AnotherPerson types and "p" from the previous example!
// We'll get a warning here: "The field labels and expected type of this
// record expression or pattern do not uniquely determine a corresponding record type"
let {Id = id; Name = name} = p
Run Code Online (Sandbox Code Playgroud)
Note, that there is no warnings with pattern matching (I suspect thats because patterns are built using "records construction expressions" and not with "records deconstruction expression"):
match p with
| {Id = _; Name = "Foo"} -> printfn "case 1"
| {Id = 42; Name = _} -> printfn "case 2"
| _ -> printfn "case 3"
Run Code Online (Sandbox Code Playgroud)
Type inference error with missing field
F# compiler will choose second type and than will issue an error because Age field is missing!
type AnotherPerson = {Id: int; Name: string}
type Person = {Id: int; Name: string; Age: int}
// Error: "No assignment given for field 'Age' of type 'Person'"
let p = {Id = 42; Name = "Foo";}
Run Code Online (Sandbox Code Playgroud)
Ugly syntax for "records deconstruction"
I asked several collegues of mine a question: "What this code is all about?"
type Person = {Id: int; Name: string;}
let p = {Id = 42; Name = "Foo";}
// What will happend here?
let {Id = id; Name = name} = p
Run Code Online (Sandbox Code Playgroud)
对于每个人来说,"id"和"name"实际上是"左值",尽管他们放置在表达的"右手边",这是令人惊讶的.我知道这更多是关于个人偏好,但对于大多数人来说似乎很奇怪,在一个特定情况下,输出值被放置在表达式的右侧.
我不认为所有这些都是错误,我怀疑这些东西大部分都是功能.
我的问题是:这种晦涩的行为背后是否存在任何理性?
您的示例可以分为两类:记录表达式和记录模式.虽然记录表达式需要声明所有字段并返回一些表达式,但记录模式具有可选字段,并且用于模式匹配.记录上的MSDN页面上有两个明确的部分,值得一读.
在这个例子中,
type AnotherPerson = {Id: int; Name: string}
type Person = {Id: int; Name: string;}
// F# compiler will use second type without any complains or warnings
let p = {Id = 42; Name = "Foo";}
Run Code Online (Sandbox Code Playgroud)
从上面的MSDN页面中规定的规则可以清楚地看出这种行为.
最近声明的类型的标签优先于先前声明的类型的标签
在模式匹配的情况下,您专注于创建所需的一些绑定.所以你可以写
type Person = {Id: int; Name: string;}
let {Id = id} = p
Run Code Online (Sandbox Code Playgroud)
以便获得id绑定以供以后使用.let绑定上的模式匹配可能看起来有点奇怪,但它与函数参数中通常模式匹配的方式非常相似:
type Person = {Id: int; Name: string;}
let extractName {Name = name} = name
Run Code Online (Sandbox Code Playgroud)
我认为关于模式匹配示例的警告是合理的,因为编译器无法猜测您的意图.
但是,不建议使用具有重复字段的不同记录.至少你应该使用合格的名字来避免混淆:
type AnotherPerson = {Id: int; Name: string}
type Person = {Id: int; Name: string; Age: int}
let p = {AnotherPerson.Id = 42; Name = "Foo"}
Run Code Online (Sandbox Code Playgroud)
我想大多数的你的意见是关系到一个事实,即记录名称在记录被定义的命名空间变得直接可用-也就是说,当你定义一个记录Person与性质Name和Id,名称Name和Id是全局可见.这有利有弊:
{Id=1; Name="bob"}您可以告诉编译器您希望始终使用RequireQualifiedAccess属性显式限定名称.这意味着你将不能写只是Id或Name,但你需要始终包含的类型名称:
[<RequireQualifiedAccess>]
type AnotherPerson = {Id: int; Name: string}
[<RequireQualifiedAccess>]
type Person = {Id: int; Name: string;}
// You have to use `Person.Id` or `AnotherPerson.Id` to determine the record
let p = {Person.Id = 42; Person.Name = "Foo" }
Run Code Online (Sandbox Code Playgroud)
这为您提供了更严格的模式,但它使编程变得不那么方便.@pad已经解释了默认值(更加模糊的行为) - 编译器只会选择稍后在源代码中定义的名称.它甚至可以通过查看表达式中的其他字段来推断类型 - 只是因为查看其他字段并不总是有效(例如,当您使用with关键字时),所以最好坚持一个简单的方法一贯的战略.
至于模式匹配,当我第一次看到语法时,我感到非常困惑.我认为它不经常使用,但它可能很有用.
重要的是要意识到F#不使用结构类型(意味着你不能使用带有更多字段的记录作为一个带有较少字段记录的函数的参数).这可能是一个有用的功能,但它不适合.NET类型系统.这基本上意味着你不能指望过于花哨的东西 - 参数必须是一个众所周知的命名记录类型的记录.
当你写:
let {Id = id; Name = name} = p
Run Code Online (Sandbox Code Playgroud)
术语" 左值"是指id并且name出现在模式中而不是表达式中的事实.F#中的语法定义告诉你这样的事情:
expr := let <pat> = <expr>
| { id = <expr>; ... }
| <lots of other expressions>
pat := id
| { id = <pat>; ... }
| <lots of other patterns>
Run Code Online (Sandbox Code Playgroud)
因此,=in 的左侧let是一个模式,而右侧是一个表达式.两者在F#中具有相似的结构 - (x, y)可以用于构造和解构元组.记录也是如此......