IO behaves oddly if used nested
Hi, the interact remark triggered me to post this remark on nested use of IO. (I posted something similar some month ago on haskell-cafe/I read the haskell-archives ones so often) What about this program: main :: IO () main = putStr (show (putStr "Hello World!")) Am I the only one who feels that there is some conceptual _wrongness_ about Hugs responding with <<IO action>>? Another question with a trivial answer, what is the result of: main :: IO (IO ()) main = return (putStr "Hello World!") Clearly it also shows the relation between IO and chosen evaluation strategy. Cheers, l4t3r Some other random thoughts:.... Hmm,... there was also some question on the use of existential types, what about the following one (might be used to implement IO trivially in a _pure*_ manner! *with respect to rewrite strategy): data Nomad a = Return a | forall b . Bind (Nomad b) (b -> Nomad a) I would like to see Haskell extended with subtyping on algebraic datatypes (or is this implemented already?) Some of my programs would really benefit from such a feature. I am a teacher, I use haskell to test random ideas from combinatorics, SAT/EDA verification, control theory, real-time processes, and compiler/verification-language implementation. In short, I use Haskell as a vehicle to test short algorithms. Oh yeah, I propose to move the link on the Haskell site on "Monads explained by the Catholic Church" from the "Humor" section to the "Learning Haskell" section since they seem to be the only ones who actually clearly understand what is up with monadic IO. ;-p (actually, that is a dead-serious proposition) Hmm, long post, cheers again, l4t3r _________________________________________________________________ The new MSN 8: smart spam protection and 2 months FREE* http://join.msn.com/?page=features/junkmail
What about this program:
main :: IO () main = putStr (show (putStr "Hello World!"))
Am I the only one who feels that there is some conceptual _wrongness_ about Hugs responding with <<IO action>>?
I think it is exactly right. Having it print "Hello World" would clearly be wrong since it confuses values (like strings) with computations that produce strings. One way to understand the difference is to think of the difference between you sending an order to a shop for them to send you a pair of socks and them actually sending you the socks. The order is a computation which you can photocopy, file in your in tray, lose, etc. The sending of the socks is an action described by the order. What would you like to see in this case? In the sock example, I might like to see: <<Order for 1 pair red socks from blaat blaat>> But monads are often opaque datatypes so the best I might be able to get is: <<Order for 0 or more things>> (Monads are often opaque datatypes because they often involve function types and function types are opaque.)
Another question with a trivial answer, what is the result of:
main :: IO (IO ()) main = return (putStr "Hello World!")
It is a computation which, if executed, will print "Hello World"
Clearly it also shows the relation between IO and chosen evaluation strategy.
This isn't clear to me at all - can you explain further? -- Alastair
Alastair Reid wrote:
Another question with a trivial answer, what is the result of:
main :: IO (IO ()) main = return (putStr "Hello World!")
It is a computation which, if executed, will print "Hello World"
Clearly it also shows the relation between IO and chosen evaluation strategy.
This isn't clear to me at all - can you explain further? Is it even type correct with main :: IO (IO ()) If it is, it shouldn't be. It makes no sense. The value computed by the top level IO action should have some consumer. Sensible types for the consumer (which in some sense is the OS) are () or some exit code.
-- Lennart
On Thu, 02 Oct 2003 12:47:15 +0200 Lennart Augustsson <lennart@augustsson.net> wrote:
Alastair Reid wrote:
Another question with a trivial answer, what is the result of:
main :: IO (IO ()) main = return (putStr "Hello World!")
It is a computation which, if executed, will print "Hello World"
Clearly it also shows the relation between IO and chosen evaluation strategy.
This isn't clear to me at all - can you explain further? Is it even type correct with main :: IO (IO ()) If it is, it shouldn't be. It makes no sense. The value computed by the top level IO action should have some consumer. Sensible types for the consumer (which in some sense is the OS) are () or some exit code.
If I'm not mistaken, the Report restricts main's type to be, at least, IO a. Anyways, it's perfectly sensible to return anything. The RTS simply discards it. The above example as an entire program is an IO action that returns an IO action that is discarded by the RTS. I.e. the program doesn't do anything. Neither example is odd behavior, unless you consider Hugs providing a perfectly reasonable instance of Show for IO a odd.
Derek Elkins wrote:
If I'm not mistaken, the Report restricts main's type to be, at least, IO a. Anyways, it's perfectly sensible to return anything. The RTS simply discards it. The above example as an entire program is an IO action that returns an IO action that is discarded by the RTS. You're right, the report says that the value of the IO is discarded (nd it can be of any type. While I don't think this is the best choice of type for main, it does make the described behaviour perfectly sensible.
-- Lennart
I think it's wrong. The return type of IO should be discarded.
I don't follow. I thought the question was 'what should this print?' not 'what is its type?'
Even if it isn't, it doesn't make sense for IO to be in Show.
The general policy for Haskell 98 libraries is that if you define a type, you should define a Show instance even if it isn't possible to provide much information. Thus, we have Show instances for -> and IO and, in the Haskell extension libraries, we have Show instances for IORef, MVar, etc. It's a bit of a delicate balance. On the one hand, it lets us derive or write a Show instance for any new type constructor that the user might define or any type that might occur in their program. This makes teaching and some forms of debugging easier. On the other hand, if you're not using it for debugging purposes, you might like the typechecker to detect that you're doing something silly just as it would if you wrote 'foo == (\x -> x)' or 'sin "1"'. But it isn't quite as bad as equality on functions would be since such a test would either fail to terminate in many cases or would have to return an incorrect answer. Overall, I think the balance is about right. Providing Show instances for IO doesn't lead to runtime problems and it doesn't break the desirable 'read . show = id' property since there is no Read instance. Of course, it might be nice if people could choose for themselves whether they get Show instances for IO and ->. The simplest way to do this would be to not provide instances in Prelude and have them explicitly import the module if they need it. Haskell compilers could perhaps implicitly import this module if the compiler is in 'Haskell 98' mode. -- Alastair Reid www.haskell-consulting.com
participants (5)
-
ajb@spamcop.net -
Alastair Reid -
blaat blaat -
Derek Elkins -
Lennart Augustsson