在计算表达式中争用TryWith [英] Wrangling TryWith in Computation expressions

查看:99
本文介绍了在计算表达式中争用TryWith的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

(由于无法理解" FParsec,我遵循了我在某处阅读的建议,并开始尝试自己编写一个解析器.不知何故,我发现了看起来有机会尝试对其进行单数化的机会,现在我有了N个问题...)

这是我的结果"类型(简体)

This is my 'Result' type (simplified)

type Result<'a> = 
    | Success of 'a
    | Failure of string

这里是计算表达式生成器

Here's the computation expression builder

type ResultBuilder() =
    member m.Return a = Success(a)
    member m.Bind(r,fn) =
        match r with
        | Success(a) -> fn a
        | Failure(m) -> Failure(m)

在第一个示例中,一切都按预期工作(编译):

In this first example, everything works (compiles) as expected:

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) }

我的问题在这里:我希望能够捕捉到合并"函数中的任何失败并返回失败,但是它说我应该定义一个零".

My problem is here: I'd like to be able to catch any failure in the 'combining' function and return a failure, but it says that I should define a 'Zero'.

    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) }

不知道我应该在零中返回什么,我只放入了member m.Zero() = Failure("hello world"),现在它说我需要TryWith.

Having no idea what I should return in a Zero, I just threw in member m.Zero() = Failure("hello world"), and it now says I need TryWith.

所以:

member m.TryWith(r,fn) =
    try 
        r()
    with
     | ex -> fn ex

现在它想要延迟,所以member m.Delay f = (fun () -> f()).

And now it wants Delay, so member m.Delay f = (fun () -> f()).

这时(在ex -> Failure上)说This expression should have type 'unit', but has type 'Result<'a>',然后我举起手臂转向你们...

At which point it says (on the ex -> Failure), This expression should have type 'unit', but has type 'Result<'a>', and I throw up my arms and turn to you guys...

播放链接: http://dotnetfiddle.net/Ho1sGS

推荐答案

with块也应从计算表达式返回结果.由于要返回Result.Failure,因此需要定义成员m.ReturnFrom a = a并使用它从with块中返回Failure.在try块中,您还应该指定fn如果不抛出成功,则返回成功.

The with block should also return a result from the computation expression. Since you want to return Result.Failure you need to define the member m.ReturnFrom a = a and use it to return the Failure from the with block. In the try block you should also specify that fn returns Success if it doesn't throw.

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)
                         }

更新:

原始实现显示警告,而不是错误.自try块返回以来,未使用with块中的表达式,因此您可以简单地添加|> ignore.在这种情况下,如果抛出fn,则返回值为m.Zero(),唯一的区别是您将得到"hello world"而不是ex.Message.下面举例说明.此处的完整脚本: http://dotnetfiddle.net/mFbeZg

The original implementation was showing a warning, not an error. The expression in the with block was not used since you returned from the try block, so you could simply add |> ignore. In that case if fn throws then the return value is m.Zero() and the only difference is that you would get "hello world" instead of ex.Message. Illustrated with an example below. Full script here: http://dotnetfiddle.net/mFbeZg

使用|> ignore来使警告静音的原始实现:

Original implementation with |> ignore to mute the warning:

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"

这篇关于在计算表达式中争用TryWith的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