优雅的Haskell案例/错误处理
我试图更好地理解如何在haskell中处理错误案例,并编写了一些代码来帮助我 是否有更好的方法(更优雅、更短、更通用)处理多个备选方案(如嵌套大小写表达式)?有关于这个主题的精彩教程吗? 本例中的一个组合类型。 这有点简化,因为大多数情况下,不只是这些嵌套的 类型,但只能按顺序检索的依赖值(例如。 从stdin读取一个id,然后从 数据库)。因此,这里的嵌套应该演示这样一种情况,即内部值只有在外部值已被检查为优雅的Haskell案例/错误处理,haskell,Haskell,我试图更好地理解如何在haskell中处理错误案例,并编写了一些代码来帮助我 是否有更好的方法(更优雅、更短、更通用)处理多个备选方案(如嵌套大小写表达式)?有关于这个主题的精彩教程吗? 本例中的一个组合类型。 这有点简化,因为大多数情况下,不只是这些嵌套的 类型,但只能按顺序检索的依赖值(例如。 从stdin读取一个id,然后从 数据库)。因此,这里的嵌套应该演示这样一种情况,即内部值只有在外部值已被检查为Nothing时才可用。请参阅我的,以获得更好的示例 目标 在其他情况下(大于或等于),
Nothing
时才可用。请参阅我的,以获得更好的示例
目标
在其他情况下(大于或等于),当int小于10时返回int
等于10,无或无)返回不同的错误消息
process Nothing ~> "error"
process (Just Nothing) ~> "error2"
process (Just (Just 20)) ~> "error3"
process (Just (Just 5)) ~> "5"
迄今为止:
积极实施。
患有“蠕动压痕”
process::MyType->String
过程t=案例t
无->“错误”
只是一个->案例
无->“错误2”
只要b->如果b<10,则显示b else“error3”
也许功能
使用maybe函数,使其更短,但也更难阅读
process2 :: MyType -> String
process2 t = maybe "error" (\a -> maybe "error2" (\b -> if b < 10 then show b else "error3") a) t
process2::MyType->String
process2 t=可能是“错误”(\a->可能是“错误2”(\b->如果b<10,则显示b else“error3”)a)t
模式匹配
迄今为止最好的解决方案,但在更复杂的情况下是不可能的
案例(请参见MyType类型定义上面的注释)
process3::MyType->String
process3 Nothing=“错误”
process3(只是Nothing)=“error2”
流程3(仅(仅a))
|a<10=显示a
|否则=“error3”
在嵌套的下可以找到代码的要点可能是非常混乱的 建议1:滚动自定义错误类型并使用
或
现在,使用以下任一选项混合确定值(右)和错误值(左):
现在,许多方便的函数,如other
,以及other a
的Applicative和Monad实例,使编写漂亮的代码变得容易:
myAdd :: MyType -> MyType -> MyType
myAdd i1 i2 = (+) <$> i1 <*> i2
或者用一种无论如何都要告诉我的方式:
process4 :: MyType -> String
process4 = either explain show
建议2:滚动自定义类型
并使用模式匹配。这不像我的文章中的建议1那么好,因为您失去了高阶函数重用,但它比Maybe(Maybe Int)
建议3:使用错误monad
阅读并使用提供的函数或error
monad transformer。我认为最可读的方法是您已经尝试过的maybe
函数,只是通过避免lambdas(无点样式)和使用maybe
甚至用于最内部的检查来稍微美化一下(将if
替换为mfilter
):
import Control.Monad(mfilter)
process2::MyType->String
进程2=
可能是“错误”$
可能是“错误2”$
也许是“错误3”
show.mfilter(谢谢你的回答,尤其是一元符号看起来很不错。我还不知道如何将其应用于我的初始问题。我想我知道我可以将我的进程
函数更改为::MyType->MyError
并调用解释$process x
,以获得所需的结果,但如何实现进程s
在这种情况下?好的,我想我现在明白了。谢谢你的时间!我的进程4
函数与你的进程
函数一样工作。如果可以,它会显示结果,如果不可以,则显示错误消息。我添加了一些解释,说明为什么我在我的示例中使用嵌套的可能
。@Svenkoske这是有意义的,是的。我仍然这样做nk不过,最好滚动您自己的错误类型。Maybe(Maybe Int)
确实可以保证,如果出现无法读取的错误,就不会出现太大的错误,但是数据
声明也为您提供了这一保证。没有什么可以阻止您在早期和之后得出读取错误
结论-您不必在每个阶段都进行包装。
process3 :: MyType -> String
process3 Nothing = "error"
process3 (Just Nothing) = "error2"
process3 (Just (Just a))
| a < 10 = show a
| otherwise = "error3"
data MyError = ReadError | TooBig Int
explain :: MyError -> String
explain ReadError = "Error: the requested Int could not be found"
explain TooBig i = "Error: the supplied Int should be at most 10, but it was " ++ show i
type MyType = Either MyError Int
myAdd :: MyType -> MyType -> MyType
myAdd i1 i2 = (+) <$> i1 <*> i2
myMult i1 i2 = do
a <- i1
b <- i2
return $ a * b
myShow :: MyType -> String
myShow = either (error.explain) show
process4 :: MyType -> String
process4 = either explain show
data MyType' = OK Int | ReadError | TooBig Int
import Control.Monad(mfilter)
process2 :: MyType -> String
process2 =
maybe "error" $
maybe "error2" $
maybe "error3"
show . mfilter (<10) . Just