避免嵌套模式匹配(可能与monad)

Dan*_*iel 10 f#

如何重写嵌套模式匹配(例如以下示例),以便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 -> ()
Run Code Online (Sandbox Code Playgroud)

des*_*sco 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>"  
Run Code Online (Sandbox Code Playgroud)

坦率地说,我怀疑在这里引入完整的"可能"实现(通过计算表达式)可以缩短代码.

编辑:梦想模式 - 开启

我认为如果F#对于特殊情况具有更轻量级的语法,那么使用Option.bind的版本可以变得更小:lambda引用其参数的某个成员:

"123" |> fun s -> s.Length // current version
"123" |> #.Length // hypothetical syntax
Run Code Online (Sandbox Code Playgroud)

这就是如何在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"
Run Code Online (Sandbox Code Playgroud)


Tec*_*ogy 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."
Run Code Online (Sandbox Code Playgroud)

  • 实际上,您可以在CE类型中使用提到的内置构造.就像`member this.Bind(v,f)= Option.bind fv` (5认同)