我想知道如何返回一个非常简单的字符串异常。我写了一个功能" 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"
答案 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
)。否则,我们会返回包含答案的Right
(Right :: 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"