[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