clarification on IO

Previous Topic Next Topic
 
classic Classic list List threaded Threaded
15 messages Options
Reply | Threaded
Open this post in threaded view
|

clarification on IO

Michael Easter
Folks,

I've been reading RWH and this
http://www.haskell.org/haskellwiki/IO_inside#Haskell_is_a_pure_language

I think I understand monads. I think I understand how IO is much different
from, e.g. Maybe, State, etc.

However there are some turns of phrase with respect to IO that have me
baffled.  I am giving a presentation
on monads next week, and hope you can help. (I'm not quite as lost as this
might imply!)

Q1: The web page mentions that normal Haskell functions cannot cause
side-effects, yet later talks about
side-effects with putStrLn. I assume the key point here that IO actions are,
by definition, _not_ normal functions?


Q2: Is it true to say that *any* monadic action *could *cause side-effects,
depending on the design of that
monad? i.e. Does one generalize from the IO monad to (possibly) an arbitrary
monad? *Musing* This must be true as
using State must surely be considered a side-effect.


Q3: The web page mentions IO as being a baton, or token, that is used to
thread/order the actions. Is true
that this is merely one simple perspective, with respect to order of
evaluation? This is hard to articulate,
but it seems to me that "in the IO monad" there is a large subsystem of
(inaccessible) state, machinery, etc.
Is it really a token?

Q4: Is the following idea accurate: a Haskell program is partitioned into 2
spaces. One is a sequence
of IO actions; the other is a space of pure functions and 'normal' Haskell
operations.  The execution of a
program begins with the main :: IO () action and, effectively, crosses from
one space to the other. In the
pure space, the math-like functions can be highly optimized but only insofar
as they do not disrupt the
implied order of the IO actions.  Because of the type system, the program
recognizes when it enters
"back" into the IO space and follows different, less optimized rules.

My concern is that the above is *not* accurate, but I don't know why.


thanks so much for your help
Michael Easter

--
----------------------
Michael Easter
http://codetojoy.blogspot.com: Putting the thrill back in blog

http://youtube.com/ocitv -> Fun people doing serious software engineering
-------------- next part --------------
An HTML attachment was scrubbed...
URL: http://www.haskell.org/pipermail/beginners/attachments/20090227/9c250695/attachment.htm
Reply | Threaded
Open this post in threaded view
|

clarification on IO

Andrew Wagner
I'll take a shot at answering some of your questions by explaining how I
understand it, and we'll see if it helps or makes it worse.

Let's talk about monads first. Monads can be thought of as a way of sort of
hiding "side effects". That is, there is nothing inherently impure about
monads. The "side effects" happen in the bind function, essentiall. For
example, in the case of state, the state is carried from one function to
another. The bind function actually says how to do this; You just don't
usually see it because it's "hidden" in the do notation. In the Maybe
function, the plumbing hides the fact that we quit at any point the
computation fails, and so on. So while in an impure language, any statement
can have any side effect, in haskell, if you know what monad you're in, you
know exactly what the "side effect" will occur - which means, it's not
really a side effect at all, but part of the actual intended effect.

Now for IO. You can think of IO as being essentially State RealWorld. That
is, every operation is dependent on the entire state of the world, including
what you're thinking, and what kind of bug is crawling on the 18th blade of
grass in your yard. If we could actually represent the whole world this way,
Haskell would truly be a completely pure language. The only reason IO, and
thus Haskell, is impure at all, is because we can't literally represent the
whole world. EVERYTHING ELSE, INCLUDING EVERY OTHER TYPE OF MONADIC ACTION
IN HASKELL, is completely pure.

Ok, so let's address your questions a little more specifically.

Q1: The web page mentions that normal Haskell functions cannot cause
> side-effects, yet later talks about
> side-effects with putStrLn. I assume the key point here that IO actions
> are, by definition, _not_ normal functions?
>

Right, IO actions can have side effects because they can take into account,
and modify, the RealWorld.


> Q2: Is it true to say that *any* monadic action *could *cause
> side-effects, depending on the design of that
> monad? i.e. Does one generalize from the IO monad to (possibly) an
> arbitrary monad? *Musing* This must be true as
> using State must surely be considered a side-effect.
>

Again, yes, this is accurate, but it's different from most impure languages
in that the side effect is completely baked in by the monad you're in, so
that you can't really say that the effect is a "side effect" at all.


> Q3: The web page mentions IO as being a baton, or token, that is used to
> thread/order the actions. Is true
> that this is merely one simple perspective, with respect to order of
> evaluation? This is hard to articulate,
> but it seems to me that "in the IO monad" there is a large subsystem of
> (inaccessible) state, machinery, etc.
> Is it really a token?
>

Again, in many ways, it's easier to think of the IO monad as a state monad.
In that sense, the state of the world is indeed being passed from one action
to the next, as defined by bind. That's the inaccessible state machinery I
suspect you're sensing.


> Q4: Is the following idea accurate: a Haskell program is partitioned into 2
> spaces. One is a sequence
> of IO actions; the other is a space of pure functions and 'normal' Haskell
> operations.  The execution of a
> program begins with the main :: IO () action and, effectively, crosses from
> one space to the other. In the
> pure space, the math-like functions can be highly optimized but only
> insofar as they do not disrupt the
> implied order of the IO actions.  Because of the type system, the program
> recognizes when it enters
> "back" into the IO space and follows different, less optimized rules.
>

I think it would be easier to talk about haskell in terms of pure and impure
code. All Haskell code is pure except for IO. Pure functions are easier to
reason about and to optimize, because you don't have to take into account
the RealWorld state, or other possible, REAL side effects..


> My concern is that the above is *not* accurate, but I don't know why.
>
>
> thanks so much for your help
> Michael Easter
>
> --
> ----------------------
> Michael Easter
> http://codetojoy.blogspot.com: Putting the thrill back in blog
>
> http://youtube.com/ocitv -> Fun people doing serious software engineering
>
> _______________________________________________
> Beginners mailing list
> [hidden email]
> http://www.haskell.org/mailman/listinfo/beginners
>
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: http://www.haskell.org/pipermail/beginners/attachments/20090227/15ec3af9/attachment-0001.htm
Reply | Threaded
Open this post in threaded view
|

clarification on IO

Michael Easter
Thanks Andrew, this is really great...

My main revelation here is that a "side-effect" in other monads is still
pure.  e.g. The Logger example in RWH builds up a list of log strings
"behind the scenes" but this is much different than writing to disk, or
launching missiles, to quote SP Jones.

'Revelation' is a good word.... The tectonic plates are coming together! I
needed this.

thanks!
Mike

ps. re: bind/hidden. Monads strike me as a glorious instance of
encapsulation that is not OO, which appropriated the term in the 1990s.

On Fri, Feb 27, 2009 at 9:30 PM, Andrew Wagner <[hidden email]>wrote:

