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

Rum*_*tov 2 haskell

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

powered::Int->Int
powered n
 | n==1         =2
 | otherwise    =iter n double 1
Run Code Online (Sandbox Code Playgroud)

iter:

iter::Int->(Int->Int)->Int->Int
iter n f x
 | n==1         =f x
 | n>1          =iter (n-1) f (f x)
 | otherwise    =x
Run Code Online (Sandbox Code Playgroud)

和双:

double::Int->Int
double n = n*2
Run Code Online (Sandbox Code Playgroud)

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

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"
Run Code Online (Sandbox Code Playgroud)

Ben*_*son 10

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

在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
Run Code Online (Sandbox Code Playgroud)

如果我们无法计算答案,则返回包含错误消息的Leftvalue(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
Run Code Online (Sandbox Code Playgroud)

Natural是一种包装的类型Int.作为Data.Natural模块的客户端,只有一种方法可以Natural:通过调用mkNatural"智能构造函数",mkNatural当它的参数不是自然数时,你会看到失败.所以Natural没有正整数就不可能.我们还提供了相反的方法,toInt :: Natural -> Int以提取底层IntNatural.

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

powered :: Natural -> Natural
Run Code Online (Sandbox Code Playgroud)

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