2009/10/21 Gregory Crosswhite <[email protected]>: > 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 _______________________________________________ Haskell-Cafe mailing list [email protected] http://www.haskell.org/mailman/listinfo/haskell-cafe
