How about three? Right identity, left identity and associativity.
That's for the laws. For the properties, IO is a monad so I can compose any value in the IO monad with other monads (Maybe, ST, etc...).
How about three? Right identity, left identity and associativity.
That's for the laws. For the properties, IO is a monad so I can compose any value in the IO monad with other monads (Maybe, ST, etc...).
Actually, I kind of wish IO was split up a bit... I have been meaning to play with rolling some typeclasses (with an IO instance and a testing instance) that wrap up IO of various types, so I define my functions in terms of these and spot what kind of IO various functions might do - I'm not yet sure how much of a win it will be, but it seems like something to explore.
http://www.haskellforall.com/2012/07/purify-code-using-free-...
http://www.haskellforall.com/2012/06/you-could-have-invented...
{-# 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 >>= printThere's a section on this practice in Real World Haskell: http://book.realworldhaskell.org/read/programming-with-monad...
I would point out that in addition to the mentioned testing use (itself not to be underestimated - QuickCheck is amazing and making more things tractable to QuickCheck is great), there's also some other big potential wins. If I have a function that deals with local files in a monad that's an instance of MonadHandle, I can write an instance for a RemoteIO type that performs file operations on a remote system through a network connection, and suddenly the function that was local also works remotely.