Pointers in Haskell
Haskell supports references, allowing you to pass mutable values within your program. However, the concept is quite different from pointers in imperative languages.
import Data.IORef
-- We'll show how mutable references work with 2 functions:
-- 'zeroVal' and 'zeroRef'. 'zeroVal' takes an Int, so it
-- receives a copy of the value.
zeroVal :: Int -> IO ()
zeroVal val = do
putStrLn $ "Inside zeroVal: " ++ show val
-- This modification is local and won't affect the original value
-- 'zeroRef' takes an IORef Int, which is a mutable reference to an Int.
-- It can modify the value at the referenced location.
zeroRef :: IORef Int -> IO ()
zeroRef ref = do
writeIORef ref 0
val <- readIORef ref
putStrLn $ "Inside zeroRef: " ++ show val
main :: IO ()
main = do
-- Create a new mutable reference
i <- newIORef 1
putStr "initial: "
readIORef i >>= print
-- Pass the value to zeroVal
readIORef i >>= zeroVal
putStr "after zeroVal: "
readIORef i >>= print
-- Pass the reference to zeroRef
zeroRef i
putStr "after zeroRef: "
readIORef i >>= print
-- We can print the "address" of the reference, but it's not as
-- straightforward as in languages with explicit pointers
putStr "reference: "
print i
In this Haskell version, we use IORef
to create mutable references, which is the closest equivalent to pointers in a pure functional language like Haskell.
zeroVal
doesn’t change the value in main
, but zeroRef
does because it has a reference to the mutable location.
When you run this program, you’ll see output similar to:
$ runhaskell pointers.hs
initial: 1
Inside zeroVal: 1
after zeroVal: 1
Inside zeroRef: 0
after zeroRef: 0
reference: IORef (0x...)
Note that Haskell’s approach to mutability is quite different from imperative languages. In Haskell, we explicitly use IORef
to create mutable references, and all operations on these references are performed within the IO
monad to maintain purity in the rest of the program.
The concept of dereferencing doesn’t exist in the same way as in languages with explicit pointers. Instead, we use readIORef
to get the value from a reference and writeIORef
to modify it.
Haskell’s type system and functional nature make it less common and often unnecessary to use mutable references, as immutable data structures and pure functions are preferred for most tasks.