如何在没有monad的纯Haskell代码中捕获错误

时间:2019-01-03 18:58:12

标签: haskell

它可能已经被要求处死,但是谁能知道最简单的“捕捉”方式

catch
:: Exception e   
=> IO a 
-> (e -> IO a)  
-> IO a

“纯” haskell计算中的错误?

(例如,某些包含head []的代码不安全,我不想将其纯粹化为真实的,也不想强迫其成为单子)

3 个答案:

答案 0 :(得分:6)

无法在纯代码中使用catch。这是设计使然:异常由IO系统处理。这就是catch类型使用IO的原因。如果要用纯代码处理失败,则应使用一种类型来表示失败的可能性。在这种情况下,失败之处在于该值有时可能不存在。

我们在Haskell中用来表示可以存在或不存在的值的类型称为MaybeMaybeMonad的一个实例,因此它是“ monadic”的,但这不应阻止您将其用于预定目的。因此,您想要的功能是来自{em> safe 包中的headMayheadMay :: [a] -> Maybe a

也就是说,如果要避免使用monad,可以改用解压缩列表的函数:

listElim :: b -> (a -> [a] -> b) -> [a] -> b
listElim nil _ [] = nil
listElim _ cons (x:xs) = cons x xs

如您所见,这将[]替换为nil,并将:替换为对cons的调用。现在,您可以编写一个安全的标题,让您指定默认值:

headDef :: a -> [a] -> a
headDef def = listElim def const

不幸的是,函数是Monad的一个实例,因此事实证明您毕竟是“ force [d]要单子”!确实没有逃脱monad的麻烦,所以也许最好学习如何高效地使用它们。

答案 1 :(得分:2)

如果您想过危险的生活,可以使用unsafePerformIO

catch'Pure'
  :: Exception e   
  => a 
  -> (e -> a)
  -> a
catch'Pure' v h = unsafePerformIO $
  evaluate v `catch` (pure . h)

问题是,这不能保证完全正常。例如,如果您将其传递给值

(let a = a in a) `seq` error "hallo!"

有时,编译器有权产生一个无限循环,而在其他时候,会产生错误消息,这违反了对纯度的基本要求。有理由使用看起来像这样的代码,但是需要格外小心才能使其表现良好。

答案 2 :(得分:2)

Control.Spoon中的

spoon package提供了mostly安全的包装程序,用于为此所需的不安全操作。

λ> spoon (head [1,2,3])
Just 1
λ> spoon (head [])
Nothing