管道顺序中的异常处理 [英] Exception handling in pipeline sequence

查看:77
本文介绍了管道顺序中的异常处理的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在使用基本的2D CAD引擎,并且管道运算符大大改善了我的代码.基本上,几个函数以空间中的点(x,y)开头,并在多次移动操作后计算最终位置:

I' working on a basic 2D CAD engine and the pipeline operator significantly improved my code. Basically several functions start with a point (x,y) in space and compute a final position after a number of move operations:

let finalPosition =
    startingPosition
    |> moveByLengthAndAngle x1 a1 
    |> moveByXandY x2 y2
    |> moveByXandAngle x3 a3
    |> moveByLengthAndAngle x4 a4
    // etc...

这非常容易阅读,我想保持这种方式.显然,各种x1,a1等在实际代码中都有一个含义名称.

This is incredibly easy to read and I'd like to keep it that way. The various x1, a1, etc. obviously have a meaning name in the real code.

现在,新要求是引入异常处理.在整个操作链上进行大量尝试是不够的,因为我想知道是哪条线导致了异常.我需要知道哪个参数无效,以便用户知道必须更改哪个参数.

Now the new requirement is to introduce exception handling. A big try/with around the whole operation chain is not enough because I'd like to know which line caused the exception. I need to know which argument is invalid, so that the user knows what parameter must be changed.

例如,如果第一行(moveByLengthAndAngle x1 a1)引发异常,我想告诉您嘿,-90是a1的无效值!a1必须在45到90之间!".鉴于可以在序列中使用许多相同类型的操作,因此不足以为每个操作定义不同的异常类型(在此示例中,我无法确定错误是先行还是后继).

For example if the first line (moveByLengthAndAngle x1 a1) raises an exception, I'd like to tell something like "Hey, -90 is an invalid value for a1! a1 must be between 45 and 90!". Given that many operations of the same type can be used in the sequence it's not enough to define a different exception type for each operation (in this example I wouldn't be able to tell if the error was the first or the last move).

显而易见的解决方案是将链分成单个let语句,每个语句位于其各自的try/with中.但是,这会使我漂亮易读的代码有点混乱,不再那么可读了.

The obvious solution would be to split the chain in single let statements, each within its respective try/with. This however would make my beautiful and readable code a bit messy, not so readable anymore.

是否可以在不牺牲当前代码的可读性和美观性的前提下满足此要求?

(注意.现在,每个moveBy函数都会在出现错误的情况下引发异常,但是我可以自由更改,例如返回一个选项,一个更大的元组,或者在需要时进行其他操作).

(note. right now every moveBy function raises an exception in case of errors, but I'm free to change for ex. to return an option, a bigger tuple, or just anything else if needed).

推荐答案

如何折叠Choices?假设您不是通过流水线操作,而是这样表示它们:

How about folding over Choices? Let's say that instead of pipelining the actions, you represent them like this:

let startingPosition = 0. ,0.

let moveByLengthAndAngle l a (x,y) = x,y // too lazy to do the math
let moveByXandY dx dy (x,y) = 
    //failwith "oops"
    x+dx, y+dy
let moveByXandAngle dx a (x,y) = x+dx, y

let actions = 
    [
        moveByLengthAndAngle 0. 0., "failed first moveByLengthAndAngle"
        moveByXandY 1. 2., "failed moveByXandY"
        moveByXandY 3. 4., "failed moveByXandY"
        moveByXandAngle 3. 4., "failed moveByXandAngle"
        moveByLengthAndAngle 4. 5., "failed second moveByLengthAndAngle"
    ]

actions的类型为((float * float -> float * float) * string) list.

现在,使用 FSharpx ,我们将操作移至选择"并折叠/绑定(不确定的用法类似于在Haskell中折叠):

Now, using FSharpx we lift the actions to Choice and fold/bind (not sure how to call it this is similar to foldM in Haskell) over the actions:

let folder position (f,message) =
    Choice.bind (Choice.protect f >> Choice.mapSecond (konst message)) position

let finalPosition = List.fold folder (Choice1Of2 startingPosition) actions

finalPosition的类型为Choice<float * float, string>,即它是所有这些函数的最终结果,或者是错误(如上表所定义).

finalPosition is of type Choice<float * float, string> , i.e. it's either the final result of all those functions, or an error (as defined in the table above).

最后一个代码段的说明:

Explanation for this last snippet:

  • Choice.protect与Tomas的保护类似,不同之处在于,当它找到异常时,它将返回包装在Choice2Of2中的异常.如果没有异常,它将返回包装在Choice1Of2中的结果.
  • Choice.mapSecond使用在操作表中定义的错误消息来更改Choice2Of2中的此潜在异常.代替(konst消息),它也可以是使用异常生成错误消息的函数.
  • Choice.bind针对当前位置运行此受保护"操作.如果当前位置错误(即Choice2Of2),它将不会执行实际操作.
  • 最后,折叠应用所有沿/累积结果Choice的动作(当前位置或错误).

所以现在我们只需要进行模式匹配就可以处理每种情况(正确的结果或错误):

So now we just have to pattern match to handle each case (correct result or error):

match finalPosition with
| Choice1Of2 (x,y) -> 
    printfn "final position: %f,%f" x y
| Choice2Of2 error -> 
    printfn "error: %s" error

如果您取消上面的failwith "oops"注释,则finalPosition将为Choice2Of2 "failed moveByXandY"

If you uncomment failwith "oops" above, finalPosition will be a Choice2Of2 "failed moveByXandY"

这篇关于管道顺序中的异常处理的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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