[Haskell-cafe] Re: Can we come out of a monad?

Dan Doel dan.doel at gmail.com
Wed Aug 11 14:47:21 EDT 2010


On Wednesday 11 August 2010 9:49:07 am mokus at deepbondi.net wrote:
> The mixture is not as free as some would like; the fact that Haskell has
> this distinction between monadic actions and pure values (and the fact
> that the former can be manipulated as an instance of the latter) means
> that the programmer must specify whether to evaluate ("=") or execute
> ("<-") an action, which is a source of endless confusion for beginners and
> debate over what "pure" means.  I don't expect I'll put an end to either,
> but I would like to point out anyway that, if you accept that distinction
> (the reality of which is attested by the existence of a computable
> function - the type checker - for making the distinction), it's fairly
> easy to see that evaluation is always pure, excepting abuse of
> unsafePerformIO, et al., and execution is not.  Both occur in the context
> of do-notation.  Functions returning monadic actions (whether the
> resulting action is being evaluated or executed) are still always
> evaluated to yield an action.  That evaluation is pure.  The execution of
> the action yielded may not be, nor should it have to be - that's the whole
> point of IO!  But we still have as much purity as is actually possible,
> because we know exactly where _execution_ occurs and we don't pretend it
> doesn't by confusing definition with assignment.  "=" always means "=" in
> Haskell, and "<-" doesn't.  In C, "=" always means "<-", even when the RHS
> is a simple variable reference (consider "x = x;").

This is the important point, I think. Some folks were arguing in #haskell the 
other day about whether BASIC could be viewed as 'pure,' since it's so simple, 
it's almost like writing a big IO block. If you go to Sabry's[1] definition of 
purity, then you could argue that "independence of evaluation order" is 
trivially satisfied, because there is no "evaluation" only "execution" as 
people call it.

But I think that side-steps something, in that "pure" on its own isn't 
interesting, certainly if it applies to BASIC that way. To be interesting, you 
have to look at the whole Sabry thesis, which is "what is a pure *functional* 
language?" For the second part of that, he identifies the requirement that 
your language have some sort of lambda calculus (possibly one enriched with 
datatypes, let, etc. as Haskell does) as a sublanguage.

It is only at that point that purity becomes interesting. A plain lambda 
calculus has certain nice, equational properties to its evaluation. We can 
inline or abstract out arbitrary expressions without changing the meaning of 
the program (at least, up to nontermination). The point of remaining "pure," 
then, is to preserve this aspect of the lambda calculus portion of the 
language. This obviously means we can't just add rand :: () -> Int, because 
then:

  let x = rand () in x + x      /=      rand () + rand ()

and that breaks the substitutional nature of the lambda calculus portion of 
the language (and it's why unsafePerformIO is clearly impure in this sense).

Instead, Haskell has a DSL for writing down the sort of effectful programs we 
want to write in practice, and the expressions in the DSL are first-class in 
the lambda calculus portion of the language. You can say that from the view 
internal to the DSL, inlining and abstraction are invalid, because:

  rand >>= \x -> x + x      /=      rand >>= \x -> rand >>= \y -> x + y

but the important part (at least, for a lot of people) is that we've preserved 
the property we want for the lambda calculus, which can be used to write large 
portions of the program.

Now, I don't think that this is necessarily tied to functional programming and 
the lambda calculus. There are probably analogous calculi for logic 
programming, and one could attempt to preserve its nice properties while 
adding in a way to do effects for 'real programs', and so on. But, to get back 
to BASIC, or C, if the language you're extending is an empty language that 
does nothing, then remaining pure to it isn't interesting. I can't actually 
write significant portions of my program in such a language, so all I'm left 
with is the DSL, which doesn't (internally) have the nice properties.

(The same applies to the C preprocessor, if you want to try that route. It is 
not a fragment of the language (even granting that it's a fragment at all) 
useful for doing actual work in the program---writing actual programs in the 
preprocessor involves files #including themselves for recursion, and is well 
in the esoteric category; it is entirely for assembling 'DSL' terms which will 
do all the actual work.)

-- Dan

[1] http://citeseerx.ist.psu.edu/viewdoc/summary?doi=10.1.1.27.7800


More information about the Haskell-Cafe mailing list