> I'll take a shot at answering some of your questions by explaining how I
> understand it, and we'll see if it helps or makes it worse.
>
> Let's talk about monads first. Monads can be thought of as a way of sort of
> hiding "side effects". That is, there is nothing inherently impure about
> monads. The "side effects" happen in the bind function, essentiall. For
> example, in the case of state, the state is carried from one function to
> another. The bind function actually says how to do this; You just don't
> usually see it because it's "hidden" in the do notation. In the Maybe
> function, the plumbing hides the fact that we quit at any point the
> computation fails, and so on. So while in an impure language, any statement
> can have any side effect, in haskell, if you know what monad you're in, you
> know exactly what the "side effect" will occur - which means, it's not
> really a side effect at all, but part of the actual intended effect.
>
> Now for IO. You can think of IO as being essentially State RealWorld. That
> is, every operation is dependent on the entire state of the world, including
> what you're thinking, and what kind of bug is crawling on the 18th blade of
> grass in your yard. If we could actually represent the whole world this way,
> Haskell would truly be a completely pure language. The only reason IO, and
> thus Haskell, is impure at all, is because we can't literally represent the
> whole world. EVERYTHING ELSE, INCLUDING EVERY OTHER TYPE OF MONADIC ACTION
> IN HASKELL, is completely pure.
>
> Ok, so let's address your questions a little more specifically.
>
> Q1: The web page mentions that normal Haskell functions cannot cause
>> side-effects, yet later talks about
>> side-effects with putStrLn. I assume the key point here that IO actions
>> are, by definition, _not_ normal functions?
>>
>
> Right, IO actions can have side effects because they can take into account,
> and modify, the RealWorld.
>
>
>> Q2: Is it true to say that *any* monadic action *could *cause
>> side-effects, depending on the design of that
>> monad? i.e. Does one generalize from the IO monad to (possibly) an
>> arbitrary monad? *Musing* This must be true as
>> using State must surely be considered a side-effect.
>>
>
> Again, yes, this is accurate, but it's different from most impure languages
> in that the side effect is completely baked in by the monad you're in, so
> that you can't really say that the effect is a "side effect" at all.
>
>
>> Q3: The web page mentions IO as being a baton, or token, that is used to
>> thread/order the actions. Is true
>> that this is merely one simple perspective, with respect to order of
>> evaluation? This is hard to articulate,
>> but it seems to me that "in the IO monad" there is a large subsystem of
>> (inaccessible) state, machinery, etc.
>> Is it really a token?
>>
>
> Again, in many ways, it's easier to think of the IO monad as a state monad.
> In that sense, the state of the world is indeed being passed from one action
> to the next, as defined by bind. That's the inaccessible state machinery I
> suspect you're sensing.
>
>
>> Q4: Is the following idea accurate: a Haskell program is partitioned into
>> 2 spaces. One is a sequence
>> of IO actions; the other is a space of pure functions and 'normal' Haskell
>> operations.  The execution of a
>> program begins with the main :: IO () action and, effectively, crosses
>> from one space to the other. In the
>> pure space, the math-like functions can be highly optimized but only
>> insofar as they do not disrupt the
>> implied order of the IO actions.  Because of the type system, the program
>> recognizes when it enters
>> "back" into the IO space and follows different, less optimized rules.
>>
>
> I think it would be easier to talk about haskell in terms of pure and
> impure code. All Haskell code is pure except for IO. Pure functions are
> easier to reason about and to optimize, because you don't have to take into
> account the RealWorld state, or other possible, REAL side effects..
>
>
>> My concern is that the above is *not* accurate, but I don't know why.
>>
>>
>> thanks so much for your help
>> Michael Easter
>>
>> --
>> ----------------------
>> Michael Easter
>> http://codetojoy.blogspot.com: Putting the thrill back in blog
>>
>> http://youtube.com/ocitv -> Fun people doing serious software engineering
>>
>> _______________________________________________
>> Beginners mailing list
>> [hidden email]
>> http://www.haskell.org/mailman/listinfo/beginners
>>
>>
>


--
----------------------
Michael Easter
http://codetojoy.blogspot.com: Putting the thrill back in blog

http://youtube.com/ocitv -> Fun people doing serious software engineering
-------------- next part --------------
An HTML attachment was scrubbed...
URL: http://www.haskell.org/pipermail/beginners/attachments/20090227/662f4e97/attachment.htm
Reply | Threaded
Open this post in threaded view
|

clarification on IO

Chaddaï Fouché
In reply to this post by Michael Easter
On Sat, Feb 28, 2009 at 3:50 AM, Michael Easter <[hidden email]> wrote:
> Q1: The web page mentions that normal Haskell functions cannot cause
> side-effects, yet later talks about
> side-effects with putStrLn. I assume the key point here that IO actions are,
> by definition, _not_ normal functions?

putStrLn is a perfectly pure function (there is no other kind in
Haskell), it takes a String and return a value of type IO (), this
value is like any other first class object in Haskell and can be
manipulated as thus, the only difference is that this value can be
computed in the context of the IO monad and then have a side-effect.
The value of type IO a are often called actions but they aren't
fundamentally different from other values.

> Q2: Is it true to say that any monadic action could cause side-effects,
> depending on the design of that
> monad? i.e. Does one generalize from the IO monad to (possibly) an arbitrary
> monad? *Musing* This must be true as
> using State must surely be considered a side-effect.

"Side-effect" isn't really appropriate in my opinion, since everything
is still perfectly pure (except in IO) and the "effects" one obtain by
using a monad are perfectly determined by the nature of the monad.

> Q3: The web page mentions IO as being a baton, or token, that is used to
> thread/order the actions. Is true
> that this is merely one simple perspective, with respect to order of
> evaluation? This is hard to articulate,
> but it seems to me that "in the IO monad" there is a large subsystem of
> (inaccessible) state, machinery, etc.
> Is it really a token?

This "token" is an useful concept to understand how the IO monad
works, indeed that is how it is encoded in GHC where the IO monad is
just a variant of the ST monad with a special type for the state :
RealWorld. Of course the primitives used in the IO monad can't be
written in pure Haskell.

> Q4: Is the following idea accurate: a Haskell program is partitioned into 2
> spaces. One is a sequence
> of IO actions; the other is a space of pure functions and 'normal' Haskell
> operations.? The execution of a
> program begins with the main :: IO () action and, effectively, crosses from
> one space to the other. In the
> pure space, the math-like functions can be highly optimized but only insofar
> as they do not disrupt the
> implied order of the IO actions.? Because of the type system, the program
> recognizes when it enters
> "back" into the IO space and follows different, less optimized rules.

You can effectively see it like that, but it must be noted that the
ordering of IO actions isn't special in any sense, it is just implied
by the strict data dependence encoded in the monad, the compiler don't
optimize differently the IO portion of the program.
"main" is the only function that is evaluated unconditionally, any
other value and/or function in a program is only evaluated if main
needs it.

--
Jeda?
Reply | Threaded
Open this post in threaded view
|

clarification on IO

Gregg Reynolds-4
In reply to this post by Michael Easter
On Fri, Feb 27, 2009 at 8:50 PM, Michael Easter <[hidden email]> wrote:

