7

Is this a reasonable view of Haskell IO?

When given a program, the Haskell runtime does the following:

  1. Calls main to get back an "IO computation"
  2. It then executes or "runs" that computation thereby performing all of the side-effects the computation contains.

This two-stage approach allows main to remain a pure function.

An IO computation in this case is like a special version of Haskell that has explicit sequencing - or perhaps there is a better way to describe this?

8
  • This is a quite good explanation indeed. But, in Haskell, main is not a function, just a value. (In Frege, OTOH, it is a function, because it gets the list of command line arguments passed) Commented Feb 23, 2012 at 23:54
  • 1
    This doesn't quite work, because results of computations can affect future computations (e.g. getLine). Commented Feb 23, 2012 at 23:58
  • 6
    @ehird Yes it does. The >>= is just a way to construct IO values. The hard parts come when dealing with things like exceptions Commented Feb 24, 2012 at 0:02
  • @ehird: That's implicit - all monads can be context-sensitive. Commented Feb 24, 2012 at 0:05
  • @Porges true, but that doesn't help the guy who "deals with main". He still has to know how to chain operations. Commented Feb 24, 2012 at 0:15

2 Answers 2

12

Yeah, that's a decent semantic model of how the programs are executed. The implementation doesn't work like that, of course, but you can still use that model to reason about the programs.

But more generally, what IO does is to allow you to treat imperative programs as pure values. The Monad operations then allow you to compose imperative programs from smaller imperative programs (or to use the usual term in this context, actions) and pure functions. So the purely functional model, while it cannot execute imperative programs, can still describe them as expressions of type IO a, and the compiler can translate these descriptions into imperative code.

Or you could say this:

  • The compiler (not the runtime) evaluates main.
  • The result of that evaluation is an imperative program.
  • This program is saved to the target executable.
  • You then execute the target program.

I.e., the "evaluate main" part of your model is pushed to the compiler, and is not in the runtime as you first describe it.

Sign up to request clarification or add additional context in comments.

7 Comments

+1 I like the explanation that "the compiler evaluates main", although you could say that about any programming language with a compiler. But you hit the main point right on the head: Haskell is different because you compose IO actions from smaller IO actions.
@Dan Burton my problem with this is that appeals to "the compiler" or "the runtime" impose a particular way of evaluating Haskell. After all, Hugs doest "compile" main, it interprets it. As much as possible we should talk about what program means, not what some mythical compiler does with it. Compilers come into the picture when we need detailed cost models, but really only then.
@PhilipJF I'm sympathetic to your position there, but still, talking about "the compiler" or "the runtime" is still of didactic value to many people. Think of it as a stepping stone. Or this way: often the best way to teach an advanced topic to a newcomer is to "lie" to them, and then, after they've absorbed the "lies," explain how the initial lie falls short. In this case, well, the shortcoming is that we want to be able to reason about programs independently of how the language is implemented.
I half agree with you about this, and think your answer is rather good. Although, at least for people without an imperative background, I think a pure language based approach can be a suitable way to teach these things. In fact, for some time I have been nursing a fantasy of writing a textbook on computer architecture for people with only a math background. Starting with lambda calculus, functional programming, and a little algebra (categories, monads, etc) we would derive logic gates, simple circuits, RISC computers, compilers, and basic operating systems.
I'm not convinced that "the compiler evaluates main"; for example the expressions if elem 10000000 [1..] then getLine else putStrLn "hello" and getLine evaluate to the same thing, yet main = if elem 10000000 [1..] then getLine else putStrLn "hello" and main = getLine will most probably result in different executables. That would not be true if the compiler really evaluated main.
|
6

Your view of IO is good, but I have a problem with this line

Calls main to get back an "IO computation"

The best way to think about Haskell is that functions dont do anything. Rather, you declaratively describe what values are. A program consists of a description of an IO value called main. The only sense to which it "calls main" is that the declaration of main is reduced to Weak Head Normal Form (or something similar).

IO is the type of arbitrary side effect-full computations. The pure subset of Haskell is a purely declarative description of values, that happens to allow for undecidable descriptions. Think of Haskell as a mathematical language like set theory. Statements in set theory dont do anything, but they might involve complicated computations like "the smallest set which contains Akerman's_function(30)". They can also contain undecidable statements like "S = the set of all sets that do not contain themselves"

@amindfv is half right: main is not a "pure function". It is not a function at all. It is a value, defined by a pure reduction, encoding unpure computation.

2 Comments

I think the poster is aiming for more of a summary for imperative programmers. In Haskell we'd say "Evaluates" instead of "Calls", but otherwise it's pretty much the same. "IO computation" is also just a variant of "IO monad". I think in 'true Haskellspeak' we'd say the runtime "evaluates main to obtain a value in the IO monad", but this is just being pedantic. The original sentence works fine for explaining it to imperative programmers. :)
I actually think "IO computation" is preferable to "IO monad" most of the time. As to being pedantic: the poster's understanding was already pretty good, I wanted to dig in deeper. From my perspective, the most important thing about "thinking Haskelly" is not thinking of the language as "doing" but thinking about it as "being". "Calls" carries to much procedural baggage, and isn't necessarily true: see sacundim's description of main being "compiled" instead of "called". I think "evaluates" sticks to the denotation without brining in as much about how. The idea is a deeper understanding.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.