
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.