【发布时间】:2015-03-21 07:57:48
【问题描述】:
F# 的“选项”似乎是一种使用类型系统将已知存在的数据与可能存在或不存在的数据分开的好方法,我喜欢 match 表达式强制所有情况都是考虑:
match str with
| Some s -> functionTakingString(s)
| None -> "abc" // The compiler would helpfully complain if this line wasn't present
s(与str 相对)是string 而不是string option,这一点非常有用。
但是,在处理具有可选字段的记录时...
type SomeRecord =
{
field1 : string
field2 : string option
}
...并且这些记录正在被过滤,match 表达式感觉没有必要,因为在 None 的情况下没有什么明智的做法,但是这个...
let functionTakingSequenceOfRecords mySeq =
mySeq
|> Seq.filter (fun record -> record.field2.IsSome)
|> Seq.map (fun record -> functionTakingString field2) // Won't compile
...不会编译,因为虽然没有填充field2 的记录已被过滤掉,但field2 的类型仍然是string option,而不是string。
我可以定义另一种记录类型,其中 field2 不是可选的,但这种方法似乎很复杂,并且可能不适用于许多可选字段。
我已经定义了一个运算符,如果选项是 None...
let inline (|?!) (value : 'a option) (message : string) =
match value with
| Some x -> x
| None -> invalidOp message
...并且已经将之前的代码改成了这个...
let functionTakingSequenceOfRecords mySeq =
mySeq
|> Seq.filter (fun record -> record.field2.IsSome)
|> Seq.map (fun record -> functionTakingString (record.field2 |?! "Should never happen")) // Now compiles
...但这似乎并不理想。我可以使用Unchecked.defaultof 而不是引发异常,但我不确定这会更好。关键是None这个case过滤后不相关。
有没有更好的处理方法?
编辑
非常有趣的答案让我注意到了记录模式匹配,我不知道,Value,我见过但被误解了(我看到如果None,它会抛出NullReferenceException )。但我认为我的示例可能很糟糕,因为我更复杂的现实问题涉及使用记录中的多个字段。我怀疑我遇到了类似...
|> Seq.map (fun record -> functionTakingTwoStrings record.field1 record.field2.Value)
除非还有别的?
【问题讨论】:
标签: f# pattern-matching field record optional