>
>
> Q1: The web page mentions that normal Haskell functions cannot cause
> side-effects, yet later talks about
> side-effects with putStrLn. I assume the key point here that IO actions
> are, by definition, _not_ normal functions?
>

Right, the IO primitives are not functions.


> Q2: Is it true to say that *any* monadic action *could *cause
> side-effects, depending on the design of that
> monad? i.e. Does one generalize from the IO monad to (possibly) an
> arbitrary monad? *Musing* This must be true as
> using State must surely be considered a side-effect.
>

It might be helpful to distinguish between internal side effects (e.g.
updating a global var in an imperative language) and external (IO).  Haskell
uses monads to simulate the former and to serialize the latter.

>
>
> Q3: The web page mentions IO as being a baton, or token, that is used to
> thread/order the actions. Is true
> that this is merely one simple perspective, with respect to order of
> evaluation? This is hard to articulate,
> but it seems to me that "in the IO monad" there is a large subsystem of
> (inaccessible) state, machinery, etc.
> Is it really a token?
>

Bear in mind that's the GHC implementation.  I'm told another implementation
uses some kind of continuation monad.  The only requirement as I understand
it is that the IO monad, however it is designed, must impose an order of
evaluation on the IO primitives.  GHC does hide the implementation details.

>
> Q4: Is the following idea accurate: a Haskell program is partitioned into 2
> spaces. One is a sequence
> of IO actions; the other is a space of pure functions and 'normal' Haskell
> operations.  The execution of a
> program begins with the main :: IO () action and, effectively, crosses from
> one space to the other. In the
> pure space, the math-like functions can be highly optimized but only
> insofar as they do not disrupt the
> implied order of the IO actions.  Because of the type system, the program
> recognizes when it enters
> "back" into the IO space and follows different, less optimized rules.
>

Hmm, to me there's something fishy about thinking in terms of two spaces.
IO expressions are normal Haskell expressions; they just happen to get
sequenced because they get chained together by monad ops.  I.e. as far has
Haskell semantics is concerned they're not special; Haskell needn't know
they're impure.  The impure part is external the Haskell semantics.  Then
remember lazy evaluation; the whole program can be optimized at compile time
to some extent, and at run time evaluation is forced by the ordering of the
"IO chain" that leads to main.

Be careful about thinking of the program as "starting at main" and then
proceeding through the IO chain.  That's perilously close to imperative
thinking.  It's more accurate (IMO) to say that the program gets evaluated
at run time, which means main gets evaluated, and since the value of main
depends on the series of IO actions chained to it, they get forced in
order.  So main isn't really the "first thing that happens"; it's the ONLY
thing that happens, since the meaning of the program (i.e. main) is
equivalent to the evaluation of the IO chain.  Actual IO is a side effect of
evaluating main.

Hope it helps,

gregg
-------------- next part --------------
An HTML attachment was scrubbed...
URL: http://www.haskell.org/pipermail/beginners/attachments/20090228/a3c4a077/attachment-0001.htm
Reply | Threaded
Open this post in threaded view
|

Re: clarification on IO

Will Ness-2
In reply to this post by Michael Easter
Michael Easter <codetojoy <at> gmail.com> writes:

>
>
> Q1: The web page mentions that normal Haskell functions cannot cause side-
effects, yet later talks about
> side-effects with putStrLn. I assume the key point here that IO actions are,
by definition, _not_ normal functions?


The way I look at it, a monad is a way of saying, "we'll be carrying along and
combining, in some specific manner, values with some extra information attached
to them, which most functions don't need to worry about". _How_ it carries
along these values and combines them, and the corresponding  hidden data, is
what makes the particular Monad type to be what it is.

Let's call a function an M-action function if its type is (:: a -> M b), for
some monad M. Such functions can get chained by M's bind, as in

 ( (x :: M a)  >>= (f :: a -> M b) ) :: M b

M can also provide us with special functions that will reveal its hidden
information, e.g. (reveal ::  (hidden_stuff -> a -> M b) -> a -> M b), so that
for a user-defined (g :: hidden_stuff -> a -> M b), (reveal g) can be chained
and the function g will get called providing us with the information, if the
monad so chooses.

For example, Philip Wadler in his paper "The Essence of Functional Programming"
gives an example of a "position" monad P which defines its own special
function, resetPosition, enabling us to reset its hidden data - in this case, a
code position.

IO primitives like putStrLn, instead of revealing anything to us, carry along a
promise to take notice of the value they are supplied with and to perform an
actual real world IO action with them, when called upon to do so by the system.

We can create chains of IO-action functions and store them in variables,
without them getting executed by the system - and thus without causing any real-
world IO action, e.g.

  x = do {putStrLn "1";return 2}

somewhere in your program will just create a definition that will just hold an
IO-action function in it.

It's just a feature of an interactive loop that when is sees a value of type
(IO a) it will not just return it, but will also execute the promised actions
held inside it:


  -- in HUGS --

  Prelude>  [ do {putStrLn "1";return 2} ]   -- a value just gets returned
  [<<IO action>>] :: [IO Integer]
  Prelude>  do {putStrLn "1";return 2}       -- actual IO action performed
  1 :: IO Integer


  -- in GHCi --

  Prelude> let x=[do {putStrLn "1"; return 2}]
  Prelude> head x
  1
  2
  Prelude> head x
  1
  2
  Prelude> :t it
  it :: Integer


The same goes to the special identifier (main :: IO a) that will also get
treated in this special way - getting executed on sight. :)



Reply | Threaded
Open this post in threaded view
|

clarification on IO

Thomas Davie
In reply to this post by Chaddaï Fouché

On 28 Feb 2009, at 10:13, Chadda? Fouch? wrote:

> On Sat, Feb 28, 2009 at 3:50 AM, Michael Easter  
> <[hidden email]> wrote:
>> Q1: The web page mentions that normal Haskell functions cannot cause
>> side-effects, yet later talks about
>> side-effects with putStrLn. I assume the key point here that IO  
>> actions are,
>> by definition, _not_ normal functions?
>
> putStrLn is a perfectly pure function (there is no other kind in
> Haskell), it takes a String and return a value of type IO (), this
> value is like any other first class object in Haskell and can be
> manipulated as thus, the only difference is that this value can be
> computed in the context of the IO monad and then have a side-effect.
> The value of type IO a are often called actions but they aren't
> fundamentally different from other values.

Note, take this information with a pretty gigantic grain of salt.  It  
is true that you are writing a pure program when using IO functions,  
but you should probably think about what that means.

The advantage of pure programs is that you get certain neat benefits  
like not having to bother about "am I doing this in the right order"  
or "will the system do weird random shit while this function is  
executed".  This is not true about IO typed functions.  An IO typed  
function generates an IO value which the runtime runs.  That means,  
that they essentially get us no further forward than C.  In C we write  
pure functions called C preprocessor macros.  These pure functions  
generate imperative programs (C programs) which are run by the runtime  
(the C compiler and your OS).  This is no different to IO.

