优雅的Haskell案例/错误处理 [英] Elegant Haskell case/error handling

查看:97
本文介绍了优雅的Haskell案例/错误处理的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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



是否有更好的处理多个替代方法(如嵌套的case表达式)的方式(更优雅,更短,更通用)?任何有关该主题的漂亮教程?



此示例的组合类型。
这有点简化,因为大多数不仅有这些嵌套的
类型,而且只能依次检索相关值(例如
从stdin读取一个id,然后检索该id的记录从
数据库)。 因此,这里的嵌套应该演示一个情况,只有当外部值已经被检查为 Nothing 时,内部值才可用。有关更好的示例,请参阅我的新问题

  type MyType =(Maybe(Maybe Int))


在其他情况下返回int(大于或等于$ b) $ b等于10,Nothing或Just Nothing)会返回不同的错误消息。

  process Nothing〜> 错误
进程(Just Nothing)〜> error2
process(Just(Just 20))〜> error3
process(Just(Just 5))〜> 5



到目前为止:



Naiive实现



遭遇爬行缩进

 过程:: MyType  - > String 
process t = case $ t
Nothing - > 错误
只是 - >案例a
无 - > error2
只需b - >如果b < 10然后显示b elseerror3



也许函数


$ b $ 使用maybe函数可以缩短阅读时间,但也更难阅读。

  process2 :: MyType  - >字符串
process2 t =也许错误(\ a - >也许是error2(\ b - >如果b <10则显示b elseerror3)a)t



模式匹配



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

  process3 :: MyType  - > String 
process3 Nothing =error
process3(Just Nothing)=error2
process3(Just(Just a))
| a< 10 =显示
|否则=error3

代码的要点可以在 https://gist.github.com/4024395

解决方案

嵌套 Maybes 确实很乱。

建议1:滚动自定义错误类型和使用或者



  data MyError = ReadError | TooBig Int 

explain :: MyError - >
解释TooBig i =错误:提供的Int应该最多为10,但它是++ show i

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

  type MyType = MyError Int 



<现在很多方便的函数,如或者,以及适用于的Applicative和Monad实例,或者都可以很容易地编写好的代码:

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

非常适用,或者

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

如果您更喜欢monadic notation。
我们可以使用或者以程序崩溃的方式使用

  myShow :: MyType  - >字符串
myShow =任何一个(error.explain)显示

方式:

  process4 :: MyType  - >字符串
process4 =解释显示



建议2:滚动自定义类型



  data MyType'= OK Int | ReadError | TooBig Int 

并使用模式匹配。这在我的观点中并不像建议1那么好,因为你失去了高阶函数重用,但它比 Maybe(Maybe Int)

$ b更好
$ b

建议3:使用Error monad



阅读关于 Control.Monad.Error 并使用提供的函数或 ErrorT monad变压器。


I'm trying to better understand how to handle error cases in haskell and made wrote some code to help me with that.

Is there a better way (more elegant, shorter, more generic) of handling multiple alternatives (like nested case expressions)? Any nice tutorials about the topic?

A made-up type for this example. This is a bit simplified because mostly there are not only these nested types but dependent values which can only be retrieved sequentially (e.g. reading an id from stdin, then retrieving the record for this id from a database). So the nesting here should demonstrate a case where the inner value will only be available when the outer value is already checked for Nothing. Please see my new question for a better example.

type MyType = (Maybe (Maybe Int))

The goal

Return the int when it is less than 10, on other cases (greater or equal 10, Nothing or Just Nothing) return diferent error messages.

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

Tried so far:

Naiive implementation.

Suffers from "creeping indentation"

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 function

Using the maybe function, which makes it shorter but also harder to read.

process2 :: MyType -> String
process2 t = maybe "error" (\a -> maybe "error2" (\b -> if b < 10 then show b else "error3") a) t

Pattern matching

Nicest solution so far but is not possible in more complex cases (see comment above type definition of MyType).

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

A gist with the code can be found under https://gist.github.com/4024395

解决方案

Nested Maybes is indeed messy.

Suggestion 1: roll a custom error type and use 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

Now use Either to mix Ok values (Right) and error values (Left):

type MyType = Either MyError Int

Now lots of handy functions like either and the Applicative and Monad instances for Either a make it easy to write nice code:

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

is nicely applicative, or

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

if you prefer monadic notation. We can use either in a crash-the-program way

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

or a tell-me-anyway way:

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

Suggestion 2: roll a custom type

data MyType' = OK Int | ReadError | TooBig Int

and use pattern matching. This isn't as nice as suggestion 1 in my veiw, because you lose the higher order function reuse, but it's better than Maybe (Maybe Int)

Suggestion 3: Use the Error monad

Read up about Control.Monad.Error and use the supplied functions or the ErrorT monad transformer.

这篇关于优雅的Haskell案例/错误处理的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