Re: IO behaves oddly if used nested

2003-10-14 Thread Antti-Juhani Kaijanaho
On 20031004T181216+0100, Alastair Reid wrote:
 be careful to distinguish 'a value of type t' from 'a computation
 which returns a value of type t'i

I always found the idea of a computation as a value a little hard to
grasp.  Therefore, when I introduced monadic transput in my functional
programming course [1], I took the following approach: IO a is the type
of a program with an exit value of type a; return, fail, putChar and
getChar are primitive programs, and monadic transput is about combining
programs using (=).  The idea of denoting programs without executing
them seems natural (in contrast to denoting computations without
executing them).  Likewise, it seems natural that there is a special
mechanism for actually executing a program: either you give it to GHCi,
or you bind it to Main.main and compile the module (thus, the program
becomes synonymous with the expression that you bind to Main.main).

Up until now, nobody in my class seems to have had problems with this
mental model of monadic transput (of course, in grand tradition of
Finnish universities, a large subset of my students are not actually
attending sessions, so I wouldn't know about them until exam time).

[1] Advanced-level introduction to FP; students are advanced undergrads
or pre-master graduates.  They have a strong background in imperattive
programming, with the usual load of data structures and algorithms,
operating systems, automata and formal languages theory (and other stuff
depending on their tastes).

(Note that printing a program is also a natural idea, and likewise
natural is the idea that the output is unintelligible.)

-- 
Antti-Juhani Kaijanaho, FM (MSc), http://www.mit.jyu.fi/antkaij/
ohjelmistotekniikan assistentti* assistant in software engineering
Jyväskylän yliopisto   * University of Jyväskylä
Tietotekniikan laitos  * Dept. of Mathematical Inf. Tech.


signature.asc
Description: Digital signature
___
Haskell mailing list
[EMAIL PROTECTED]
http://www.haskell.org/mailman/listinfo/haskell


Re: IO behaves oddly if used nested

2003-10-06 Thread blaat blaat
[snip]

 Neither example is odd behavior, unless
you consider Hugs providing a perfectly reasonable instance of Show for
IO a odd.
True, every program behaves exactly as according to the definition of the 
run-time behavior of Haskell programs. (Hugs deviates a bit from ghc but ah 
well) The odd is not in the run-time behavior.

The odd is in the conceptual explanation. If I give a description of some f 
x = y function in Haskell I expect that some program f x is reduced to y and 
the result is given back (possibly printed). A good story to sell to 
students.

This is almost everywhere the case except for the IO monad. In the former 
example, the inner putStr is _not_ evaluated but a message is shown (hinting 
that a IO has some structure we can observe before evaluation). In the 
latter example, it is shown that we are not interested in a result of a IO 
program, but only in its (lazily generated) side-effects. The monad behaves 
oddly with respect to the f x = y behavior.

I think I observe the following reactions when I explain IO:

1* Can't the guy in front of me stop poking his nose? (the blank faces and 
parse errors)
2* Hmm, feels like Java, looks like Java, ahah! is Java! (coders)
3* Hmm, feels like math, looks like math, ahah! is math! (designers and 
thinkers)

Group 1 will mimic, group 2 will produce obfuscuated Java code in Haskell, 
group 3 has great difficulty with the IO monad (although they are the first 
group who will write OK haskell programs) because it behaves differently 
from what they expect. The usual questions of group 3:

* Why is an IO a evaluated if I am not interested in it's result? (opposite 
to the f x = y lazy behavior)
* Why is in the putStr hello world example Hello World not shown? 
(opposite to expected f x = y eval-first-then-show behavior)
* Why is in the IO (IO ()) example the inner IO () not evaluated? (somewhat 
opposite to expected f (f x) behavior - I personally wonder if it is even 
sound in a category theoretical setting)
* ...Lots of other questions...

Hmm, have to finish this email now - time constraints. I guess the short 
story just is: IO monads == sometimes bad conceptual story to sell. 
Especially so since the best group has the biggest problems and 
non-trivial examples are only explainable in the end in terms of run-time 
behavior. What was that remark again about pointy headed people and Haskell? 
;-)

For that I also gave the Nomad datatype as a possible solution (needs a 
pretty hefty type checker though). A nice (old) idea would be to represent 
IO as programs which are interpreted by some _outside_ RTS in a given 
manner, and leave the Haskell language clean.
(It might even be a good idea with respect to the compiler implementation 
since it removes checking against unsafe IO behavior from the compiler -- 
just a thought)

Bye now, and cheers,
 l4t3r
_
The new MSN 8: smart spam protection and 2 months FREE*  
http://join.msn.com/?page=features/junkmail

___
Haskell mailing list
[EMAIL PROTECTED]
http://www.haskell.org/mailman/listinfo/haskell


Re: IO behaves oddly if used nested

2003-10-04 Thread Alastair Reid

 The odd is in the conceptual explanation. If I give a description of some f
 x = y function in Haskell I expect that some program f x is reduced to y
 and the result is given back (possibly printed). A good story to sell to
 students.

This is true of IO as well.

The bit that's tripping up your students is the question of what an object of 
type 'IO Int' is.  It sound like they think it is the same as Int.  But this 
isn't so - no more than an object of type [Int] or 'Maybe Int' is the same as 
an Int.

It sounds like you're trying to make that distinction when you talk about the 
'Nomad' datatype but you have to be careful to keep on making that 
distinction every time you talk about monads and be careful to distinguish 'a 
value of type t' from 'a computation which returns a value of type t' or, at 
least, keep making that distinction until after your audience fully 
appreciates the distinction and can handle more informal explanations.

Once they get round the idea that 'IO Int /= Int', surely it is reasonable to 
accept that '1' and 'return 1' might be printed in different ways?