So, the lesson ? IO is in theory pure.  But that doesn't mean that we  
get all the nice benefits of functional programming while using the IO  
monad.  One can say pretty much the same thing about all stateful  
monads, although IO is by far the worse, in that the state can be  
influenced by chunks of reality totally outside your control.

Bob
Reply | Threaded
Open this post in threaded view
|

Re: clarification on IO

Will Ness-2
In reply to this post by Michael Easter
Michael Easter <codetojoy <at> gmail.com> writes:

>
>
> Thanks Andrew, this is really great...My main revelation here is that a "side-
effect" in other monads is still pure.? e.g. The Logger example in RWH builds
up a list of log strings "behind the scenes" but this is much different than
writing to disk, or launching missiles, to quote SP Jones.


May be this is exactly how we ought to look at the IO monad - as a Logger
monad? Each IO-bound chain of action-functions defining an IO value that holds
a record of what it is the IO primitives that we used promised us they will do
when run by the system.

That's it.

(?)

After all, we can have a definition of such a value, and have it run multiple
times for us, so _as definition_ it's no different than any other definition in
Haskell. It's just that _its value_ can cause the system to actually perform
these IO actions in some circumstances.

As for terminology: we've got to have some special name for functions that are
chainable by bind. Calling them actions confuses them with the real world
actions performed by IO.

May be to call them "action functions"?


Reply | Threaded
Open this post in threaded view
|

Re: clarification on IO

Gregg Reynolds-4
On Sun, Mar 1, 2009 at 4:28 AM, Will Ness <[hidden email]> wrote:

> Michael Easter <codetojoy <at> gmail.com> writes:
> ...
> After all, we can have a definition of such a value, and have it run
> multiple
> times for us, so _as definition_ it's no different than any other
> definition in
> Haskell. It's just that _its value_ can cause the system to actually
> perform
> these IO actions in some circumstances.


But it isn't a definition.  "Reference" would be better; "getChar" is a term
that references a value.

>
> As for terminology: we've got to have some special name for functions that
> are
> chainable by bind. Calling them actions confuses them with the real world
> actions performed by IO.
>

Correction:  special name for IO "functions" (actually "IO terms" would be
better).  The monad just organizes stuff, so the IO monad, as monad, is no
different than any other monad.

>
> May be to call them "action functions"?
>

This was a big problem for me; I find terms like "action", "computation",
"function" completely misleading for IO terms/values.  You might find
"Computation"
considered harmful. "Value" not so hot
either<http://syntax.wikidot.com/blog:5>useful; see also the comment
"Another try at the key sentence".  There are a
few other articles on the blog that address this terminology problem.

-gregg
-------------- next part --------------
An HTML attachment was scrubbed...
URL: http://www.haskell.org/pipermail/beginners/attachments/20090301/e0ccb0b8/attachment.htm
Reply | Threaded
Open this post in threaded view
|

Re: clarification on IO

Will Ness-2
Gregg Reynolds <dev <at> mobileink.com> writes:

> On Sun, Mar 1, 2009 at 4:28 AM, Will Ness <will_n48 <at> yahoo.com> wrote:
>
>
> Michael Easter <codetojoy <at> gmail.com> writes:
> ...
> After all, we can have a definition of such a value, and have it run multiple
> times for us, so _as definition_ it's no different than any other definition
in
> Haskell. It's just that _its value_ can cause the system to actually perform
> these IO actions in some circumstances.
>
> But it isn't a definition.? "Reference" would be better; "getChar" is a term
that references a value.


Excuse me for being too brief. I was talking about the definitions of IO
values, of type (IO a), viz. the do-block (or bind-chain) definitions, as in

  let v=do { getChar; putStrLn "12"; return 2 }

that are just regular Haskell defintions, from inside the Haskell world POV.
The IO-action functions involved here would be

  getChar
  (\_-> putStrLn "12")
  (\_-> return 2)

These IO-action functions, chaind by IO bind, create a combined-actions IO
value, in this case of type (:: Num a => IO a).

The IO-action is not the I/O operation in the real world, but an action of
recording a promise to perform it.

Any IO primitive can be seen as latching this hidden promise onto its explicit
return value, thus creating a monadic value (:: IO a), carrying along this
hidden promise. IO bind combines these promises into a combined record, or log,
of promises to perform actual I/O activity, if called upon by the system.

That recording of a promise is the IO-action that IO monad is about, from pure
Haskell standpoint.

Another monad will have another meaning for its actions, latching another
hidden data on their results, but they still can be seen as actions, in context
of being sequenced and combined by that monad's bind.

Haskell will also attempt to reduce and simplify this combined actions value,
unlike in IO case where it can't do that because the values involved can change
from one invocation to another.

So seen from inside the pure Haskell world IO monad is just another Logging
Monad, building logs of future activity to be performed. And it can be
performed more than once, of course:

  Prelude> let v=do{ getChar; putStrLn "12"; return () }
  Prelude> [v]
  [<<IO action>>] :: [IO ()]  
  Prelude> v
  c12
  Prelude> v
  d12
  Prelude>

> As for terminology: we've got to have some special name for functions that are
> chainable by bind. Calling them actions confuses them with the real world
> actions performed by IO.


What I seek here is to demystify the monad, any monad, and IO monad in
particular, and for that a clear and consisent terminology must be employed. We
should be able to name things, in English, that we talk about - in English.


> Correction:? special name for IO "functions" (actually "IO terms" would be
better).?


Why? They are just fuctions, of type (Monad m => a -> m b). What I'm saying,
they are of special type, chainable by the M monad, so it seems logical to have
a special name for such M-chainable functions, e.g. "M-action functions"
(whatever the M).

The monad M is chaining and combining something. It is M-action functions, of
type (a -> M b), that get chained by its bind, and their hidden data combined
by it, behind the curtain.

Usually when we have a name for some concept, it becomes clearer. And vice
versa.


> The monad just organizes stuff, so the IO monad, as monad, is no different
than any other monad.
>
>
>
> May be to call them "action functions"?
>
>
> This was a big problem for me; I find terms
like "action", "computation", "function" completely misleading for IO
terms/values.?


Why? A function of type (a -> M b) is a function that returns a value, (:: M
b), tagged with some monadic hidden data. In case of IO, it is a promise to
perform some actual I/O that's passed around, hidden. But the M-action function
itself is just a regular Haskell function. It can be defined elsewhere,
anywhere.

IO values themselves are no mystery too. They just carry along with them a log
of promised I/O activity as specified at time of their creation (definition).


> You might find ["Computation" considered harmful. "Value" not so hot either]
useful; see also the comment "Another try at the key sentence".? There are a
few other articles on the blog that address this terminology problem.


Thank you. Will do.




Reply | Threaded
Open this post in threaded view
|

Re: clarification on IO

Gregg Reynolds-4
On Sun, Mar 1, 2009 at 11:59 AM, Will Ness <[hidden email]> wrote:


