Reputation:
I'm reading http://learnyouahaskell.com/ ... And something surprised me:
Because of that,
main
always has a type signature ofmain :: IO something
, wheresomething
is some concrete type.
? So main
doesn't have to be of type IO()
, but rather can be IO(String)
or IO(Int)
? But what's the use of this?
I did some playing...
m@m-X555LJ:~$ cat wtf.hs
main :: IO Int
main = fmap (read :: String -> Int) getLine
m@m-X555LJ:~$ runhaskell wtf.hs
1
m@m-X555LJ:~$ echo $?
0
m@m-X555LJ:~$
Hmm. So my first hypothesis is disproven. I thought this was a way for a Haskell program to return exit status to the shell, much like a C program starts from int main()
and reports the exit status with return 0
or return 1
.
But nope: the above program consumes the 1
from input and then does nothing, and in particular doesn't seem to return this 1
to the shell.
One more test:
m@m-X555LJ:~$ cat wtf.hs
main = getContents
m@m-X555LJ:~$ runhaskell wtf.hs
m@m-X555LJ:~$
Wow. This time I tried returning IO String
. For reasons unknown to me, this time Haskell doesn't even wait for input, as it did when I was returning IO Int
. The program seems to simply do nothing.
This hints that the value is really not returned anywhere: apparently, since the results of getContents
are nowhere used, the whole instruction was skipped due to laziness. But if this was the case, why was returning IO Int
not skipped? Well yes: I did fmap read
on the IO
action; but same stuff seems to apply, computing the read
is only necessary if the result of the action is used, which - as the main = getContents
example seems to hint - is not used, so laziness should also skip the read
and hence also the getLine
, right? Well, wrong - but I'm confused why.
What's the use of returning IO Something
from main
rather than only IO ()
?
Upvotes: 4
Views: 101
Reputation: 15693
This is actually multiple questions, but in order:
main
doesn't have a meaning, that is why it can be ()
or anything else. It's not used at all.IO ()
are allowed for main is for convenience; Otherwise you'd always have to do something like main = void $ realMain
to discard results (you may well want to have an action that could return a result which you don't care about as the last thing that happens) which is a bit tedious. IMHO silently discarding things is bad and so I'd prefer if main
was forced to be :: IO ()
, but you can always get that effect by just supplying the type signature yourself so it's not really a problem in practice.System.Exit
fmap read getLine
consumes output and getContents
doesn't is because getContents
is lazy and getLine
isn't - i.e. getLine
does read a line of text where you'd think it does, whereas getContents
only does any actual IO if the result is "needed" in the Haskell world; Since the result of IO
isn't used for anything that means it doesn't do anything if getContents
is your whole main
.Upvotes: 5