And if you've previously shown them that 'show id' produces a fairly 
uninformative result, maybe they can accept that 'show (do{c - getChar; 
putChar c})' produces a similarly uninformative result?

You might also try to warm students up to the issues by having them think 
about what (incorrectly typed) expressions like:

  ord getChar + ord getChar

or

  (getChar, getChar)

or

  let f x = do{ print x; return (x+1) }
  in drop 3 [ f i | i - [1..10] ]

might do under different evaluation orders.  Understanding the problem makes 
it clearer why we want 'IO Int' /= 'Int'.


 * Why is an IO a evaluated if I am not interested in it's result? (opposite
 to the f x = y lazy behavior)

You are interested in its result though.

If you recall my example of mailing an order for socks to a clothing company, 
evaluating a value of type 'IO Int' corresponds to opening the envelope 
containing the order.

Opening the envelope does not, in itself, lead to socks being sent.  For  
example, they may decide to discard the first 3 valid orders they receive 
each day or they may reject orders from people with a 'b' in their name.  It 
is only when they decide to execute the order than the socks get sent.


 * Why is in the putStr hello world example Hello World not shown?
 (opposite to expected f x = y eval-first-then-show behavior)

For the same reason that my feet get cold and ink-stained if I wear orders for 
socks instead of wearing socks.

The value 'putStr hello world' is a computation which will print hello 
world when you execute it.  Evaluating the computation is not the same as 
executing it.

 * Why is in the IO (IO ()) example the inner IO () not evaluated? (somewhat
 opposite to expected f (f x) behavior - I personally wonder if it is even
 sound in a category theoretical setting)

It _is_ evaluated.

But evaluating a value is not the same as executing it just as opening an 
order is not the same as obeying instructions in the order.


 A nice (old) idea would be to represent
 IO as programs which are interpreted by some _outside_ RTS in a given
 manner, and leave the Haskell language clean.
 (It might even be a good idea with respect to the compiler implementation
 since it removes checking against unsafe IO behavior from the compiler --
 just a thought)

Haskell used to do this prior to Haskell 1.3.
We moved away from it for two reasons:


1) It was notoriously hard to write correct code using the old interface 
because you had to reason carefully about the order of evaluation of pure 
Haskell code.

2) As specified, it gave us a fixed language in which to express commands and 
responses.  For example, there was no command to open a network connection or 
to draw a circle on a window and no response to say 'the result is this COM 
object'.  IMHO, Haskell would be an interesting but useless academic toy 
without the ability to do things like that.

The first might have been fixable (e.g., Clean provides a non-monadic 
alternative).

The second might also be fixable but I doubt that any useful solution would 
avoid the cleanliness and safety issues you refer to.  The sad truth is that 
as soon as you connect to a world that is not strongly typed (and uses a 
different type system anyway) and doesn't perform any runtime checks to 
compensate, you get corrupted by the a bunch of safety issues.  

--
Alastair Reid www.haskell-consulting.com

___
Haskell mailing list
[EMAIL PROTECTED]
http://www.haskell.org/mailman/listinfo/haskell


Re: IO behaves oddly if used nested

2003-10-03 Thread Alastair Reid

 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 Reidwww.haskell-consulting.com


___
Haskell mailing list
[EMAIL PROTECTED]
http://www.haskell.org/mailman/listinfo/haskell


RE: IO behaves oddly if used nested

2003-10-03 Thread Simon Marlow
Alastair Reid writes:
 ... Thus, we have Show instances for - and IO ...

Actually, you have to explicitly import Text.Show.Functions to get the
Show instance for (-).

Cheers,
Simon

___
Haskell mailing list
[EMAIL PROTECTED]
http://www.haskell.org/mailman/listinfo/haskell


Re: Feature-request (was: Re: IO behaves oddly if used nested)

2003-10-03 Thread ketil+haskell
Simon Marlow [EMAIL PROTECTED] writes:

 You don't need to know :-)

 It's actually in the base package, but GHCi knows about all hierarchical
 libraries without having to specify any extra command-line arguments.

Ai!  And here I've been doing :set -package all this time.

-kzm
-- 
If I haven't seen further, it is by standing in the footprints of giants
___
Haskell mailing list
[EMAIL PROTECTED]
http://www.haskell.org/mailman/listinfo/haskell


Re: IO behaves oddly if used nested

2003-10-02 Thread Alastair Reid

 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

___
Haskell mailing list
[EMAIL PROTECTED]
http://www.haskell.org/mailman/listinfo/haskell


Re: IO behaves oddly if used nested

2003-10-02 Thread Lennart Augustsson
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

___
Haskell mailing list
[EMAIL PROTECTED]
http://www.haskell.org/mailman/listinfo/haskell


Re: IO behaves oddly if used nested

2003-10-02 Thread Derek Elkins
On Thu, 02 Oct 2003 12:47:15 +0200
Lennart Augustsson [EMAIL PROTECTED] 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.

___
Haskell mailing list
[EMAIL PROTECTED]
http://www.haskell.org/mailman/listinfo/haskell


Re: IO behaves oddly if used nested

2003-10-02 Thread Lennart Augustsson
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

___
Haskell mailing list
[EMAIL PROTECTED]
http://www.haskell.org/mailman/listinfo/haskell


Re: IO behaves oddly if used nested

2003-10-02 Thread ajb
G'day all.

Alastair Reid [EMAIL PROTECTED] wrote:

 I think it is exactly right.

I think it's wrong.  The return type of IO should be discarded.

Even if it isn't, it doesn't make sense for IO to be in Show.

Cheers,
Andrew Bromage
___
Haskell mailing list
[EMAIL PROTECTED]
http://www.haskell.org/mailman/listinfo/haskell