> The IO-action is not the I/O operation in the real world, but an action of
> recording a promise to perform it.
>
> Any IO primitive can be seen as latching this hidden promise onto its
> explicit
> return value, thus creating a monadic value (:: IO a), carrying along this
> hidden promise. IO bind combines these promises into a combined record, or
> log,
> of promises to perform actual I/O activity, if called upon by the system.
>
> That recording of a promise is the IO-action that IO monad is about, from
> pure
> Haskell standpoint.
>

Ok, I kinda like the idea of accumulating a "log" of promised actions,
although I'd suggest a different term since log usually means history.
Maybe antilog or prelog or future trace or the like.  In any case, I think
that's useful for explaining lazy evaluation, but it's not directly
implicated by monad semantics.  IOW monad semantics and evaluation strategy
are (mostly) orthogonal.


> Another monad will have another meaning for its actions, latching another
> hidden data on their results, but they still can be seen as actions, in
> context
> of being sequenced and combined by that monad's bind.
>

I see, you're thinking of action as something like the promised eval.  As
opposed to the action of performing actual IO.  I fear that might confuse
newcomers, though; probably better to stick with "function" terminology and
discuss evaluation separately.

>
> > Correction:  special name for IO "functions" (actually "IO terms" would
> be
> better).
>
>
> Why? They are just fuctions, of type (Monad m => a -> m b). What I'm
> saying,
> they are of special type, chainable by the M monad, so it seems logical to
> have
> a special name for such M-chainable functions, e.g. "M-action functions"
> (whatever the M).
>

Technically they cannot be functions - there's no "same input, same output"
(at least not for input operations).  No referential transparency.  That's
the problem.


> >
> > This was a big problem for me; I find terms
> like "action", "computation", "function" completely misleading for IO
> terms/values.
>
>
> Why? A function of type (a -> M b) is a function that returns a value, (::
> M
> b), tagged with some monadic hidden data. In case of IO, it is a promise to
>
>
perform some actual I/O that's passed around, hidden. But the M-action
> function
> itself is just a regular Haskell function. It can be defined elsewhere,
> anywhere.
>

But the "promise to perform" is a matter of evaluation semantics, not
denotational semantics.  Denotationally these things cannot be functions.

I saw your other note too.  I think the idea that the runtime builds a
"future log" is useful - simple and pretty easy to grok.  But I would
recommend keeping a clear distinction between Haskell's language semantics
(denotational) and its compiler/runtime semantics (evaluational).
Denotationally, all a monad does is ensure sequencing, which is necessary to
properly order the (non-deterministic) IO values.  With lazy eval this gets
translated into the building of a "future log" etc.

Thanks,

-gregg
-------------- next part --------------
An HTML attachment was scrubbed...
URL: http://www.haskell.org/pipermail/beginners/attachments/20090302/a21f797c/attachment.htm
Reply | Threaded
Open this post in threaded view
|

Re: clarification on IO

Will Ness-2
Gregg Reynolds <dev <at> mobileink.com> writes:

>
>
> On Sun, Mar 1, 2009 at 11:59 AM, Will Ness <will_n48 <at> yahoo.com> wrote:
>
> ?
>
> The IO-action is not the I/O operation in the real world, but an action of
> recording a promise to perform it.
> Any IO primitive can be seen as latching this hidden promise onto its explicit
> return value, thus creating a monadic value (:: IO a), carrying along this
> hidden promise. IO bind combines these promises into a combined record, or
log,
> of promises to perform actual I/O activity, if called upon by the system.
> That recording of a promise is the IO-action that IO monad is about, from pure
> Haskell standpoint.
>
>
> Ok, I kinda like the idea of accumulating a "log" of promised actions,
although I'd suggest a different term since log usually means history.? Maybe
antilog or prelog or future trace or the like.? In any case, I think that's
useful for explaining lazy evaluation, but it's not directly implicated by
monad semantics.? IOW monad semantics and evaluation strategy are (mostly)
orthogonal.?
>
> ?
>
> Another monad will have another meaning for its actions, latching another
> hidden data on their results, but they still can be seen as actions, in
context
> of being sequenced and combined by that monad's bind.
>
>
> I see, you're thinking of action as something like the promised eval.? As
opposed to the action of performing actual IO.? I fear that might confuse
newcomers, though; probably better to stick with "function" terminology and
discuss evaluation separately.?
>
>
>
> > Correction:? special name for IO "functions" (actually "IO terms" would be
> better).?
> Why? They are just fuctions, of type (Monad m => a -> m b). What I'm saying,
> they are of special type, chainable by the M monad, so it seems logical to
have
> a special name for such M-chainable functions, e.g. "M-action functions"
> (whatever the M).
>
>
> Technically they cannot be functions - there's no "same input, same output"
(at least not for input operations).? No referential transparency.? That's the
problem.?

>
> >
> > This was a big problem for me; I find terms
> like "action", "computation", "function" completely misleading for IO
> terms/values.?
> Why? A function of type (a -> M b) is a function that returns a value, (:: M
> b), tagged with some monadic hidden data. In case of IO, it is a promise to
>
>
> perform some actual I/O that's passed around, hidden. But the M-action
function
> itself is just a regular Haskell function. It can be defined elsewhere,
> anywhere.
>
>
> But the "promise to perform" is a matter of evaluation semantics, not
denotational semantics.? Denotationally these things cannot be functions.I saw
your other note too.? I think the idea that the runtime builds a "future log"
is useful - simple and pretty easy to grok.? But I would recommend keeping a
clear distinction between Haskell's language semantics (denotational) and its
compiler/runtime semantics (evaluational).? Denotationally, all a monad does is
ensure sequencing, which is necessary to properly order the (non-deterministic)
IO values.? With lazy eval this gets translated into the building of a "future
log" etc.Thanks,-gregg
>
>
>
>
> _______________________________________________
> Beginners mailing list
> Beginners <at> haskell.org
> http://www.haskell.org/mailman/listinfo/beginners
>




Reply | Threaded
Open this post in threaded view
|

Re: clarification on IO

Will Ness-2
In reply to this post by Gregg Reynolds-4
(something happened at my previous attempt to post; my apologies)

Gregg Reynolds <dev <at> mobileink.com> writes:

>
>
> On Sun, Mar 1, 2009 at 11:59 AM, Will Ness <will_n48 <at> yahoo.com> wrote:
>
>  
>
> The IO-action is not the I/O operation in the real world, but an action of
> recording a promise to perform it.
>
>
> Ok, I kinda like the idea of accumulating a "log" of promised actions,
although I'd suggest a different term since log usually means history.  

It is a log of history of promises _requested_.

Or it can be a plan then. An operational record. A list. :)


>Maybe antilog or prelog or future trace or the like.  In any case, I think
that's useful for explaining lazy evaluation, but it's not directly implicated
by monad semantics.  IOW monad semantics and evaluation strategy are (mostly)
orthogonal.


I don't follow. Monad semantics in general is to chain together its action-
functions (:: a -> M b). IO monad's semantics is that it promises to perform
the recorded requests, if called upon. Not only is it directly implicated by
its semantics, it IS its semantics. It is what IO-bind is. Other monad's binds
will mean something else.

