如何重写嵌套模式匹配(例如以下示例),以便仅指定None
一次?我认为 Maybe monad解决了这个问题。 F#核心库中有类似的东西吗?或者,有替代方法吗?
match a with
| Some b ->
let c = b.SomeProperty
match c with
| Some d ->
let e = d.SomeProperty
//and so on...
| None -> ()
| None -> ()
答案 0 :(得分:12)
您可以使用内置功能解决此问题: Option.bind
type A =
member this.X : B option = Unchecked.defaultof<_>
and B =
member this.Y : С option = Unchecked.defaultof<_>
and С =
member this.Z : string option = Unchecked.defaultof<_>
let a : A = Unchecked.defaultof<_>
let v =
match
a.X
|> Option.bind (fun v -> v.Y)
|> Option.bind (fun v -> v.Z) with
| Some s -> s
| None -> "<none>"
坦率地说,我怀疑在这里引入完整的“可能”实现(通过计算表达式)可以缩短代码。
编辑:梦想模式 - 开启
我认为如果F#对于特殊情况具有更轻量级的语法,则 Option.bind 的版本可以变小:lambda引用其参数的某个成员:
"123" |> fun s -> s.Length // current version
"123" |> #.Length // hypothetical syntax
这就是如何在Nemerle中重写已经具备此类功能的示例:
using System;
using Nemerle.Utility; // for Accessor macro : generates property for given field
variant Option[T]
{
| Some {value : T}
| None
}
module OptionExtensions
{
public Bind[T, U](this o : Option[T], f : T -> Option[U]) : Option[U]
{
match(o)
{
| Option.Some(value) => f(value)
| Option.None => Option.None()
}
}
}
[Record] // Record macro: checks existing fields and creates constructor for its initialization
class A
{
[Accessor]
value : Option[A];
}
def print(_)
{
// shortened syntax for functions with body -> match over arguments
| Option.Some(_) => Console.WriteLine("value");
| Option.None => Console.WriteLine("none");
}
def x = A(Option.Some(A(Option.Some(A(Option.None())))));
print(x.Value.Bind(_.Value)); // "value"
print(x.Value.Bind(_.Value).Bind(_.Value)); // "none"
答案 1 :(得分:5)
我喜欢desco的回答;一个人应该总是喜欢内置的结构。但是FWIW,这是工作流版本的样子(如果我理解正确的话):
type CE () =
member this.Bind (v,f) =
match v with
| Some(x) -> f x
| None -> None
member this.Return v = v
type A (p:A option) =
member this.P
with get() = p
let f (aIn:A option) = CE () {
let! a = aIn
let! b = a.P
let! c = b.P
return c.P }
let x = f (Some(A(None)))
let y = f (Some(A(Some(A(Some(A(Some(A(None)))))))))
printfn "Your breakpoint here."
答案 2 :(得分:1)
我不建议这样做,但你也可以通过异常处理来解决它:
try
<code that just keeps dotting into option.Value with impunity>
with
| :? System.NullReferenceException -> "None"
我只是想指出异常处理与Maybe / Either monad或Option.bind
的粗略等价。通常更喜欢其中一个引发和捕获异常。
答案 3 :(得分:0)
使用FSharpx中的Option.maybe
:
open FSharpx
type Pet = { Name: string; PreviousOwner: option<string> }
type Person = { Name: string; Pet: option<Pet> }
let pers = { Name = "Bob"; Pet = Some {Name = "Mr Burns"; PreviousOwner = Some "Susan"} }
Option.maybe {
let! pet = pers.Pet
let! prevOwner = pet.PreviousOwner
do printfn "%s was the previous owner of %s." prevOwner pet.Name
}
输出:
Susan was the previous owner of Mr Burns.
但是,例如与此人相反,没有输出:
let pers = { Name = "Bob"; Pet = None }