Haskell: generic IORef, MVar?
Asked Answered
V

1

6

I made the following function which is specific for the IO monad:

memoIO :: MonadIO m => m a -> IO (m a)
memoIO action = do
  ref <- newMVar Nothing
  return $ do
    x <- maybe action return =<< liftIO (takeMVar ref)
    liftIO . putMVar ref $ Just x
    return x

Example usage:

main :: IO ()
main = do
  p <- memoIO $ putStrLn "hello"
  p
  p

Prints "hello" once.

I would like (a pet peeve) to make it work for as many cases as possible (not just in IO).

I found stateref on hackage and with it my code looks like this:

{-# LANGUAGE FlexibleContexts, FlexibleInstances, MultiParamTypeClasses, Rank2Types, UndecidableInstances #-}

import Data.MRef

class (NewMRef r m a, DefaultMRef r m a, PutMRef r m a, TakeMRef r m a) => MRef r m a
instance (NewMRef r m a, DefaultMRef r m a, PutMRef r m a, TakeMRef r m a) => MRef r m a

memo :: (MRef r m (Maybe a), Monad s) => (forall x. m x -> s x) -> s a -> m (s a)
memo liftFunc action = do
  ref <- newDefaultMRef Nothing
  return $ do
    x <- maybe action return =<< liftFunc (takeDefaultMRef ref)
    liftFunc . putDefaultMRef ref $ Just x
    return x

Is there an alternative for stateref or a better way to use it than I did?

Vitovitoria answered 7/7, 2009 at 10:47 Comment(4)
It looks like you are trying to re-invent part of sebfisch.github.com/explicit-sharing ? (Not a bad thing, I'd just like to clarify.)Thrave
I don't think "peeve" means what you think it means.Shrunk
@ephemient: I know about explicit-sharing. But as I understand it will require my code to run inside the sharing monad transformer, which won't fit to GLUT callbacks (plain IO) unless I make it run in another thread.Vitovitoria
@ShreevatasaR: I double-checked with wikipedia: "A pet peeve is a minor annoyance that one identifies as particularly annoying to them". I get a minor annoyance from non-generic code.Vitovitoria
M
7

I've rewritten a cheesy little MonadRef class on a few separate occasions for my own personal use and someone probably has one on Hackage, but I can't find one that is unencumbered with other baggage.

class Monad m => MonadRef m where
    type Ref m :: * -> *
    newRef :: a -> Ref m a
    writeRef :: Ref m a -> -> m ()
    readRef :: Ref m a -> m a

instance MonadRef IO where
    type Ref IO = IORef
    newRef = newIORef
    writeRef = writeIORef
    readRef = writeIORef

instance MonadRef STM where
    type Ref STM = TVar
    ...


instance MonadRef (ST s) where
    type Ref (ST s) = STRef s
    ...

Then it is easy to abstract away your memoization routine (though you probably want to replace IORef in this context with an MVar.)

[Edit: clarified verbage]

Matazzoni answered 7/7, 2009 at 15:51 Comment(3)
Hackage has similar implementations in the "TypeCompose" and "ArrayRef" packages (found using hayoo)Vitovitoria
Er I suppose self-contained was the wrong term, I meant 'without a bunch of other baggage', so perhaps 'unencumbered with other baggage' would be more appropriate. =)Matazzoni
anyhow, I guess I'll go with generalizing it only if the need actually arises.. :)Vitovitoria

© 2022 - 2024 — McMap. All rights reserved.