We really ought to look at IO monad as just another kind of regular Haskell
monad first.

Here your idea from the blog helps, of separating the two worlds, of compiler
and of its run-time system implementation (in my re-formulation). The
operational plan built by IO monad may have multiple entries interspersed,
going back and force to one world or another (getting new input into its
placeholder and then going into Haskell to perform a chunk of pure computation
with it, then possibly going back for another input etc.).

Or in case of output only of known in advance values it can be compiled down to
just one output instruction. Which is recorded and stored in an (:: IO a)
value. Which can be run SEVERAL times by the run-time, or none at all.

It's really helpful to imagine this operational plan as just a symbolic list of
operations to be performed by some interpreter, capable of taking input, and
calling back into Haskell code with it. The next step is to imagine it compiled
into some kind of C with set-once semantics.

To recapture, the only difference IO monad has, is that it refers EXPLICITLY to
a compiler, and its runtime execution operations. But from inside Haskell it's
just anther monad (I've said that already haven't I? :) ).


>> Another monad will have another meaning for its actions, latching another
>> hidden data on their results, but they still can be seen as actions, in
>> context
>> of being sequenced and combined by that monad's bind.
>
>
> I see, you're thinking of action as something like the promised eval.

It is an action of entering a request (for future I/O activity) to be recorded
by Haskell, to be performed later by its run-time system (notice I avoid the
word evaluated which is confusing as it alludes back to the Haskell-world
calculations). It _is_ the meaning of IO-action-function's associated ...well,
action.

> As opposed to the action of performing actual IO.  I fear that might confuse
newcomers, though; probably better to stick with "function" terminology and
discuss evaluation separately.

That's why I propose to call these bind-chainable functions action functions,
not just actions, and actual I/O acivities to call just that, activity. Also
notice I write I/O there where it belongs to the actual world action (... the
terminology really MUST be refined here!). I think anything else is confusing.

I'm open for another suggestion for how to name these "action-functions", but
it's time the definite name is finalized; it's very confusing to see these IO-
action-functions referred to, in all these tutorials, as performing real world
I/O actions. They don't, of course.

Better to avoid "evaluation" altogether (that seems to be your another idea
from that log, is it?).

I think the KEY is to always keep a clear separation of what is inside the pure
Haskell world, and what is executed by its run-time, as an imperative program
iving inside the real volatile world (capable of calling back into Haskell).

And when inside the pure Haskell world, IO monad is ABSOLUTELY in NO RESPECT no
different than any other. The operational log metaphor help keep this part of
its semantics clear, from the other part - the fact that its operational log
will actually get executed by run-time.


>> > Correction:  special name for IO "functions" (actually "IO terms" would be
>> > better).
>> Why? They are just fuctions, of type (Monad m => a -> m b). What I'm saying,
>> they are of special type, chainable by the M monad, so it seems logical to
>> have
>> a special name for such M-chainable functions, e.g. "M-action functions"
>> (whatever the M).
>
>
> Technically they cannot be functions - there's no "same input, same output"
(at least not for input operations).

Yes there is. There's the whole point I'm driving at. We are not performing a
computation with our code. We describe the computation that will be performed.
Our values are functions. The usage of actual input is deferred to the runtime
system.

It's just like Show functions that (will) add their output onto a hidden
parameter, the string-being-built (when called). Same here, with the log-being-
built. Assentially, we're dealing here with the delayed application of carried
functions, that's all.

Here's again a simple outline of how an IO monad might look like, inside
Haskell. It helped clarify things for me (dealing with output only, but still):

________________________________
data IO a = IORec -> (a,IORec)
-- building the record of I/O activities to be performed

instance Monad IO where
  return a rec = (a,rec)            -- return :: a -> IO a
  (m ?= g) rec = uncurry g $ m rec  -- g      :: a -> IO b

putStrLn :: a -> IO ()
putStrLn a rec = ((),rec ++ [("putStrLn", a)])
================================


> No referential transparency.  That's the problem.

Everything is referentially transparent. There are no side effects in Haskell.
You know that. :) You wrote as much yourself (assuming you're the author of
that blog).
_______________________________________
IO value describes the computation that
WILL BE performed OUTSIDE of Haskell.
=======================================

That is a statement that is easy to understand, and is not at all confusing. I
think.


>> > This was a big problem for me; I find terms
>> like "action", "computation", "function" completely misleading for IO
>> terms/values.
>> Why? A function of type (a -> M b) is a function that returns a value, (:: M
>> b), tagged with some monadic hidden data. In case of IO, it is a promise to
>> perform some actual I/O that's passed around, hidden. But the M-action
>> function
>> itself is just a regular Haskell function. It can be defined elsewhere,
>> anywhere.
>
>
> But the "promise to perform" is a matter of evaluation semantics, not
denotational semantics.  Denotationally these things cannot be functions.

But they are. They describe future computation to be performed outside of
Haskell. Their values - inside Haskell - are one and the same - it's (:: IO a)
entities. Which encapsulate the record, the
_sceleton_of_future_computation_to_be_performed, which is ONE and only. It's
just that it has holes in it, where the actual values will go into. It's like
back into Prolog with its yet-unassigned variables. Or to any imperative
language with set-once.


>I saw your other note too.  I think the idea that the runtime builds a "future
log" is useful - simple and pretty easy to grok.  But I would recommend keeping
a clear distinction between Haskell's language semantics (denotational) and its
compiler/runtime semantics (evaluational).  

I think we've established that compiler and runtime should be kept completely
separate.

>Denotationally, all a monad does is ensure sequencing, which is necessary to
properly order the (non-deterministic) IO values.  

No it does more than that. It ascribes actual meaning to what its M-action-
functions mean, and it defines what it means for them to be combined in a
chain. They are of course kept in sequence, in that chain.

>With lazy eval this gets translated into the building of a "future log" etc.

Right, only better not to use "eval" - ever. Haskell has expressions which get
reduced; values belong to its runtime system. They are OUTSIDE of Haskell
world.

We do not "evaluate" anything. It would be an imperative. :)




> Thanks,-gregg


Thank you, for a great and enlightening discussion.

>
> _______________________________________________
> Beginners mailing list
> Beginners <at> haskell.org
> http://www.haskell.org/mailman/listinfo/beginners
>



Reply | Threaded
Open this post in threaded view
|

Re: clarification on IO

Gregg Reynolds-4
In reply to this post by Will Ness-2
Hi Will,

I can tell I'm talking to a kindred spirit - we oughta be able to describe
all this stuff in plain, simple, clear English.  It's a great challenge for
a prose writer.

On Mon, Mar 2, 2009 at 9:21 AM, Will Ness <[hidden email]> wrote:

