EDIT:
if all you need is to see the sequence of values that led to fib n
, you should just use something like:
fibs = 0 : 1 : zipWith (+) fibs (tail fibs)
fib n = (take n fibs, fibs !! n)
which works like this:
*Main> fib 0
([],0)
*Main> fib 1
([0],1)
*Main> fib 10
([0,1,1,2,3,5,8,13,21,34],55)
If you want, you can use the State monad to separate out the list aspect of the computation but it would only mean a list that is "global" in a limited scope (e.g. the main
function). That's the closest you can get to whay you want while staying in proper Haskell land.
The Right Way
I'm assuming you're attempting this for purposes of momoization? If yes, basically there already is a global list under the hood of any list-based implementation of fib
— any callers of fib
use the same lazily evaluated list that the Haskell (e.g. GHC) runtime uses under the hood; any elements of that list that have already been computed are not going to be re-computed.
fibs = 0 : 1 : zipWith (+) fibs (tail fibs)
— here, fibs
is a global list, and whereas you can't explicitly/manually mutate it, by accessing individual elements in it (fibs !! n
), you are indirectly causing the underlying list to be mutated as the runtime adds new values to it.
The Wrong Way (but go ahead if you're not going to use this in real code)
If, on the other hand, you need the list for the sake of just having that list, e.g. for debugging or some other weird reason, you can use a really ugly and unsafe hack such as the following IORef
based solution, but please remember to stick to function names that end in Unsafe
, in the spirit of how the stdlib is marking all of the unsafe calls so that people who use them are aware of the dangers:
import Data.IORef
import System.IO.Unsafe (unsafePerformIO)
fibsStorageUnsafe :: IORef [Integer]
fibsStorageUnsafe = unsafePerformIO $ newIORef []
-- this is one one that uses the pure definition
-- above to compute the value and then store it
-- in a global, mutable IORef
fibUnsafe n = unsafePerformIO (updateFibs ret) `seq` ret
where
ret = fib' n
updateFibs x = do
old <- readIORef fibsStorageUnsafe
writeIORef fibsStorageUnsafe (x:old)
fib' :: Int -> Integer
fib' 0 = 0
fib' 1 = 1
-- you can recursively call fib' instead of fibUnsafe
-- to only log the "top level" (non-recursive)
-- calls to fibUnsafe and not all recursive calls
fib' n = fibUnsafe (n-1) + fibUnsafe (n-2)
which works like this:
*Main> readIORef fibsStorageUnsafe
[]
*Main> fibUnsafe 0
0
*Main> readIORef fibsStorageUnsafe
[0]
*Main> fibUnsafe 1
1
*Main> readIORef fibsStorageUnsafe
[1,0]
*Main> fibUnsafe 3
2
*Main> readIORef fibsStorageUnsafe
[1,0,1,1,2,1,0]
*Main> fibUnsafe 10
55
*Main> readIORef fibsStorageUnsafe
[1,0,1,1,2,1,0,1,3,1,0,1,1,2,5,1,0,1,1,2,1,0,1,3,8,1,0,1,1,2,1,0,1,3,1,0,1,1,2,5,13,1,0,1,1,2,1,0,1,3,1,0,1,1,2,5,1,0,1,1,2,1,0,1,3,8,21,1,0,1,1,2,1,0,1,3,1,0,1,1,2,5,1,0,1,1,2,1,0,1,3,8,1,0,1,1,2,1,0,1,3,1,0,1,1,2,5,13,34,1,0,1,1,2,1,0,1,3,1,0,1,1,2,5,1,0,1,1,2,1,0,1,3,8,1,0,1,1,2,1,0,1,3,1,0,1,1,2,5,13,1,0,1,1,2,1,0,1,3,1,0,1,1,2,5,1,0,1,1,2,1,0,1,3,8,21,55,1,0,1,1,2,1,0]
You can adjust the definitions of fibUnsafe
, fib'
and other components to fit your goals, but again: I'd strongly recommend against using an approach like this, unless it's solely for learning or experimentation purposes.
For example, if you modify fib'
to call itself not fibUnsafe
like this:
fib' n = fib' (n-1) + fib' (n-2)
then calling fibUnsafe 10
will yield the following contents for fibStorageUnsafe
instead of what you're seeing above:
*Main> fibUnsafe 10
55
*Main> readIORef fibsStorageUnsafe
[55]
DISCLAIMER: I don't even know if the IORef
based code above will work correctly under all circumstances — I admit I'm not too familiar with IORef
s and how unsafePerformIO
works to give any guarantees as to the semantics of the code above.