So, your objection amounts to a confusion about "actions" and "values". As it happens, an action is a specific type of value. Ignoring this "specific type of action" clause is leading to your confusion. An action is a "monadic" value. A specific kind of value. To get at what "monadic" means in this context, we have to talk about functors and monads.

A functor is a function which maps a type to another type, while preserving the algebraic structure of the first type. In other words, an embedding of an algebra into another algebra. For example, the identity functor:

data Id x = Id x — where fmap f (Id x) = Id (f x)

is a function that takes x's from some algebra, and returns the "same" algebra, only each of the elements have the "tag" (Id) attached to them. This functor is an isomorphism. No "container fallacy" or whatever here. A functor attaches things to elements in an algebra — in this case, the "token" (Id). Given a value x, we'll call its corresponding functorial value (in this case Id x) a "functorial value".

A monad is a specific kind of functor, which has some associated functions called bind and return. I won't go into the axioms. But the point is, given a monad (which is a functor), we'll call its functorial values "monadic values" or "actions".

]]>Compiler is free to reduce everything it can down to the ready-made answer, when all input values are known. Then the only job run-time will be left with is print the answer string to a terminal.

If there are some unknowns involved - marked as such by being in IO monad - compiler can go no further than certain steps involving the unknown values placeholders. Then what we're left with (if the compilation is done to its fullest) is what the run-time will run, the imperative, set-once, strict program.

The values of type `IO a` describe these left-over programs.

Here's one possible sketch-out of IO monad along these lines:

`data IO a = IORec -> (a,IORec)` — record of I/O activities to be performed

`instance Monad IO where return a rec = (a,rec) — :: IO a putStrLn a rec = ((),rec ++ [("putStrLn", a)]) — :: IO () (m »= g) rec = uncurry g $ m rec`

Inside the pure world of Haskell the IO monad just builds up these records of promises to perform I/O activities, if called upon, by the run-time system.

A user writes IO-bind chainable functions of type `:: a -> IO b` - we can call them IO-action functions - in terms of the IO primitives like `return` and `putStrLn` and combines them by means of bind into chains. I/O requests get recorded behind the scenes without the record ever being explicitly referred to by a user.

When the time comes, the system will supply an initial (empty) record value, and the record of I/O to be performed built on the inside will get interpreted by some interpreter on the outside in the run-time, and actual I/O actions will get performed. The same I/O requests record (IO value), defined once, may be run several times by the system, or not at all.

]]>If the SDIOH is a true definition, then IO values are not like other values, and we have a two classes of value in Haskell.

Not any more than

If the SDListH is a true definition, then List values are not like other values, and we have a two classes of value in Haskell.

And yes, we are working with different definitions of tautology.

]]>Thanks,

gregg

]]>Regarding S = {a,b}, let's make sure we're working with the same definition of tautology. My idea of tautology is that, if we have A = B, and B does not add any information to the meaning of A, then we have a tautology. All mathematical equations are tautologies, for example. Take "5 = 2 + 3". The expression "2 + 3" does not tell us anything about "5" that we don't already know. In other words, it is a necessary judgment. In traditional terms it is an "analytic" judgment, as oppposed to a "synthetic" judgment, which provides new information. So in my view, the reasons you give for not considering it a tautology are precisely what makes it a tautology.

]]>The expression "[a]" represents application of the list functor "[]" to the argument 'a'. The denotation of the expression is a primitive/atomic mathematical object; it is not a list, although it is very useful the think of it as a list.

Wait. If you are talking about the same [a] as above, with a a type, I obviously would *not* say [a] is a list. It is not atomic, so far as I am concerned, since it has the structure you so nicely describe. If we a talking about *values* of type [a]… well, I'll need to read what you say first.

Given set S = {a,b}, what is S? It is {a,b}

In other words, S is the set containing precisely two elements a and b. I expect you to argue that this is a tautology. I disagree, since 1) by extensionality, this is a definite description; 2) All we know about S can be concluded from it; 3) thus, I don't care about any other description of S.

]]>The problem with introducing the idea of procedures as you described it is that they cannot be fully described in purely mathematical language. Of course we can model a procedure mathematically as a sequence of expressions, but there are no side effects to such a model; it's just a representation of an algorithm. You're forced inevitably to use magic, which is what the SDIOH does: the function/procedure goes away and conjures up a value. Never mind how, it just does. That's fine in the imperative world, but the goal of functional languages is to do away with magic and replace it with math (IMHO).

The only way to reconcile mathematics and real-world side effects is to split the semantics- mathematics on one side, interpretation (mapping to real world) on the other. No magic.

-Gregg

]]>This is the "Container Fiction". The expression "[a]" represents application of the list functor "[]" to the argument 'a'. The denotation of the expression is a primitive/atomic mathematical object; it is not a list, although it is very useful the *think* of it as a list. I'll post a more detailed explanation in another article.

In your second example you defined type Maybe by listing its extension. That's a tautology. Given set S = {a,b}, what is S? It is {a,b}.

The SDIOH you give is a tautology, but it is incomplete, since it does not account for the side effect. Also, it is a function, and functions don't do anything until applied; even then all they do is map. Where does RealWord come from when this function is applied?

-gregg

]]>It is clear in a language that distinguishes functions and procedures that a function is something you call primarily for its return value and a procedure is something you call primarily for its side-effects, even if the language doesn't enforce that procedures can't return values or that functions can't have side-effects.

I don't think it is too much of a stretch to say that in Haskell procedures can't take parameters directly, and that instead you must have a function that returns a procedure.

]]>Now consider how one might describe a run-of-the-mill non-IO value: "a value of type T a is … a value of type T a". All definitions are tautologies.

A value of type [a] is a list of values of type a.

A value of type Maybe a is either None or a Just a.

No tautologies. In fact, the SDIOH corresponds to such a definition for the GHC: IO a = RealWorld -> (RealWorld, a)

]]>