>
> >Maybe antilog or prelog or future trace or the like.  In any case, I think
> that's useful for explaining lazy evaluation, but it's not directly
> implicated
> by monad semantics.  IOW monad semantics and evaluation strategy are
> (mostly)
> orthogonal.
>
>
> I don't follow. Monad semantics in general is to chain together its action-
> functions (:: a -> M b). IO monad's semantics is that it promises to
> perform
> the recorded requests, if called upon. Not only is it directly implicated
> by
> its semantics, it IS its semantics. It is what IO-bind is. Other monad's
> binds
> will mean something else.


Right, but this is //Haskell// monad semantics.  It's an artifact of lazy
evaluation.  Referring back to the mathematical definition of monad, there's
no evaluation process or promise, only denotation.


> To recapture, the only difference IO monad has, is that it refers
> EXPLICITLY to
> a compiler, and its runtime execution operations. But from inside Haskell
> it's
> just anther monad (I've said that already haven't I? :) ).
>

Mmm, I wouldn't say explicitly.  Again, this is because of lazy evaluation,
not because of the nature of monads.  They just put stuff in order,
regardless of evaluation strategy.


> That's why I propose to call these bind-chainable functions action
> functions,
> not just actions, and actual I/O acivities to call just that, activity.
> Also
> notice I write I/O there where it belongs to the actual world action (...
> the
> terminology really MUST be refined here!). I think anything else is
> confusing.
>

Agreed, that's the key distinction.

>
> I'm open for another suggestion for how to name these "action-functions",
> but
> it's time the definite name is finalized; it's very confusing to see these
> IO-
> action-functions referred to, in all these tutorials, as performing real
> world
> I/O actions. They don't, of course.
>

Alas, this is where taste enters into it.  You can come up with the perfect
set of names and somebody won't like it.  ;)  My own preference is to
dispense with the action/function idiom and just say that IO //terms// are
essentially unbound (but typed) variables, that will be bound to values when
interpreted at runtime.  So I wouldn't even call "getChar" an action; I'd
just say it's a term denoting an IO Char value to be provided later.  In
contrast with a constant term like '3'.


>
> Better to avoid "evaluation" altogether (that seems to be your another idea
> from that log, is it?).
>

Yep.  Hate evaluation.  ;)

>
> I think the KEY is to always keep a clear separation of what is inside the
> pure
> Haskell world, and what is executed by its run-time, as an imperative
> program
> iving inside the real volatile world (capable of calling back into
> Haskell).
>

Yeah, I don't recall many tutorials zeroing in on this.  I think it's very
helpful.

>
> And when inside the pure Haskell world, IO monad is ABSOLUTELY in NO
> RESPECT no
> different than any other. The operational log metaphor help keep this part
> of
> its semantics clear, from the other part - the fact that its operational
> log
> will actually get executed by run-time.
>
>
> >> > Correction:  special name for IO "functions" (actually "IO terms"
> would be
> >> > better).
> >> Why? They are just fuctions, of type (Monad m => a -> m b). What I'm
> saying,
> >> they are of special type, chainable by the M monad, so it seems logical
> to
> >> have
> >> a special name for such M-chainable functions, e.g. "M-action functions"
> >> (whatever the M).
> >
> >
> > Technically they cannot be functions - there's no "same input, same
> output"
> (at least not for input operations).
>
> Yes there is. There's the whole point I'm driving at. We are not performing
> a
> computation with our code. We describe the computation that will be
> performed.
> Our values are functions. The usage of actual input is deferred to the
> runtime
> system.
>

What's the domain of "getChar"?  We can't mess with the mathematical
definition of function, so we really can't use it for IO stuff (or any
non-deterministic value, e.g. random).

To quibble yet more:  there's no computation involved in IO, strictly
speaking, since it's analog.  A Turing machine (as he originally described
it) can't do IO.

>
> It's just like Show functions that (will) add their output onto a hidden
> parameter, the string-being-built (when called). Same here, with the
> log-being-
> built. Assentially, we're dealing here with the delayed application of
> carried
> functions, that's all.
>
> Here's again a simple outline of how an IO monad might look like, inside
> Haskell. It helped clarify things for me (dealing with output only, but
> still):
>
> ________________________________
> data IO a = IORec -> (a,IORec)
> -- building the record of I/O activities to be performed
>
> instance Monad IO where
>  return a rec = (a,rec)            -- return :: a -> IO a
>  (m ?= g) rec = uncurry g $ m rec  -- g      :: a -> IO b
>
> putStrLn :: a -> IO ()
> putStrLn a rec = ((),rec ++ [("putStrLn", a)])
> ================================
>
>
> > No referential transparency.  That's the problem.
>
> Everything is referentially transparent. There are no side effects in
> Haskell.
> You know that. :) You wrote as much yourself (assuming you're the author of
> that blog).
> _______________________________________
> IO value describes the computation that
> WILL BE performed OUTSIDE of Haskell.
> =======================================
>
> That is a statement that is easy to understand, and is not at all
> confusing. I
> think.
>

But also logically inconsistent:  how can an expression "inside" of Haskell
refer to something outside of Haskell?  More specifically, Haskell
expressions can only denote values in the Haskell semantic universe.  IO
processes (not computations) lie outside of that universe, so Haskell cannot
say anything about them.  But the //result// of an IO process is a value
within the semantic universe, so it can be referenced.

>> > This was a big problem for me; I find terms
>> like "action", "computation", "function" completely misleading for IO
>> terms/values.
>> Why? A function of type (a -> M b) is a function that returns a value,
(:: M
>> b), tagged with some monadic hidden data. In case of IO, it is a promise
to
>> perform some actual I/O that's passed around, hidden. But the M-action
>> function
>> itself is just a regular Haskell function. It can be defined elsewhere,
>> anywhere.
>
>
> But the "promise to perform" is a matter of evaluation semantics, not
denotational semantics.  Denotationally these things cannot be functions.

But they are. They describe future computation to be performed outside of

> Haskell. Their values - inside Haskell - are one and the same - it's (:: IO
> a)
> entities. Which encapsulate the record, the
> _sceleton_of_future_computation_to_be_performed, which is ONE and only.
> It's
> just that it has holes in it, where the actual values will go into. It's
> like
> back into Prolog with its yet-unassigned variables. Or to any imperative
> language with set-once.
>

The whole future/promise thing comes from lazy evaluation.  With strict
evaluation, there would be no such promise; expressions would be evaluated
(reduced) on the spot, so there would be no log of promised execution.
Language semantics (denotational) and evaluation strategy (operational?) are
orthogonal.  Evaluation strategy doesn't change the meaning (denotation) of
the program, but it does affect its execution profile - memory consumption,
etc. - so programmers have to think about it.  Except of course it does
change the behavior of the program where IO is concerned.   In a lazy
language you can write IO expressions that will never get
evaluate/performed, but not so in a strict language.  But behavior and
meaning are different things.

Take another example: the strict application operator '$!'.  It doesn't
change the denotation of a program but it does change its behavior, by which
I mean the interpretational process.  Such operators don't denote, really;
they're more like meta-syntax or pragmas than Haskell syntax.

