F# “是否有一个标准的单子?”;相反的;也许是monad?

F# “是否有一个标准的单子?”;相反的;也许是monad?,f#,monads,F#,Monads,maybe monad允许链接一组所有操作都可能失败的操作符(通过返回None),如果每个子操作都成功,最后返回Some result,如果任何操作失败,则返回None。下面是一个小的虚拟示例: type MaybeBuilder() = member this.Return(x) = Some x member this.Bind(m, f) = match m with | Some x -> f x

maybe monad允许链接一组所有操作都可能失败的操作符(通过返回None),如果每个子操作都成功,最后返回
Some result
,如果任何操作失败,则返回
None
。下面是一个小的虚拟示例:

type MaybeBuilder() =
    member this.Return(x) = 
        Some x
    member this.Bind(m, f) = 
        match m with
        | Some x -> f x
        | None -> None

let maybe = MaybeBuilder()

let list = [1;2;3;4]

// evaluates to Some 3
maybe {
    let! x1 = List.tryFind ((=) 1) list
    let! x2 = List.tryFind ((=) 2) list
    return x1 + x2
}

// evaluates to None 
maybe {
    let! x1 = List.tryFind ((=) 1) list
    let! x2 = List.tryFind ((=) 6) list
    return x1 + x2
}
这大致相当于:

// evaluates to Some 3
match List.tryFind ((=) 1) list with
| None -> None
| Some x1 ->
    match List.tryFind ((=) 2) list with
    | None -> None
    | Some x2 -> Some (x1 + x2)

// evaluates to None
match List.tryFind ((=) 1) list with
| None -> None
| Some x1 ->
    match List.tryFind ((=) 6) list with
    | None -> None
    | Some x2 -> Some (x1 + x2)
在我的一段代码中,我正在做与此相反的事情,返回第一个成功命中:

// evaluates to Some 1
match List.tryFind ((=) 1) list with
| Some x1 -> Some x1
| None ->
    match List.tryFind ((=) 2) list with
    | Some x2 -> Some x2
    | None -> None

// evaluates to Some 2
match List.tryFind ((=) 6) list with
| Some x1 -> Some x1
| None ->
    match List.tryFind ((=) 2) list with
    | Some x2 -> Some x2
    | None -> None

这是否也可以使用monad来获得良好的计算表达式语法?

Haskell使用定义为以下内容的
MonadPlus
类型类来实现这一点:

class Monad m => MonadPlus m where
  mzero :: m a
  mplus :: m a -> m a -> m a
可能
将此类型类实现为

instance MonadPlus Maybe where
  mzero                   = Nothing
  Nothing `mplus` Nothing = Nothing
  Just x  `mplus` Nothing = Just x
  Nothing `mplus` Just x  = Just x
  Just x  `mplus` Just y  = Just x

似乎
MonadPlus
mzero
mplus
成员对应于F#计算表达式使用的
Zero
Combine
成员。例如,以下是返回0且从不执行
printfn
语句的计算:

let test() = imperative {
  return 0
  printfn "after return!"
  return 1 }
我认为您的代码示例可以写成:

imperative { return! List.tryFind ((=) 1) list 
             return! List.tryFind ((=) 2) list }
它是如何工作的? 正如您所建议的(Lee也提到过),类型也基于
选项Tomas的解决方案

imperative { 
    return! List.tryFind ((=) 1) list
    return! List.tryFind ((=) 2) list }
做我想做的事,但我刚刚意识到,我也可以更简单地实现我所需要的:

// evaluates to Some 1
[1;2] |> List.tryPick (fun x -> List.tryFind ((=) x) list)
// evaluates to Some 2
[6;2] |> List.tryPick (fun x -> List.tryFind ((=) x) list)

您还可以定义一个简单的函数来执行此操作:

let orElse f = function
  | None -> f()
  | Some _ as x -> x
您可以将任意多个函数链接在一起,第一个
Some
结果作为整个表达式的结果返回:

List.tryFind ((=) 1) list
|> orElse (fun () -> List.tryFind ((=) 2) list)
|> orElse (fun () -> List.tryFind ((=) 3) list)
|> orElse (fun () -> List.tryFind ((=) 4) list)

这种特殊情况也可以用序列模拟:

let tests = seq {
  yield List.tryFind ((=) 5) list
  yield List.tryFind ((=) 3) list
  yield List.tryFind ((=) 6) list
}

tests |> Seq.tryFind Option.isSome |> Option.bind id

是的,
mzero
mplus
对应于
Zero
Combine
:-)但为了使其行为合理,您还需要添加
延迟(因为F#并不懒惰)当
/
尝试使用
/
时,为
/
添加
是一个好主意。为了在计算块中支持标准语言结构…+1当一个人有一个大锤子时,很容易忽略简单的解决方案!这也是一个很好的解决方案
let orElse f = function
  | None -> f()
  | Some _ as x -> x
List.tryFind ((=) 1) list
|> orElse (fun () -> List.tryFind ((=) 2) list)
|> orElse (fun () -> List.tryFind ((=) 3) list)
|> orElse (fun () -> List.tryFind ((=) 4) list)
let tests = seq {
  yield List.tryFind ((=) 5) list
  yield List.tryFind ((=) 3) list
  yield List.tryFind ((=) 6) list
}

tests |> Seq.tryFind Option.isSome |> Option.bind id