Alex
Alex

Reputation: 667

haskell : How to report an error for my function

this is an expansion to my last question here: basic haskell : Copying elements

however when an invalid input is added then I want it to print out an error message saying "negative value" or something similar. Is this possible in haskell? working code:

copy :: Int->a->[a]
copy 0 _ = [] 
copy y a = [a]++(copy (y-1) a)

final line:

copy b c  = error "negative value"

Upvotes: 1

Views: 381

Answers (3)

daniel gratzer
daniel gratzer

Reputation: 53881

Because partial functions make me sad, I'd suggest doing something more along the lines of

copy :: Int -> a -> Maybe [a]
copy 0 _ = Just []
copy n a | n < 0 = Nothing
         | otherwise = fmap (a:) (copy (n-1) a)

We've swapped out that if for a "guard"

foo bar | baz = quux
        | ...

is just

 foo bar = if baz then quux else ...

Note that I also changed your code a little,

 [a] ++ copy (y-1) a ====> fmap (a:)  (copy (y-1) a)

You can think of (:) as append.

 1 : [2, 3] ==> [1, 2, 3]

It's the preferred alternative to [1] ++ [2, 3]. Say it out loud as "cons", like "construct". We can write this with an operator section

(a:) ==> \x -> a : x

Next we use this wonky fmap function. Think of fmap like this

fmap f Nothing = Nothing
fmap f (Just x) = Just (f x)

So it unwraps a Just and applies a function before rewrapping the result. So our final code returns Nothing if our number is negative, otherwise, just the list.

Why aren't I recommending error? Well because error will blow up your whole program with pretty minimal information and it's a bad idea to try to catch it. Haskell doesn't even mandate that it's possible to do so, GHC just implements error in such a way that it's possible. In other words, you have little chance to recover.

This isn't a big deal for 10 lines of code, but I've spent upwards of 6 hours searching for the offending call to a function using error. It's much faster to debug and more idiomatic haskell.

Upvotes: 6

bheklilr
bheklilr

Reputation: 54058

You can do this with guards

copy :: Int -> a -> [a]
copy n x
    | n < 0 = error "negative value"
    | n == 0 = []
    | otherwise = x : copy (n - 1) x

However, if this fails then it will likely crash your program. A better way is to use the Maybe type:

copySafe :: Int -> a -> Maybe [a]
copySafe n x 
    | n < 0 = Nothing
    | otherwise = Just (copy n x)

Then you can use it as

main = do
    putStrLn "Enter a number:"
    nStr <- getLine
    let n = read nStr :: Int
        maybeXs = copySafe n n
    case maybeXs of
        Nothing -> putStrLn "You entered a negative number!"
        Just xs -> print xs

This style forces you to consider both cases of copySafe, either it can fail on a negative value or it can return a valid list. It doesn't crash your program and the error handling is enforced by the type system.

Upvotes: 1

user2840552
user2840552

Reputation: 23

look at http://www.haskell.org/haskellwiki/Error_vs._Exception

for example

copy b c = if c > b then error "negativ value"

Upvotes: -6

Related Questions