2014-10-26 5 views
1

Я хотел бы знать, как я могу вернуть очень простое исключение строки. Я написал функцию «питание», которая принимает целое число, n и возвращает 2^(n). Вот код:Как я могу вернуть строковое исключение в Haskell?

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" 

ответ

1

Самый простой способ с помощью 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") 
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"  
8

система исключения в Haskell намеренно под напряжением. Вы не можете перехватывать исключения в чистом коде, поэтому обработка исключений может происходить только на очень крупнозернистом уровне внутри монады IO. Это довольно сложно - хотя это возможно - остановить исключение от сбоя вашей программы в целом. (Представьте себе, что вы можете написать только 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 с этим типом.) К отделяя проблемы от ввода проверки в новый тип, мы закончили с более чистого кода.

Смежные вопросы