Apparent redundant calls in a IO monad?

106 Views Asked by At

Here is a snippet of code taken from the Haskell GPipe project (commented by myself, save the line with "Really?"). In the memoize function, I don't understand why its author call the getter a second time to cache a newly computed value. It doesn't seem necessary to me and it can be removed without apparent bad consequences (at least, a medium-sized project of mine still works without it).

{- | A map (SN stands for stable name) to cache the results 'a' of computations 'm a'.
The type 'm' ends up being constrained to 'MonadIO m' in the various functions using it.
-}
newtype SNMap m a = SNMap (HT.BasicHashTable (StableName (m a)) a)

newSNMap :: IO (SNMap m a)
newSNMap = SNMap <$> HT.new

memoize :: MonadIO m
    => m (SNMap m a) -- ^ A "IO call" to retrieve our cache.
    -> m a -- ^ The "IO call" to execute and cache the result.
    -> m a -- ^ The result being naturally also returned.
memoize getter m = do
    s <- liftIO $ makeStableName $! m -- Does forcing the evaluation make sense here (since we try to avoid it...)?
    SNMap h <- getter
    x <- liftIO $ HT.lookup h s
    case x of
        Just a -> return a
        Nothing -> do
            a <- m
            SNMap h' <- getter -- Need to redo because of scope. <- Really?
            liftIO $ HT.insert h' s a
            return a
1

There are 1 best solutions below

0
On

I get it. The scope term used is not related to the Haskell 'do' scope. It is simply that a computation could recursively update the cache when evaluated (as in the scopedM function in the same module...). It is kind of obvious in retrospect.