6
votes

Just for learning I did this on my console WinGHCi:

let ls = [putChar 'x',putChar 'y']

then if do:

head ls

the output is obviusly x (obviusly in the sense that I undestand why)

else if I do:

tail ls

I get this error:

No instance for (Show (IO ())) arising from a use of ‘print’

In a stmt of an interactive GHCi command: print it

Why? Shouldn't output y or [putChar 'y']?

4
well in the first case GHCi knows to execute the IO action - in the second case it get's a list of IO actions (which are not an instance of Show) - you can use sequence_ $ tail ls to get something similarRandom Dev

4 Answers

15
votes

This behaviour is explained here.

head ls has type IO () so GCHi executes the action and doesn't print the result ().

In contrast, tail ls has type [IO ()]. Since this is not an IO action, GCHi attempts to display it using print which has type:

print :: Show a => a -> IO ()

however since there is no Show instance for [IO ()] you get the error. If you want to evaluate the actions you can use sequence_:

sequence_ (tail ls)
7
votes

then if do: head ls the output is obviusly x

well, actually not obviously! In fact, head ls is not something directly related to the character 'x'. First of all, it's simply an IO action, and GHCi assumes that this action does something you want to happen: it has a special treatment that for an IO action it tries not to show the action, but executes it. Well, good that you didn't consider instead the list [launchMissiles, putStrLn "oops"]...

Now, executing the action does indeed result in the character 'x' being printed to the console, which just happens to be also your viewport, so you see it as if it was just the value of putChar 'x'. But it's not!

OTOH, tail ls is from the outside just a list, so ghci doesn't assume you want anything executed. It does try to show the list, but you can't show an IO action or anything with IO actions in it; that's what the error message says.

5
votes

When you have

> let ls = [putChar 'x', putChar 'y']

you aren't executing the actions stored in that list, you're just constructing those actions, if that makes sense. You could have something like

printHelp :: IO ()
printHelp = putStrLn "You need to pass XYZ options to this program"

defined in a file. This doesn't immediately print out the message, but instead constructs an action that, once executed, prints that message to the console. Execution happens in one of two places: in the main function and in GHCi.

It looks like you're using GHCi to execute code, which is awesome for exploring different functions, but is a bit different than executing code in a file. Whenever you evaluate an IO Something action by itself in GHCi, it runs that IO action. Whenever you evaluate any other value in GHCi it inserts print in front of that line. So when you do

> head ls

This has type IO (), and so the action putChar 'x' is executed, resulting in x being printed out to the console. When you do

> tail ls

This has type [IO ()], so GHCi tries to put a print in front of it, making it equivalent to

> print (tail ls)

The print function has type

print :: (Show a) => a -> IO ()

We've passed a list to print here, and there is an instance of Show for lists, but only when the elements of the list have an instance of Show. So the compiler tries to see if IO () has an instance for Show. Unfortunately, it does not, since IO is a pretty complex type that represents that pretty much anything can happen. So the compiler shows an error that IO () doesn't have an instance for Show.

If you want to instead run every action in a list in order, there is a function for that: Control.Monad.sequence (or Control.Monad.sequence_ if you don't care about the result). This function actually works on all Monads, not just IO:

> sequence_ (tail ls)
y> sequence_ ls
xy>
0
votes

It's not just tail ls, if you do ls or init ls , you will get the same error.

The type of ls is:

ls :: [IO ()]

When you type something in GHCi, it will evaluate the expression and print the result.

In the second case (ls), from the type of ls, the returned value is a list of actions and GHC does not know how to show an action of IO ().

In the first case, GHCi evaluates the IO (), which prints x. There is no returned values for IO (), so there is nothing to show and no error.