[Haskell-cafe] Simple but interesting (for me) problem
minh thu
noteed at gmail.com
Thu Oct 22 03:48:24 EDT 2009
2009/10/21 Gregory Crosswhite <gcross at phys.washington.edu>:
> And just because this has not been explicitly stated: it's not just for
> aesthetic reasons that you couldn't do this with a pure function, but
> because it violates the semantics and gets you the wrong result. So for
> example, if you modified Tim's code to be
>
> import Data.IORef
> import System.IO.Unsafe
> mkNext :: (Num a) => IO a
> mkNext = do
> ref <- newIORef 0
> return . unsafePerformIO $
> do
> modifyIORef ref (+1)
> readIORef ref
> main :: IO ()
> main = do
> foo <- mkNext
> print foo
> print foo
> print foo
>
> Then the output that you will see (with GHC at least) is
> 1
> 1
> 1
> because the compiler assumes that it only needs to evaluate foo once, after
> which it can cache the result due to assumed referential transparency.
> - Greg
This is indeed wrong, but not how you think it is.
The code you pass to unsafePerformIO has type Num a => IO a, so the
value passed to return has type Num a. So foo has type Num a too and
its value is 1.
Exactly like in
mkNext = do
ref <- newIORef 0
modifyIORef ref (+1)
readIORef ref
which is a complicated way to write
mkNext = return 1
Now, it's clear that foo has value 1 and printing it three times
should output three 1. The whole point of having mkNext return an
action (that should be called next, and not foo, as it is much
clearer) in previous code was too be able to execute it multiple times
and having it return a new value.
In general, expecting
print bar
print bar
print bar
outputing three different things is wrong, as bar should be pure. If
bar is not pure, then it should be
a <- bar
print a
b <- bar
print b
c <- bar
print c
Cheers,
Thu
More information about the Haskell-Cafe
mailing list