我在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()
,但编译器需要它。
答案 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)