Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Passing a non-empty list to a function

I have written this small function to retrieve the tail of a list:

let getTail l = if length l > 0 then tail l else "empty list"

Passing []to getTail returns empty list but passing [1,2,3] gives the following error:

<interactive>:1:14:
No instance for (Num Char)
  arising from the literal `3'
Possible fix: add an instance declaration for (Num Char)
In the expression: 3
In the first argument of `getTail', namely `[1, 2, 3]'
In the expression: getTail [1, 2, 3]

I can't understand what that error means. What is the problem? Using GHCi 7.0.4

like image 896
badmaash Avatar asked Feb 03 '26 12:02

badmaash


2 Answers

Let's think about the type of your getTail function. Initially, we can imagine l to be any list. The return type, however, has to be a String because you sometimes return "empty list". Since that's part of an if statement where you might also return tail l, it means l has to be a String.

So your getTail function has type String -> String.

When you call getTail with [1,2,3], it expects a String, which is just a [Char]. Remember that numeric literals are overloaded: [1,2,3] is equivalent to calling [fromInteger 1, fromInteger 2, fromInteger 3]. So, given this list, Haskell is trying to get a number from a Char. Since Chars are not part of the Num class, this fails, giving you your error.

A good solution would be to return a Maybe type and give Nothing for the error rather than returning "empty list". So rewrite your function as:

let getTail l = if length l > 0 then Just (tail l) else Nothing

Now its type is [a] -> Maybe [a]. Using this will force anybody using your function to check whether getTail succeeded before being able to use the result.

Another option is to return [] in place of "empty list". In this case, your getTail function will work exactly like drop 1.

like image 85
Tikhon Jelvis Avatar answered Feb 05 '26 03:02

Tikhon Jelvis


Prelude> let getTail l = if length l > 0 then tail l else "empty list"

You didn't give getTail a type signature, so one is inferred.

Prelude> :t getTail
getTail :: [Char] -> [Char]

:t gives you the type of anything in GHCi. This is extremely useful. Type signatures say a lot about a function in Haskell.

This is probably not what you meant. Why does it only work on [Char], (or equivalently, String), and not on any list?

Here's why:

A function can only have one return type. Since you return "empty list" when the list is empty, the return type must be String. It just so happens that the return type of tail xs where xs is a [Char] is also a String, which is why you don't get an error when you first define the function. Therefore your function as it is must be [Char] -> [Char].

You're trying to call it with the argument [1, 2, 3], which has a type of Num a => a. GHC is telling you "A Char doesn't have an instance of Num", or "I can't turn a number into a Char." This involves typeclasses—if you don't understand those yet, it doesn't matter, because you would still get an error of you tried getTail ([1, 2, 3] :: [Int]). You're trying to pass a list of Ints to a function that takes a list of Chars.

I would recommend reading through LYAH to understand the Haskell type system.

like image 32
mk12 Avatar answered Feb 05 '26 04:02

mk12



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!