Hacker Newsnew | comments | show | ask | jobs | submit login

I've seen them created a few times. I'm pretty sure there are at least a few implementations in Hackage of such divided IO monads. More generally, the haute method for solving this would be to use Operational or Free monads to create your restricted monad type and then interpret it into IO.



I think optimally, with a set of typeclasses, one could define instances for IO and just use things with no interpretation or build a datastructure with free monads to perform whatever other inspections or manipulations you want. There could be some incompatability between the two notions that I'm missing, though...


I think I've seen the typeclass route as well before. It may have just been a blog post and not a library implementation, though. The two methods have different epistemological ideas, but could be combined. For instance:

    {-# LANGUAGE DeriveFunctor, GeneralizedNewtypeDeriving, FlexibleInstances #-}
    module CrWr where
    import Control.Monad.Free
    import Data.IORef
    data RWRefF t a = NewRef t (IORef t -> a) | PeekRef (IORef t) (t -> a) | PutRef (IORef t) t a
                    deriving Functor
    newtype RWRef t a = RWRef (Free (RWRefF t) a) deriving Monad
    newRef :: t -> RWRef t (IORef t)
    newRef t = RWRef $ liftF $ NewRef t id
    peekRef :: IORef t -> RWRef t t
    peekRef r = RWRef $  liftF $ PeekRef r id
    putRef :: IORef t -> t -> RWRef t ()
    putRef r t = RWRef $ liftF $ PutRef r t ()
    interpret :: RWRef t a -> IO a
    interpret (RWRef (Pure a)) = return a
    interpret (RWRef (Free (NewRef t f))) = newIORef t >>= interpret . RWRef . f
    interpret (RWRef (Free (PeekRef r f))) = readIORef r >>= interpret . RWRef . f
    interpret (RWRef (Free (PutRef r t next))) = writeIORef r t >> interpret (RWRef next)
    -- Highly specialized class of IO monads
    class Monad m => RWIntRefMonad m where
      new  :: Int -> m (IORef Int)
      peek :: IORef Int -> m Int
      put  :: IORef Int -> Int -> m ()
      toIO :: m a -> IO a
    instance RWIntRefMonad (RWRef Int) where
      new = newRef
      peek = peekRef
      put = putRef
      toIO = interpret
    incr :: RWIntRefMonad m => IORef Int -> m ()
    incr r = peek r >>= put r . (+1)
    main = do r <- newIORef 0
              -- We need to specialize before the m gets erased
              toIO (incr r :: RWRef Int ())
              readIORef r >>= print


Guidelines | FAQ | Support | API | Security | Lists | Bookmarklet | DMCA | Apply to YC | Contact