packages feed

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 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+