优雅的Haskell案例/错误处理

时间:2012-11-06 12:47:08

标签: haskell

我正在努力更好地理解如何处理haskell中的错误情况,并编写了一些代码来帮助我。

处理多个替代方案(如嵌套案例表达式)是否有更好的方法(更优雅,更简洁,更通用)?有关该主题的任何不错的教程?

此示例的伪造类型。 这有点简化,因为大多数不仅有这些嵌套 类型但依赖值只能按顺序检索(例如 从stdin读取id,然后从a中检索此id的记录 数据库)。 因此,此处的嵌套应该演示一种情况,即只有在Nothing已经检查外部值时,内部值才可用。有关更好的示例,请参阅我的new question

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"

到目前为止尝试过:

Naiive实施。

患有“匍匐缩进”

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

模式匹配

目前为止最好的解决方案,但在更复杂的情况下是不可能的 case(请参阅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

下找到包含该代码的要点

2 个答案:

答案 0 :(得分:4)

嵌套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值(右)和错误值(Left):

type MyType = Either MyError Int

现在有许多方便的功能,如eitherEither a的Applicative和Monad实例,可以轻松编写出色的代码:

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

非常适用,或

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

如果您更喜欢monadic符号。 我们可以以程序崩溃的方式使用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并使用提供的函数或ErrorT monad变换器。

答案 1 :(得分:1)

我认为最可读的方式是你已经尝试的maybe函数,通过避免lambdas(无点样式)和使用maybe进行最内部检查(通过替换{{ 1}}与if):

mfilter