The System.IO docs contains a mysterious, undocumented function fixIO
. Its source only adds to the mystery:
fixIO :: (a -> IO a) -> IO a
fixIO k = do
m <- newEmptyMVar
ans <- unsafeInterleaveIO (takeMVar m)
result <- k ans
putMVar m result
return result
This appears to do the moral equivalent of dereferencing NULL (reading from an empty MVar). Indeed, trying it:
import System.IO
main = fixIO $ \x -> putStrLn x >> return x
results in an error "thread blocked indefinitely in an MVar operation"
Searching turns up nothing save a 15 year old message from Simon Peyton-Jones himself, in which he provides the above source, and hopes that it would make the meaning clear (and yet here I am).
Can someone please shed some light on this? What does fixIO do and when should I use it?
fixIO
is the IO
equivalent of fix
.
You've probably seen this definition of the fibonacci sequence:
fibs = 1 : 1 : zipWith (+) fibs (tail fibs)
which reuses the fibs
variable within the definition of fibs
to do corecursion. It works because we exploit laziness to define each element of fibs
before it needs to be used.
We can use fix
to do the same without having to define a variable, tying the knot for us:
fix $ \fibs -> 1 : 1 : zipWith (+) fibs (tail fibs)
Which is handy if you don't especially need to keep the entire fibonacci sequence, you just want to know its tenth element:
λ (fix $ \fibs -> 1 : 1 : zipWith (+) fibs (tail fibs)) !! 9
55
fixIO
is similar, except it lets you recurse on the output of an IO action. That's why you got your "thread blocked" error - you were using the corecursive result without defining it.
λ fmap (take 10) . fixIO $ \fibs -> putStrLn "computing fibs" >> return (1 : 1 : zipWith (+) fibs (tail fibs))
computing fibs
[1,1,2,3,5,8,13,21,34,55]
fixIO
is the witness to the MonadFix IO
instance. See the HaskellWiki page on MonadFix and the paper A Recursive do for Haskell.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With