16

我很惊讶,我无法在任何地方找到答案。如何捕捉(并忽略)对错误函数的调用?

我正在写一个roguelike,并且使用来自hackage的ncurses库,这是一个相当不错的ncurses库封装。现在ncurses有这个怪癖,如果你试图写下右下角字符,它会这样做,然后它试图将光标移动到下一个字符,然后失败,因为没有地方移动它。它返回一个你只能忽略的错误值。

我的问题是,haskell ncurses库编写器忠实地检查所有调用中的任何错误,并且当有一个错误时,他会调用:error“drawText:etc”等。

在其他语言中,比如c或者python,为了解决这个问题,你不得不忽略错误或者忽略异常,但是对于我来说,我无法弄清楚如何在haskell中做到这一点。错误功能是不可恢复的?

我会在本地修改库,以便不检查该函数上的错误(如果必须的话),但我讨厌这样做。我也打开任何解决方法,这将允许我绘制最后一个字符而不移动光标,但我不认为这是可能的。

+0

Hoogle for“catch”[1]。第二个链接关闭。 [1] http://haskell.org/hoogle/?hoogle=catch – 2010-11-22 19:13:30

+0

不幸的是,有问题的错误不在IO monad中。那么它开始于IO,然后你去runCurses,这是Curses monad,然后updateWindow,这是更新monad。所以我不认为保罗的答案会起作用。但卢奎看起来有潜力,当我回家时我会尝试。 – 2010-11-22 20:24:44

+0

看着ncurses我感觉它不会工作。 'drawText'不会调用'error',它直接委托给C函数。并且它返回'Update' monad,它是'ReaderT Window IO a' ='Window - > IO a',所以'unsafeCleanup'只有在产生该函数时出错时才会工作,而不是运行动作(不太可能)。我认为你的选择是:捕捉顶层IO中的错误,或者打开curses源码让你注入一个更本地的catch函数。 (它可以轻松完成,只是打破封装) – luqui 2010-11-22 21:52:28

回答

12

error被认为是可观察的无限循环。您只能在IO中捕获error,这就像是在说“如果您知道魔术,您可以。”。但是从Haskell非常好的部分,纯代码,它是不可恢复的,因此强烈建议而不是在您的代码中使用,只是您将永远使用无限循环作为错误代码。

ncurses是粗鲁的,让你做魔法纠正它。我会说unsafePerformIO将被保证清理它。除此之外,这与保罗的答案大致相同。

import qualified Control.Exception as Exc 

{-# NOINLINE unsafeCleanup #-} 
unsafeCleanup :: a -> Maybe a 
unsafeCleanup x = unsafePerformIO $ Exc.catch (x `seq` return (Just x)) handler 
    where 
    handler exc = return Nothing `const` (exc :: Exc.ErrorCall) 

然后包unsafeCleanup周围将评估一个错误把它变成一个Maybe任意值。

+0

顺便说一下,'Exc。catch(Exc.evaluate(Just $!x))handler'将会稍微更清晰imho – hvr 2013-03-28 00:00:54

+1

FWIW现在在[spoon](http://hackage.haskell.org/package/spoon)库中实现。 – luqui 2013-03-28 00:07:22

+0

勺子也可以做'a - >或者是一个字符串a'而不是'a - >也许a',这样我就可以看到被捕获的错误信息了吗?或者那是禁忌? – 2015-03-06 12:03:22

15

您可以使用Control.Exceptioncatch。但是,请注意,您需要在IO monad中执行此操作。

import qualified Control.Exception as Exc 

divide :: Float -> Float -> Float 
divide x 0 = error "Division by 0." 
divide x y = x/y 

main :: IO() 
main = Exc.catch (print $ divide 5 0) handler 
    where 
     handler :: Exc.ErrorCall -> IO() 
     handler _ = putStrLn $ "You divided by 0!" 
+0

我认为putStrLn中的'$'char可以被移除xD。但这应该是公认的答案,因为它更清洁 – dani24 2016-09-08 23:14:21