我试图更好地了解如何处理Haskell中的错误情况,并编写了一些代码来帮助我。
有没有更好的方法(更优雅、更短、更通用)来处理多个替代项(如嵌套大小写表达式)?关于该主题的任何精彩教程?
此示例的虚构类型。 这有点简化,因为大多数情况下不仅有这些嵌套的 类型但只能按顺序检索的依赖值(例如 从标准输入读取 ID,然后从 数据库)。因此,此处的嵌套应该演示一种情况,即只有在已经检查了外部值Nothing
时才可用。请参阅我的新问题以获取更好的示例。
type MyType = (Maybe (Maybe Int))
目标
当 int 小于 10 时返回 int,在其他情况下(大于或 等于 10,无或只是无)返回不同的错误消息。
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"
也许功能
使用 may 函数,这使得它更短,但也更难阅读。
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 下找到带有代码的要点
>嵌套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
现在使用任一混合 Ok 值(右)和错误值(左):
type MyType = Either MyError Int
现在,许多方便的函数,如either
和用于Either a
的Applicative和Monad实例,使编写漂亮的代码变得容易:
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:使用错误单体
阅读有关Control.Monad.Error的信息,并使用提供的函数或ErrorT
的monad变压器。
我认为最易读的方法是您已经尝试过的maybe
函数,只是通过避免 lambdas(无点样式)并使用maybe
进行最里面的检查(通过将if
替换为mfilter
):
import Control.Monad(mfilter)
process2 :: MyType -> String
process2 =
maybe "error" $
maybe "error2" $
maybe "error3"
show . mfilter (<10) . Just