4

我试图更好地理解如何在 haskell 中处理错误情况,并编写了一些代码来帮助我解决这个问题。

是否有更好的方法(更优雅、更短、更通用)来处理多种替代方案(如嵌套 case 表达式)?关于该主题的任何不错的教程?

此示例的虚构类型。这有点简化,因为大多数情况下不仅有这些嵌套类型,还有只能按顺序检索的依赖值(例如,从标准输入读取一个 id,然后从数据库中检索该 id 的记录)。所以这里的嵌套应该演示这样一种情况,即只有在已经检查了外部值时,内部值才可用Nothing。请参阅我的新问题以获取更好的示例。

type MyType = (Maybe (Maybe Int))

目标

当它小于 10 时返回 int,在其他情况下(大于或等于 10、Nothing 或 Just Nothing)返回不同的错误消息。

process Nothing ~> "error"
process (Just Nothing) ~> "error2"
process (Just (Just 20)) ~> "error3"
process (Just (Just 5)) ~> "5"

到目前为止尝试过:

天真的实施。

遭受“爬行压痕”

process :: MyType -> String
process t = case t of
        Nothing -> "error"
        Just a -> case a of
                    Nothing -> "error2"
                    Just b -> if b < 10 then show 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

模式匹配

迄今为止最好的解决方案,但在更复杂的情况下是不可能的(参见上面的 MyType 类型定义的注释)。

process3 :: MyType -> String
process3 Nothing = "error"
process3 (Just Nothing) = "error2"
process3 (Just (Just a))
  | a < 10 = show a
  | otherwise = "error3"

可以在https://gist.github.com/4024395下找到带有代码的要点

4

2 回答 2

6

嵌套Maybes确实很乱。

建议1:滚动自定义错误类型并使用Either

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

现在使用 Either 混合 Ok 值(右)和错误值(左):

type MyType = Either MyError Int

现在有很多方便的函数,比如eitherApplicative 和 Monad 实例,Either a让编写漂亮的代码变得容易:

myAdd :: MyType -> MyType -> MyType
myAdd i1 i2 = (+) <$> i1 <*> i2

非常适用,或者

myMult i1 i2 = do
    a <- i1
    b <- i2
    return $ a * b

如果你更喜欢一元符号。我们可以either以崩溃程序的方式使用

myShow :: MyType -> String
myShow = either (error.explain) show 

或告诉我无论如何:

process4 :: MyType -> String
process4 = either explain show

建议2:滚动自定义类型

data MyType' = OK Int | ReadError | TooBig Int

并使用模式匹配。在我看来,这不如建议 1 好,因为你失去了高阶函数重用,但它比Maybe (Maybe Int)

建议 3:使用 Error monad

阅读Control.Monad.Error并使用提供的函数或ErrorTmonad 转换器。

于 2012-11-06T13:28:45.107 回答
1

我认为最易读的方法是maybe您已经尝试过的函数,只是通过避免 lambdas(无点样式)并使用maybe甚至用于最里面的检查(通过替换ifmfilter)来稍微美化:

import Control.Monad(mfilter)

process2 :: MyType -> String
process2 =  
  maybe "error"  $ 
  maybe "error2" $ 
  maybe "error3" 
  show . mfilter (<10) . Just 
于 2012-11-06T22:45:33.953 回答