优雅的Haskell案例/错误处理

优雅的Haskell案例/错误处理,haskell,Haskell,我试图更好地理解如何在haskell中处理错误案例,并编写了一些代码来帮助我 是否有更好的方法(更优雅、更短、更通用)处理多个备选方案(如嵌套大小写表达式)?有关于这个主题的精彩教程吗? 本例中的一个组合类型。 这有点简化,因为大多数情况下,不只是这些嵌套的 类型,但只能按顺序检索的依赖值(例如。 从stdin读取一个id,然后从 数据库)。因此,这里的嵌套应该演示这样一种情况,即内部值只有在外部值已被检查为Nothing时才可用。请参阅我的,以获得更好的示例 目标 在其他情况下(大于或等于),

我试图更好地理解如何在haskell中处理错误案例,并编写了一些代码来帮助我

是否有更好的方法(更优雅、更短、更通用)处理多个备选方案(如嵌套大小写表达式)?有关于这个主题的精彩教程吗?

本例中的一个组合类型。 这有点简化,因为大多数情况下,不只是这些嵌套的 类型,但只能按顺序检索的依赖值(例如。 从stdin读取一个id,然后从 数据库)。因此,这里的嵌套应该演示这样一种情况,即内部值只有在外部值已被检查为
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