1
votes

I'm currently trying to implement linked lists in Haskell. So far everything works, including functions for working with these linked lists, but my issue is in the Show instance. My code:

data LinkedList a = EmptyList | Node a (LinkedList a)

instance (Show a) => Show (LinkedList a) where
    show EmptyList = "{ }"
    show l = "{ " ++ showLL l ++ " }"
        where
            showLL (Node x EmptyList) = show x
            showLL (Node x l) = show x ++ " -> " ++ showLL l

If I remove the (Show a) constraint, Haskell freaks out because there's no guarantee that a is showable. But when I try print EmptyList, the constraint cannot be solved since EmptyList doesn't have a type a which is or isn't showable.
Trying to declare two instances, one with the constraint and one without, gives me the duplicate instance declaration error. Trying to declare it as an instance of LinkedList instead of (LinkedList a) also gives me an error since it expects an argument to be passed to LinkedList (i.e. a).

Here is the weirdest part: declaring an instance for Functor prevents the error from showing up:

instance Functor LinkedList where
    fmap _ EmptyList = EmptyList
    fmap f (Node x l) = Node (f x) (fmap f l)

Note that fmap _ EmptyList = EmptyList, meaning that regardless of what function I want to map onto an EmptyList the result will just be an EmptyList.
The part that is confusing to me:

print $ EmptyList           -- error
print $ fmap id EmptyList   -- error
print $ fmap (*2) EmptyList -- no error! output is "{ }" as expected

So how can I print both a list that is empty and one that is not? And why on Earth does mapping any function that isn't id (or anything equivalent like \x -> x) not give the same error? Does the fmap function somehow return an EmptyList that isn't the same as the regular EmptyList?

Thanks!

(For the record, I'm using GHCi v8.6.5)

1

1 Answers

1
votes

It is unclear what the a of your LinkedList should be when you use EmptyList. For fmap (*2) that is not a problem since then the type constraints will determine that a has to be a Num type and this will default to Integer, but without such type hint, a can be anything.

You can give it a typhint, for example:

print (EmptyList :: LinkedList Int)

This then produces the expected:

Prelude> print (EmptyList :: LinkedList Int)
{ }

This might be important, since printing an EmptyList :: LinkedList Int might be different from printing EmptyList :: LinkedList Char for example.