[Haskell-cafe] On the purity of Haskell
Steve Horne
sh006d3592 at blueyonder.co.uk
Fri Dec 30 01:44:45 CET 2011
On 29/12/2011 23:30, Chris Smith wrote:
> Sorry to cut most of this out, but I'm trying to focus on the central
> point here.
>
> On Thu, 2011-12-29 at 22:01 +0000, Steve Horne wrote:
>> In pure functional terms, the result should be equivalent to a fully
>> evaluated value - but putStrLn isn't pure. It cannot be fully
>> evaluated until run-time.
> And here it is, I think. You're insisting on viewing the performing of
> some effect as part of the evaluation of an expression, even though the
> language is explicitly and intentionally designed not to conflate those
> two ideas. Effects do not happen as a side-effect of evaluating
> expressions. Instead they happen because you define the symbol 'main'
> to be the effect that you want to perform, and then set the runtime
> system to work on performing it by running your program.
So, to resurrect an example from earlier...
f :: Int -> IO Int
f = getAnIntFromTheUser >>= \i -> return (i+1)
Are you claiming that the expression (i+1) is evaluated without knowing
the value of i?
If not, at run-time your Haskell evaluates those expressions that
couldn't be fully evaluated at compile-time.
If you do, we're back to my original model. The value returned by main
at compile-time is an AST-like structure wrapped in an IO monad
instance. The "AST nodes" are partially evaluated functions, but since
they can't be evaluated in a sense that knows the precise result of that
+ operator, we say "this unevaluated function can be treated as a value
in itself - let's compose bigger ASTs out of smaller ones".
In that model, the 'i' above - the argument to the lambda - never gets
an Int value because it's really just a placeholder for tracking the
intended flow of data that won't actually flow until run-time.
But that AST is just a translated description of the same program. It
isn't the end result - it's just an intermediate step along the road to
being able to run the thing. The unevaluated function is just falling
back on returning a representation of its unevaluated self. That model
is still compiled to executable code. That executable code, when run,
still interacts with it's environment. That is as much an aspect of what
Haskell defines as the functional core.
Switching mental models doesn't change the logic any more than switching
number bases. They are different descriptions of the same thing. The
models are superficially different, but the logic is equivalent. It
really doesn't matter whether you call something an AST node or an
unevaluated function. An AST node can represent an unevaluated function.
An unevaluated function can be implemented as a closure - which is just
a collection of data, the same as an AST node. The two things are really
both *still* the exact same thing. Even when it's translated to binary
executable code, it is *still* the unevaluated function - right up until
it gets executed (and in the same moment, evaluated).
Either way, at run-time, Haskell is impure.
More information about the Haskell-Cafe
mailing list