>
> >Denotationally, all a monad does is ensure sequencing, which is necessary
> to
> properly order the (non-deterministic) IO values.
>
> No it does more than that. It ascribes actual meaning to what its M-action-
> functions mean, and it defines what it means for them to be combined in a
> chain. They are of course kept in sequence, in that chain.


Ok, then for the IO monad all it does is ensure sequencing. The behavior of
getChar comes from its implementation, not from the monad it is wrapped in.
Remember GHC's implementation of IO as a state transformer is not the only
possible implementation.

>
>
> >With lazy eval this gets translated into the building of a "future log"
> etc.
>
> Right, only better not to use "eval" - ever. Haskell has expressions which
> get
> reduced; values belong to its runtime system. They are OUTSIDE of Haskell
> world.
>
> We do not "evaluate" anything. It would be an imperative. :)
>

We're probably stuck with it, practically speaking, but where extra clarity
is needed I suggest "reduction" instead of "evaluation", from the lambda
calculus.

>
>
>
>
> > Thanks,-gregg
>
>
> Thank you, for a great and enlightening discussion.
>

Same here!
-------------- next part --------------
An HTML attachment was scrubbed...
URL: http://www.haskell.org/pipermail/beginners/attachments/20090302/018e69c8/attachment-0001.htm
Reply | Threaded
Open this post in threaded view
|

Re: clarification on IO

Will Ness-2
Gregg Reynolds <dev <at> mobileink.com> writes:

>
>
> Hi Will,I can tell I'm talking to a kindred spirit - we oughta be able to
describe all this stuff in plain, simple, clear English.? It's a great
challenge for a prose writer.

>
> On Mon, Mar 2, 2009 at 9:21 AM, Will Ness <will_n48 <at> yahoo.com> wrote:
>
> Monad semantics in general is to chain together its action-
> functions (:: a -> M b). IO monad's semantics is that it promises to perform
> the recorded requests, if called upon. Not only is it directly implicated by
> its semantics, it IS its semantics. It is what IO-bind is. Other monad's binds
> will mean something else.?
>
> Right, but this is //Haskell// monad semantics.? It's an artifact of lazy
evaluation.?

I don't think so, no. This value can be forced just like any other:

Prelude> let x = do { c <- getChar; putChar c; return c }
Prelude> const 1 $! x
1
Prelude> :t x
x :: IO Char


> Referring back to the mathematical definition of monad, there's no evaluation
process or promise, only denotation.


Right, it denotes lists of requests that come from chained action-functions, in
case of my metaphoric "IO".


>
> ________________________________
> data IO a = IORec -> (a,IORec)
> -- building the record of I/O activities to be performed
>
> instance Monad IO where
>  ?return a rec = (a,rec) ? ? ? ? ? ?-- return :: a -> IO a
>  ?(m ?= g) rec = uncurry g $ m rec ?-- g ? ? ?:: a -> IO b
> putStrLn :: a -> IO ()
> putStrLn a rec = ((),rec ++ [("putStrLn", a)])
> ================================
>
> _______________________________________
> IO value describes the computation that
> WILL BE performed OUTSIDE of Haskell.
> =======================================
>
>
> But also logically inconsistent:? how can an expression "inside" of Haskell
refer to something outside of Haskell?? More specifically, Haskell expressions
can only denote values in the Haskell semantic universe.? IO processes (not
computations) lie outside of that universe, so Haskell cannot say anything
about them.? But the //result// of an IO process is a value within the semantic
universe, so it can be referenced.


No, it is just described, symbolically, to be interpreted by some external
interpreter, outside of Haskell realm (in our example). The actual I/O hasn't
got a chance to be performed yet. The "holes" in the computation structure,
ready to receive their values, stay empty. IOW the function is built but not
applied yet, its argument(s) not yet bound, computation not yet performed.

But the definition that defines this computation is already there. It can stay
lazy, it can be forced too.


 
> The whole future/promise thing comes from lazy evaluation.?

No, not at all. We could force the value totally that is produced by the above
monad. All it does is it produces a symbolic description of things to do (in my
metaphor). It has nothing to do with Haskell being lazy or strict. The whole
thing could be strictly computed, and still be describing - symbolically -
requests to perform I/O (and pure Haskell calculations that go with them,
working with thus received values).




> With strict evaluation, there would be no such promise; expressions would be
evaluated (reduced) on the spot, so there would be no log of promised
execution.?

No, this can only be done with impure language. Strict or not, doesn't matter.

Since Haskell is pure, it records these requests to be performed later by the
impure run-time system. It's got nothing to do with delay/promise of lazy
evaluation. There is no evaluation in Haskell. Eval is an imperative. :)

That's the whole central point about it. The computation gets defined (as a
function) - but not yet performed (function not called).
___________________________________________
It is all about separating pure and impure,
===========================================
not about doing it strictly or non-strictly.

It'll be performed when the run-time system will call that function. It may do
this twice, or never. The functions is defined just as well. Its definition can
be forced to be more strict, to be fleshed out more fully. It's still a
function wating to be called, so that the computation process it describes will
get performed.

See?

what is promised, is actual I/O operations to be performed - **by the impure
run-time system**. That's the promises I'm talking about, and that's the reason
it's all put aside into a function.

It's to separate the pure and the impure, not to delay some //calculations//.
We're not talking about no delayed evaluation. :)




> Language semantics (denotational) and evaluation strategy (operational?) are
orthogonal.? Evaluation strategy doesn't change the meaning (denotation) of the
program, but it does affect its execution profile - memory consumption, etc. -
so programmers have to think about it.? Except of course it does change the
behavior of the program where IO is concerned.?? In a lazy language you can
write IO expressions that will never get evaluate/performed, but not so in a
strict language.?

No, not so. You can have function in a strict language, calling the I/O
primitives. This function might never get called. So yes, you can do that in a
strict language.

Never once in this whole discussion was I talking about "evaluation strategy".
____________________________________________
It's not about strictness, it's about purity.
============================================


>
>>>Denotationally, all a monad does is ensure sequencing, which is necessary to
>>>properly order the (non-deterministic) IO values.
>>No it does more than that. It ascribes actual meaning to what its M-action-
>>functions mean, and it defines what it means for them to be combined in a
>>chain. They are of course kept in sequence, in that chain.
>
> Ok, then for the IO monad all it does is ensure sequencing. The behavior of
getChar comes from its implementation, not from the monad it is wrapped in.?


Yes. But the fact that the primitive _io_get_char (or whatever) actually gets
_called_ later, *does* come from the monad it is wrapped in. Or else the I/O
would get performed by the following (and it doesn't):


Prelude> const 1 $! getChar
1


>
> >With lazy eval this gets translated into the building of a "future log" etc.
> Right, only better not to use "eval" - ever. Haskell has expressions which get
> reduced; values belong to its runtime system. They are OUTSIDE of Haskell
> world.
> We do not "evaluate" anything. It would be an imperative. :)
>
>
> We're probably stuck with it, practically speaking, but where extra clarity
is needed I suggest "reduction" instead of "evaluation", from the lambda
calculus.


"Reduction" is always better. Not every rewrite simplifies the code though.


Cheers,