异常monad的F#计算表达式

时间:2015-05-12 09:29:01

标签: f# monads

我在F#中实现了一个异常monad。首先,我采用了ML方法,该方法有效:

module ExceptionMonad =
  type exception_ = string
  type 'a t = Raise of exception_ | Return of 'a

  let return' t = Return t
  let raise' e = Raise e
  let (>>=) m k =
    match m with
    | Raise e -> Raise e
    | Return a -> k a

module TestExceptionMonad =
  open ExceptionMonad

  let test_exception_monad =
    begin
      return' 1 >>= fun a ->
      return' 0 >>= fun b ->
      if b = 0
        then raise' "divide by zero"
        else return' (a / b)
    end (* => raise' "divide by zero" *)

现在我尝试使用F#计算表达式:

type exception_ = string
type 'a exception_monad = Raise of exception_ | Return of 'a
type ExceptionMonad() =
    member x.Bind(m, k) =
        match m with
        | Raise e -> Raise e
        | Return a -> k a
    member x.Return(t) = Return t
    member x.Zero() = Return 0

module TestExceptionMonad =
    let monad = new ExceptionMonad()

    let test_exception_monad =
        monad {
            let! a = Return 1 in
            let! b = Return 0 in
            if b = 0 then
                Raise "divide by zero"
            else
                return (a / b)
        } (* => Raise "divide by zero" *)

但是F#抱怨表达式Raise "divide by zero"的类型为unit。事实并非如此,但我猜有一些代码生成在幕后进行,错误就是指。我也不确定为什么需要x.Zero(),但编译器需要它。

2 个答案:

答案 0 :(得分:5)

在F#计算表达式中,如果要从计算中返回“monadic value”,则需要使用return!构造:

if b = 0 then return! Raise "divide by zero"
else return (a / b)

为了支持这一点,您还需要将ReturnFrom添加到计算构建器:

member x.ReturnFrom(c) = c

也就是说,如果你试图理解计算构建器在F#中是如何工作的,那么为异常实现计算构建器是一个很好的练习。但除非你有充分的理由想要它,否则我不会在实践中推荐它。 F#已经直接支持你可以使用的异常,而没有任何在大多数情况下运行良好的特殊语法,所以实际上没有必要用更复杂的方法来替换它。

如果你正在寻找输入验证(而不是普通的异常处理),那么有一个很好的F#项目实现了Chessie的计算,但是再一次 - 这比输入验证更多异常处理。

答案 1 :(得分:0)

您需要使用其中一个绑定操作来执行monadic操作。即return!let!do!。在这种情况下,你可以写:

        if b = 0 then
            return! Raise "divide by zero"
        else
            return (a / b)