[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