user677526
user677526

Reputation:

Specify list type for input

I'm learning Haskell and I've decided to to the H-99 problem set. Naturally, I've become stuck on the first problem!

I have the following code:

module Main where

getLast [] = []
getLast x = x !! ((length x) - 1)

main = do 
    putStrLn "Enter a list:"
    x <- readLn
    print (getLast x)

Compiling this code gives the following error:

h-1.hs:8:14:
    No instance for (Read a0) arising from a use of `readLn'
    The type variable `a0' is ambiguous
    Possible fix: add a type signature that fixes these type variable(s)
    Note: there are several potential instances:
      instance Read () -- Defined in `GHC.Read'
      instance (Read a, Read b) => Read (a, b) -- Defined in `GHC.Read'
      instance (Read a, Read b, Read c) => Read (a, b, c)
        -- Defined in `GHC.Read'
      ...plus 25 others
    In a stmt of a 'do' block: x <- readLn
    In the expression:
      do { putStrLn "Enter a list:";
           x <- readLn;
           print (getLast x) }
    In an equation for `main':
        main
          = do { putStrLn "Enter a list:";
                 x <- readLn;
                 print (getLast x) }

h-1.hs:9:9:
    No instance for (Show a0) arising from a use of `print'
    The type variable `a0' is ambiguous
    Possible fix: add a type signature that fixes these type variable(s)
    Note: there are several potential instances:
      instance Show Double -- Defined in `GHC.Float'
      instance Show Float -- Defined in `GHC.Float'
      instance (Integral a, Show a) => Show (GHC.Real.Ratio a)
        -- Defined in `GHC.Real'
      ...plus 26 others
    In a stmt of a 'do' block: print (getLast x)
    In the expression:
      do { putStrLn "Enter a list:";
           x <- readLn;
           print (getLast x) }
    In an equation for `main':
        main
          = do { putStrLn "Enter a list:";
                 x <- readLn;
                 print (getLast x) }

That's a large error, but it seems to me that Haskell isn't sure what the input type will be. That's fine, and completely understandable. However, as this is supposed to work on a list of generics, I'm not sure how to specify that type. I tried this:

    x :: [a] <- readLn

...as [a] is the type that Haskell returns for an empty list (found with :t []). This won't compile either.

As I'm a beginner, I know there's a lot I'm missing, but in a basic sense - how can I satisfy Haskell's type system with input code? I'm a Haskell beginner looking for a beginner answer, if that's at all possible. (Also, note that I know there's a better way to do this problem (reverse, head) but this is the way I came up with first, and I'd like to see if I can make it work.)

Upvotes: 3

Views: 939

Answers (2)

Sibi
Sibi

Reputation: 48766

This should work:

getLast :: Num a => [a] -> a
getLast [] = 0
getLast x = x !! ((length x) - 1)

main = do 
  putStrLn "Enter a list:"
  x <- readLn :: IO [Int]
  print (getLast x)

why return 0 for an empty list, instead of an empty list?

Because it won't typecheck. Because you are returning [] for empty list and for other cases you are returning the element inside the list i.e a. Now since a type is not equal to list, it won't typecheck. A better design would be to catch this type of situation using the Maybe datatype.

Also, because of returning 0, the above function will work only for List of types which have Num instances created for them. You can alleviate that problem using error function.

However, this should work for a generic list, not just a list of Ints or Numbers, right?

Yes, it should work for a polymorphic list. And you can create a function like getLast which will work for all type of List. But when you want to get input from the user, it should know what type of input you are giving. Because the typechecker won't be able to know whether you meant it as List of Int or List of Double or so on.

Upvotes: 1

luqui
luqui

Reputation: 60543

You can't hope to write something like this which will detect the type of x at run time -- what kind of thing you're reading must be known at compile time. That's why @Sibi's answer uses [Int]. If it can't be deduced, you get a compile time error.

If you want a polymorphic read, you have to construct your own parser which lists the readable types.

maybeDo :: (Monad m) => Maybe a -> (a -> m b) -> m b
maybeDo f Nothing = return ()
maybeDo f (Just x) = f x

main = do
    str <- getLine
    maybeDo (maybeRead str :: Maybe Int) $ \i -> 
        putStrLn $ "Got an Int: " ++ show i
    maybeDo (maybeRead str :: Maybe String) $ \s ->
        putStrLn $ "Got a String: " ++ show s

There are lots of ways to factor out this repetition, but at some point you'll have to list all the types you'll accept.

(An easy way to see the problem is to define a new type MyInt which has the same Read instance as Int -- then how do we know whether read "42" should return an Int or a MyInt?)

Upvotes: 2

Related Questions