Reputation: 887
I would like to know how I can returns a very simple string-exception. I have written a function "powered" that takes an integer, n, and return 2^(n). Here is the code:
powered::Int->Int
powered n
| n==1 =2
| otherwise =iter n double 1
where 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
and double:
double::Int->Int
double n = n*2
This code works fine for all Natural numbers. I, however, would like to make it that if I pass a negative integer to it, it returns a string exception that says: "Incorrect input". How can I make it to do that. Here is pseudo-code for what I'd like to accomplish:
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"
Upvotes: 2
Views: 2492
Reputation: 44634
Haskell's exception system is deliberately under-powered. You can't catch exceptions in pure code, so exception handling can only happen at a very coarse-grained level, inside the IO
monad. It's pretty difficult - though possible - to stop an exception from crashing your program altogether. (Imagine if you could only write catch
in the main
method of an imperative program!) We therefore avoid throwing exceptions where possible; there's a much better alternative.
The "right way" to do exception-style programming in Haskell is to leverage the type system. Here I'm using Either
to represent the possibility of the computation failing.
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
If we couldn't compute the answer, we return a Left
value (Left :: a -> Either a b
) containing a String
error message. Otherwise we return a Right
(Right :: b -> Either a b
) containing the answer.
The compiler forces the caller of powered
to inspect the return value to figure out if the computation failed. You simply can't get at the result of the computation without also handling or propagating the possible error.
We can go one step further. We can encode the fact that powered
expects a positive integer into the type signature itself. If we structure our code right, the compiler will make sure that no one tries to call it with a negative integer.
-- 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
is a type which wraps an Int
. As a client of the Data.Natural
module, there's only one way to make a Natural
: by calling the mkNatural
"smart constructor", and you'll see that mkNatural
fails when its argument is not a natural number. So it's impossible to make a Natural
without a positive integer. We also provide the opposite method, toInt :: Natural -> Int
, to extract the underlying Int
from a Natural
.
Now we can write the following type signature for powered
, which makes it impossible to call the function with invalid input:
powered :: Natural -> Natural
This is way more expressive: the type signature clearly states that powered
is an operation over the natural numbers which returns a new natural number. (I'll leave it as an exercise for you to implement powered
with this type.) By separating the concerns of input validation into a new type, we've ended up with cleaner code.
Upvotes: 10
Reputation: 76280
The simplest way is via error
:
error "Incorrect input"
The GHC implementation raises an errorCallException
exception:
error :: [Char] -> a
error s = raise# (errorCallException s)
with the corresponding string.
An other alternative would be to use an assertion:
assert (n >= 0) (iter n double 1)
This won't allow you to specify an error message, but it will automatically provider file and line number of the assertion that failed.
And finally you can use a custom exception as follows (using Control.Exception
):
data PoweredError = PoweredError String deriving (Eq, Show, Typeable)
instance Exception MyException
-- ...
and then:
throw (PoweredError "Incorrect input")
Upvotes: 2
Reputation: 64740
Just use error
to throw an IOError:
| otherwise = error "Incorrect input"
This is the canonical way to throw an error in Haskell. While atypical, you can throw errors of an arbitrary type such as string by first declaring the type an instance of the Exception class:
{-# 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"
Upvotes: 1