(未能'grok'FParsec,我按照我在某处读到的建议开始尝试自己编写一个小解析器。不知怎的,我发现了什么看起来像是一个机会尝试和monadify它,现在我有N个问题......)
这是我的'结果'类型(简化)
type Result<'a> =
| Success of 'a
| Failure of string
这是计算表达式构建器
type ResultBuilder() =
member m.Return a = Success(a)
member m.Bind(r,fn) =
match r with
| Success(a) -> fn a
| Failure(m) -> Failure(m)
在第一个例子中,一切都按预期工作(编译):
module Parser =
let res = ResultBuilder()
let Combine p1 p2 fn =
fun a -> res { let! x = p1 a
let! y = p2 a
return fn(x,y) }
我的问题在于:我希望能够捕捉到'组合'功能中的任何失败并返回失败,但它说我应该定义'零'。
let Combine2 p1 p2 fn =
fun a -> res { let! x = p1 a
let! y = p2 a
try
return fn(x,y)
with
| ex -> Failure(ex.Message) }
我不知道应该在Zero中返回什么,我只是扔了member m.Zero() = Failure("hello world")
,现在它说我需要TryWith
。
所以:
member m.TryWith(r,fn) =
try
r()
with
| ex -> fn ex
现在它想要延迟,所以member m.Delay f = (fun () -> f())
。
它说(在ex -> Failure
),This expression should have type 'unit', but has type 'Result<'a>'
,然后我举起手臂转向你们......
答案 0 :(得分:2)
with
块也应该返回计算表达式的结果。由于您要返回Result.Failure,您需要定义成员m.ReturnFrom a = a
并使用它从with
块返回失败。在try
块中,您还应指定fn
如果不抛出则返回Success。
let Combine2 p1 p2 fn =
fun a -> res { let! x = p1 a
let! y = p2 a
return!
try
Success(fn(x,y))
with
| ex -> Failure(ex.Message)
}
<强>更新强>
原始实现显示警告,而不是错误。由于您从with
块返回,因此未使用try
块中的表达式,因此您只需添加|> ignore
即可。在这种情况下,如果fn
抛出,则返回值为m.Zero()
,唯一的区别是您将获得"hello world"
而不是ex.Message
。下面举例说明。完整的脚本:http://dotnetfiddle.net/mFbeZg
使用|> ignore
进行原始实施以使警告静音:
let Combine3 p1 p2 fn =
fun a -> res { let! x = p1 a
let! y = p2 a
try
return fn(x,y)
with
| ex -> Failure(ex.Message) |> ignore // no warning
}
运行它:
let comb2 a =
let p1' x = Success(x)
let p2' y = Success(y)
let fn' (x,y) = 1/0 // div by zero
let func = Parser.Combine2 p1' p2' fn' a
func()
let comb3 a =
let p1' x = Success(x)
let p2' y = Success(y)
let fn' (x,y) = 1/0 // div by zero
let func = Parser.Combine3 p1' p2' fn' a
func()
let test2 = comb2 1
let test3 = comb3 1
结果:
val test2 : Result<int> = Failure "Attempted to divide by zero."
val test3 : Result<int> = Failure "hello world"
答案 1 :(得分:2)
如果您想在计算构建器中支持try
... with
,则需要添加TryWith
(正如您所尝试的)以及其他一些成员,包括{{ 1}}和Delay
(取决于您希望如何实现Run
)。为了能够返回失败,您还需要通过添加Delay
来支持return!
:
ReturnFrom
现在您可以执行以下操作:
type ResultBuilder() =
member m.Return a = Success(a)
member m.Bind(r,fn) =
match r with
| Success(a) -> fn a
| Failure(m) -> Failure(m)
member m.TryWith(r,fn) =
try r() with ex -> fn ex
member m.Delay(f) = f
member m.Run(f) = f()
member m.ReturnFrom(r) = r
诀窍是普通分支仅使用let Combine2 p1 p2 fn = fun a -> res {
let! x = p1 a
let! y = p2 a
try
return fn(x,y)
with ex ->
return! Failure(ex.Message) }
(表示成功),但异常处理程序使用return
使用return!
返回显式创建的结果。
也就是说,如果你对解析器感兴趣,那么你需要使用不同的类型 - 你在这里描述的更像是选项(或者可能)monad。要实现解析器组合器,您需要一个表示解析器的类型,而不是解析器的 result 。请参阅示例this article。