[Haskell-cafe] On the purity of Haskell

Steve Horne sh006d3592 at blueyonder.co.uk
Thu Dec 29 21:16:06 CET 2011


On 29/12/2011 19:26, Heinrich Apfelmus wrote:
> Steve Horne wrote:
>> Heinrich Apfelmus wrote:
>>>
>>> Again, purity refers to the semantics of functions (at run-time): 
>>> given the same argument, will a function always return the same 
>>> result? The answer to this question solely decides whether the 
>>> language is pure or impure. Note that this depends on the meaning of 
>>> "function" within that language. In C, side-effects are part of the 
>>> semantics of functions, so it's an impure language. In Haskell, on 
>>> the other hand, functions will always return the same result, so the 
>>> language is pure. You could say that side effects have been moved 
>>> from functions to some other type (namely IO) in Haskell.
>>
>> Anyway, if you're using IO actions, your code is not referentially 
>> transparent and is therefore impure - by your own definition of 
>> "impure". Causing side-effects may not be pedantically the issue, but 
>> the mix of causing and reacting to them - ie interacting with the 
>> "outside" - clearly means that some of your function results are 
>> dependent on what's happening "outside" your program. That includes 
>> side-effects "outside" your program yet caused by program program.
>
> No, that's not my definition of "impure". Also, my Haskell code is 
> referentially transparent even though I'm using IO actions. If this 
> sounds paradoxical, then it's probably worth mulling about some more. 
> Maybe it helps to try to find an example of a function  f :: A -> B  
> for some cleverly chosen types A,B that is not pure, i.e. does not 
> return the same values for equal arguments.
That doesn't prove Haskell pure.

Of course your challenge looks like a safe one. It can't be done because 
the IO monad is a black box - you can't e.g. pattern-match on it's data 
constructors.

Of course you can extract values out of IO actions to work with them - 
the bind operator does this for you nicely, providing the value as an 
argument to the function you pass to the right-hand argument of the 
bind. But that function returns another IO action anyway - although 
you've extracted a value out and the value affects a computation, all 
you can do with it in the long run is return another IO action.

Even so, that value can only be extracted out at run-time, after the 
action is executed.

So, consider the following...

getAnIntFromTheUser :: IO Int

 From a pure functional point of view, that should return the same 
action every time. Well, the partially evaluated getAnIntFromTheUser has 
the same structure each time - but the actual Int packaged inside the 
action isn't decided until runtime, when the action is executed. At 
compile-time, that action can only be partially evaluated - the final 
value OF THE ACTION depends on what Int the user chooses to give because 
that Int is a part of the action value.

For your specific challenge, place that as a left-hand argument in a bind...

f :: Int -> IO Int
f = getAnIntFromTheUser >>= \i -> return (i+1)

Well, the value of i isn't decidable until runtime. The value of i+1 is 
not decidable until runtime. The value of return (i+1) is not decidable 
until runtime and so on. It can only be partially evaluated at 
compile-time, but when it is fully evaluated, you get a different IO 
action returned by f depending on what Int you got from the user.

And so we get right back to the 
referential-transparency-by-referencing-the-world-as-an-argument thing, 
I guess.




More information about the Haskell-Cafe mailing list