tardis 0.2.0.0 → 0.3.0.0
raw patch · 4 files changed
+255/−9 lines, 4 filesPVP ok
version bump matches the API change (PVP)
API changes (from Hackage documentation)
Files
- Control/Monad/Tardis.hs +110/−1
- Control/Monad/Tardis/Class.hs +50/−3
- Control/Monad/Trans/Tardis.hs +78/−3
- tardis.cabal +17/−2
Control/Monad/Tardis.hs view
@@ -12,9 +12,25 @@ {-# LANGUAGE OverlappingInstances #-} #endif +-- | This module re-exports both 'MonadTardis' and 'TardisT'+-- (Wherever there is overlap, the 'MonadTardis' version is preferred.),+-- as well as the 'TardisT' instance of 'MonadTardis'.+-- If you installed this library with the use-undecidable-instances flag,+-- then another instance was also exported:+-- any 'MonadTrans' on top of any 'MonadTardis'+-- is also a 'MonadTardis'.+-- +-- The recommended usage of a Tardis is to import this module. module Control.Monad.Tardis- ( module Control.Monad.Trans.Tardis+ ( -- * Re-exports+ module Control.Monad.Trans.Tardis , module Control.Monad.Tardis.Class++ -- * What is a Tardis?+ -- $whatis+ + -- * How do you use a Tardis?+ -- $howuse ) where @@ -66,4 +82,97 @@ sendFuture = lift . sendFuture tardis = lift . tardis #endif++{- $whatis+ A Tardis is the combination of the State monad transformer+ and the Reverse State monad transformer.+ + The State monad transformer features a forwards-traveling state.+ You can retrieve the current value of the state,+ and you can set its value, affecting any future attempts+ to retrieve it.++ The Reverse State monad transformer is just the opposite:+ it features a backwards-traveling state.+ You can retrieve the current value of the state,+ and you can set its value, affecting any /past/ attempts+ to retrieve it. This is a bit weirder than its+ forwards-traveling counterpart, so its Monad instance+ additionally requires that the underlying Monad it transforms+ must be an instance of MonadFix.++ A Tardis is nothing more than mashing these two things together.+ A Tardis gives you /two/ states: one which travels /backwards/+ (or /upwards/) through your code (referred to as @bw@),+ and one which travels /forwards/ (or /downwards/) through your code+ (referred to as @fw@). You can retrieve the current+ value of either state, and you can set the value of either state.+ Setting the forwards-traveling state will affect the /future/,+ while setting the backwards-traveling state will affect the /past/.+ Take a look at how Monadic bind is implemented for 'TardisT':++> m >>= f = TardisT $ \ ~(bw, fw) -> do+> rec (x, ~(bw'', fw' )) <- runTardisT m (bw', fw)+> (x', ~(bw' , fw'')) <- runTardisT (f x) (bw, fw')+> return (x', (bw'', fw''))++ Like the Reverse State monad transformer, TardisT's Monad instance+ requires that the monad it transforms is an instance of MonadFix,+ as is evidenced by the use of @rec@.+ Notice how the forwards-traveling state travels /normally/:+ first it is fed to @m@, producing @fw'@, and then it is fed to @f x@,+ producing @fw''@. The backwards-traveling state travels in the opposite+ direction: first it is fed to @f x@, producing @bw'@, and then+ it is fed to @m@, producing @bw''@.++-}++{- $howuse+ A Tardis provides four primitive operations,+ corresponding to the /get/ and /put/ for each of its two states.+ The most concise way to explain it is this:+ 'getPast' retrieves the value from the latest 'sendFuture',+ while 'getFuture' retrieves the value from the next 'sendPast'.+ Beware the pitfall of performing send and get in the wrong order.+ Let's consider forwards-traveling state:++> do sendFuture "foo"+> x <- getPast++ In this code snippet, @x@ will be @\"foo\"@, because 'getPast'+ grabs the value from the latest 'sendFuture'. If you wanted+ to observe that state /before/ overwriting it with @\"foo\"@,+ then re-arrange the code so that 'getPast' happens earlier+ than 'sendFuture'. Now let's consider backwards-traveling state:++> do x <- getFuture+> sendPast "bar"++ In this code snippet, @x@ will be @\"bar\"@, because 'getFuture'+ grabs the value from the next 'sendPast'. If you wanted+ to observe that state /before/ overwriting it with @\"bar\"@,+ then re-arrange the code so that 'getFuture' happens later+ than 'sendPast'.++ TardisT is an instance of MonadFix. This is especially important+ when attempting to write backwards-traveling code, because+ the name binding occurs later than its usage.+ The result of the following code will be @(11, \"Dan Burton\")@.++> flip execTardis (10, "Dan") $ do+> name <- getPast+> sendFuture (name ++ " Burton")+> rec+> sendPast (score + 1)+> score <- getFuture+> return ()++ To avoid using @rec@, you may find 'modifyBackwards' to be useful.+ This code is equivalent to the previous example:++> flip execTardis (10, "Dan") $ do+> modifyForwards (++ "Burton")+> modifyBackwards (+ 1)++-}
Control/Monad/Tardis/Class.hs view
@@ -3,8 +3,16 @@ {-# LANGUAGE MultiParamTypeClasses #-} {-# LANGUAGE FunctionalDependencies #-} +-- | The class definition of a Tardis,+-- as well as a few straightforward combinators+-- based on its primitives.+-- +-- See Control.Monad.Tardis for the general explanation+-- of what a Tardis is and how to use it. module Control.Monad.Tardis.Class- ( MonadTardis (..)+ ( -- * The MonadTardis class+ MonadTardis (..)+ -- * Composite Tardis operations , modifyForwards , modifyBackwards , getsPast@@ -14,10 +22,39 @@ import Control.Applicative import Control.Monad.Fix +-- | A Tardis is parameterized by two state streams:+-- a 'backwards-traveling' state and a 'forwards-traveling' state.+-- This library consistently puts the backwards-traveling state first+-- whenever the two are seen together.+-- +-- Minimal complete definition:+-- ("tardis") or+-- ("getPast", "getFuture", "sendPast", and "sendFuture"). class (Applicative m, MonadFix m) => MonadTardis bw fw m | m -> bw, m -> fw where+ -- | Retrieve the current value of the 'forwards-traveling' state,+ -- which therefore came forwards from the past.+ -- You can think of forwards-traveling state as traveling+ -- 'downwards' through your code. getPast :: m fw+ + -- | Retrieve the current value of the 'backwards-traveling' state,+ -- which therefore came backwards from the future.+ -- You can think of backwards-traveling state as traveling+ -- 'upwards' through your code. getFuture :: m bw+ + -- | Set the current value of the 'backwards-traveling' state,+ -- which will therefore be sent backwards to the past.+ -- This value can be retrieved by calls to "getFuture"+ -- located 'above' the current location,+ -- unless it is overwritten by an intervening "sendPast". sendPast :: bw -> m ()+ + -- | Set the current value of the 'forwards-traveling' state,+ -- which will therefore be sent forwards to the future.+ -- This value can be retrieved by calls to "getPast"+ -- located 'below' the current location,+ -- unless it is overwritten by an intervening "sendFuture". sendFuture :: fw -> m () getPast = tardis $ \ ~(bw, fw) -> (fw, (bw, fw))@@ -25,6 +62,7 @@ sendPast bw' = tardis $ \ ~(_bw, fw) -> ((), (bw', fw)) sendFuture fw' = tardis $ \ ~(bw, _fw) -> ((), (bw, fw')) + -- | A Tardis is merely a pure state transformation. tardis :: ((bw, fw) -> (a, (bw, fw))) -> m a tardis f = do rec@@ -35,16 +73,25 @@ sendFuture past' return a -+-- | Modify the forwards-traveling state+-- as it passes through from past to future. modifyForwards :: MonadTardis bw fw m => (fw -> fw) -> m () modifyForwards f = getPast >>= sendFuture . f +-- | Modify the backwards-traveling state+-- as it passes through from future to past. modifyBackwards :: MonadTardis bw fw m => (bw -> bw) -> m ()-modifyBackwards f = getFuture >>= sendPast . f+modifyBackwards f = do+ rec+ sendPast (f x)+ x <- getFuture+ return () +-- | Retrieve a specific view of the forwards-traveling state. getsPast :: MonadTardis bw fw m => (fw -> a) -> m a getsPast f = f <$> getPast +-- | Retrieve a specific view of the backwards-traveling state. getsFuture :: MonadTardis bw fw m => (bw -> a) -> m a getsFuture f = f <$> getFuture
Control/Monad/Trans/Tardis.hs view
@@ -1,17 +1,26 @@ {-# OPTIONS_GHC -Wall #-} {-# LANGUAGE DoRec #-} +-- | The data definition of a "TardisT"+-- as well as its primitive operations,+-- and straightforward combinators based on the primitives.+-- +-- See Control.Monad.Tardis for the general explanation+-- of what a Tardis is and how to use it. module Control.Monad.Trans.Tardis (+ -- * The Tardis monad transformer TardisT , runTardisT , evalTardisT , execTardisT + -- * The Tardis monad , Tardis , runTardis , evalTardis , execTardis + -- * Primitive Tardis operations , tardis , getPast@@ -19,12 +28,14 @@ , sendPast , sendFuture + -- * Composite Tardis operations , modifyForwards , modifyBackwards , getsPast , getsFuture + -- * Other , noState ) where @@ -35,10 +46,26 @@ -- Definition -------------------------------------------------++-- | A TardisT is parameterized by two state streams:+-- a 'backwards-traveling' state and a 'forwards-traveling' state.+-- This library consistently puts the backwards-traveling state first+-- whenever the two are seen together. newtype TardisT bw fw m a = TardisT- { runTardisT :: (bw, fw) -> m (a, (bw, fw)) }+ { runTardisT :: (bw, fw) -> m (a, (bw, fw))+ -- ^ A TardisT is merely an effectful state transformation+ }++-- | Using a Tardis with no monad underneath+-- will prove to be most common use case.+-- Practical uses of a TardisT require that the+-- underlying monad be an instance of MonadFix,+-- but note that the IO instance of MonadFix+-- is almost certainly unsuitable for use with+-- Tardis code. type Tardis bw fw = TardisT bw fw Identity +-- | A Tardis is merely a pure state transformation. runTardis :: Tardis bw fw a -> (bw, fw) -> (a, (bw, fw)) runTardis m = runIdentity . runTardisT m @@ -46,19 +73,36 @@ -- Helpers ------------------------------------------------- +-- | Run a Tardis, and discard the final state,+-- observing only the resultant value. evalTardisT :: Monad m => TardisT bw fw m a -> (bw, fw) -> m a evalTardisT t s = fst `liftM` runTardisT t s +-- | Run a Tardis, and discard the resultant value,+-- observing only the final state (of both streams).+-- Note that the 'final' state of the backwards-traveling state+-- is the state it reaches by traveling from the 'bottom'+-- of your code to the 'top'. execTardisT :: Monad m => TardisT bw fw m a -> (bw, fw) -> m (bw, fw) execTardisT t s = snd `liftM` runTardisT t s ++-- | Run a Tardis, and discard the final state,+-- observing only the resultant value. evalTardis :: Tardis bw fw a -> (bw, fw) -> a evalTardis t = runIdentity . evalTardisT t +-- | Run a Tardis, and discard the resultant value,+-- observing only the final state (of both streams). execTardis :: Tardis bw fw a -> (bw, fw) -> (bw, fw) execTardis t = runIdentity . execTardisT t +-- | Some Tardises never observe the 'initial' state+-- of either state stream, so it is convenient+-- to simply hand dummy values to such Tardises.+-- +-- > noState = (undefined, undefined) noState :: (a, b) noState = (undefined, undefined) @@ -95,33 +139,64 @@ -- Basics ------------------------------------------------- +-- | From a stateful computation, construct a Tardis.+-- This is the pure parallel to the constructor "TardisT",+-- and is polymorphic in the transformed monad. tardis :: Monad m => ((bw, fw) -> (a, (bw, fw))) -> TardisT bw fw m a tardis f = TardisT $ \s -> return (f s) -+-- | Retrieve the current value of the 'forwards-traveling' state,+-- which therefore came forwards from the past.+-- You can think of forwards-traveling state as traveling+-- 'downwards' through your code. getPast :: Monad m => TardisT bw fw m fw getPast = tardis $ \ ~(bw, fw) -> (fw, (bw, fw)) +-- | Retrieve the current value of the 'backwards-traveling' state,+-- which therefore came backwards from the future.+-- You can think of backwards-traveling state as traveling+-- 'upwards' through your code. getFuture :: Monad m => TardisT bw fw m bw getFuture = tardis $ \ ~(bw, fw) -> (bw, (bw, fw)) +-- | Set the current value of the 'backwards-traveling' state,+-- which will therefore be sent backwards to the past.+-- This value can be retrieved by calls to "getFuture"+-- located 'above' the current location,+-- unless it is overwritten by an intervening "sendPast". sendPast :: Monad m => bw -> TardisT bw fw m () sendPast bw' = tardis $ \ ~(_bw, fw) -> ((), (bw', fw)) +-- | Set the current value of the 'forwards-traveling' state,+-- which will therefore be sent forwards to the future.+-- This value can be retrieved by calls to "getPast"+-- located 'below' the current location,+-- unless it is overwritten by an intervening "sendFuture". sendFuture :: Monad m => fw -> TardisT bw fw m () sendFuture fw' = tardis $ \ ~(bw, _fw) -> ((), (bw, fw')) +-- | Modify the forwards-traveling state+-- as it passes through from past to future. modifyForwards :: MonadFix m => (fw -> fw) -> TardisT bw fw m () modifyForwards f = getPast >>= sendFuture . f +-- | Modify the backwards-traveling state+-- as it passes through from future to past. modifyBackwards :: MonadFix m => (bw -> bw) -> TardisT bw fw m ()-modifyBackwards f = getFuture >>= sendPast . f+modifyBackwards f = do+ rec+ sendPast (f x)+ x <- getFuture+ return () +-- | Retrieve a specific view of the forwards-traveling state. getsPast :: MonadFix m => (fw -> a) -> TardisT bw fw m a getsPast f = fmap f getPast ++-- | Retrieve a specific view of the backwards-traveling state. getsFuture :: MonadFix m => (bw -> a) -> TardisT bw fw m a getsFuture f = fmap f getFuture
tardis.cabal view
@@ -1,5 +1,5 @@ name: tardis-version: 0.2.0.0+version: 0.3.0.0 synopsis: Bidirectional state monad transformer homepage: https://github.com/DanBurton/tardis bug-reports: https://github.com/DanBurton/tardis/issues@@ -11,6 +11,20 @@ build-type: Simple cabal-version: >=1.8 +description:+ A Tardis is a combination of both a forwards and a backwards+ state transformer, providing two state values that \"travel\"+ in opposite directions.+ .+ You can install this library with the @use-undecidable-instances@ flag,+ but this feature is only provided for toying around.+ If you depend on this library, or install any libraries that+ depend on this library, then you should install this library /without/+ that flag.+ .+ A detailed description of what a Tardis is and how to use it+ can be found in the documentation for Control.Monad.Tardis.+ Flag use-undecidable-instances description: Include additional instances for MonadTardis that require some sketchy language extensions@@ -36,4 +50,5 @@ source-repository this type: git location: git://github.com/DanBurton/tardis.git- tag: tardis-0.2.0.0+ tag: tardis-0.3.0.0+