This is a follow-up to a question I asked earlier. I am wondering if the way list is updated in IORef below in the accepted solution is O(1) or not, in every call of fetch. I suspect it is because IORef is likely just keeping a pointer to head of the list (instead of traversing and copying entire list which will be O(n) every time. Just changing the pointer to new head should be O(1), and should prevent eager evaluation of entire list). But, ghc-core won't show that low-level code. So, asking here:
mklstream :: L.ByteString -> (IO S.ByteString -> IO r) -> IO r
mklstream lbs sink = do
ref <- newIORef (L.toChunks lbs)
let fetch :: IO S.ByteString
fetch = do chunks <- readIORef ref
case chunks of
[] -> return S.empty
(c:cs) -> do writeIORef ref cs
return c
sink fetch
Yes, in GHC it is O(1). The things that are read and written from
IORefs are exactly the same pointers that everything else in the implementation uses as data representation. Indeed, you can know just from the type ofwriteIORefthat it does nothing special to its data:Because the
ais completely unconstrained,writeIORefcannot inspect the data and in particular cannot traverse any list you hand it. (This is not quite convincing -- the runtime can do anything it likes even with unconstrained types, and you might believe thatwriteIORefis a runtime primitive -- but it turns out to be true in this case anyway.)