如何在Haskell中返回字符串异常?

时间:2014-10-26 23:39:37

标签: haskell

我想知道如何返回一个非常简单的字符串异常。我写了一个功能" powered"取整数n,并返回2 ^(n)。这是代码:

powered::Int->Int
powered n
 | n==1         =2
 | otherwise    =iter n double 1

在哪里:

iter::Int->(Int->Int)->Int->Int
iter n f x
 | n==1         =f x
 | n>1          =iter (n-1) f (f x)
 | otherwise    =x

并加倍:

double::Int->Int
double n = n*2

此代码适用于所有自然数字。但是,我想说,如果我向它传递一个负整数,它会返回一个字符串异常,表示:"输入错误"。我怎样才能做到这一点。这是我想要完成的伪代码:

powered::Int->Int
powered n
 | n==0         =1
 | n==1         =2
 | n>1          =iter n double 1
 | otherwise    ="Incorrect input"

main = do
print(powered (-1)) ~> "Incorrect input"

3 个答案:

答案 0 :(得分:9)

Haskell的异常系统故意不足。您无法在纯代码中捕获异常,因此异常处理只能在IO monad内的非常粗粒度级别进行。很难 - 尽管可能 - 阻止异常完全崩溃您的程序。 (想象一下,如果你只能在命令式程序的catch方法中编写main!)因此我们避免在可能的情况下抛出异常;有一个更好的选择。

在Haskell中进行异常风格编程的“正确方法”是利用类型系统。在这里,我使用Either来表示计算失败的可能性。

powered :: Int -> Either String Int
powered n
    | n <= 0       = Left "Incorrect input"
    | n==1         = Right 2  -- Right means "the right answer"
    | otherwise    = Right $ iter n double 1

如果我们无法计算答案,我们会返回包含Left错误消息的Left :: a -> Either a b值(String)。否则,我们会返回包含答案的RightRight :: b -> Either a b)。

编译器强制powered的调用者检查返回值以确定计算是否失败。如果不处理或传播可能的错误,你根本无法得到计算结果。


我们可以更进一步。我们可以编码powered期望正整数进入类型签名本身的事实。如果我们正确构造代码,编译器将确保没有人试图用负整数调用它。

-- file Natural.hs

-- don't export the 'Natural' value constructor: 'mkNatural' acts as our "gatekeeper"
module Data.Natural (Natural, toInt, mkNatural) where

newtype Natural = Natural {toInt :: Int} deriving (Eq, Show)

mkNatural :: Int -> Either String Natural
mkNatural x
    | x <= 0 = Left "Must be greater than 0"
    | otherwise = Right $ Natural x

Natural是一种包裹Int的类型。作为Data.Natural模块的客户,只有一种方法可以通过调用Natural“智能构造函数”来生成mkNatural,并且您会看到mkNatural失败当它的论证不是自然数。因此,如果没有正整数,则无法生成Natural。我们还提供了相反的方法toInt :: Natural -> Int,以便从Int中提取基础Natural

现在我们可以为powered编写以下类型签名,这使得不可能使用无效输入调用该函数:

powered :: Natural -> Natural

这更具表现力:类型签名清楚地表明powered是对返回新自然数的自然数的操作。 (我将把它留作练习,以便用这种类型实现powered。)通过将输入验证的关注点分成新类型,我们最终得到了更清晰的代码

答案 1 :(得分:2)

最简单的方法是通过error

error "Incorrect input"

GHC实施引发errorCallException例外:

error :: [Char] -> a
error s = raise# (errorCallException s)

带有相应的字符串。

另一种选择是使用断言:

assert (n >= 0) (iter n double 1)

这不允许您指定错误消息,但它会自动提供失败的断言的文件和行号。

最后,您可以使用以下自定义异常(使用Control.Exception):

data PoweredError = PoweredError String deriving (Eq, Show, Typeable)

instance Exception MyException
    -- ...

然后:

throw (PoweredError "Incorrect input")

答案 2 :(得分:1)

只需使用error抛出IOError:

| otherwise    = error "Incorrect input"

这是在Haskell中抛出错误的规范方法。虽然是非典型的,但您可以通过首先声明类型是Exception类的实例来抛出任意类型的错误,例如字符串:

{-# LANGUAGE ScopedTypeVariables #-} 
{-# LANGUAGE FlexibleInstances #-}
import Control.Exception

instance Exception [Char] -- String can now be used as an Exception

main :: IO ()
main = catch (print foo)
             (\(e :: String) -> putStrLn $ "Caught: " ++ e)

-- foo is a computation that might throw an error
foo :: String                                               
foo = throw "foo"