packages feed

tardis 0.4.2.0 → 0.4.3.0

raw patch · 9 files changed

+532/−449 lines, 9 filesdep +tardisPVP ok

version bump matches the API change (PVP)

Dependencies added: tardis

API changes (from Hackage documentation)

Files

− Control/Monad/Tardis.hs
@@ -1,128 +0,0 @@-{-# OPTIONS_GHC -Wall #-}---- | This module re-exports both 'MonadTardis' and 'TardisT'--- (Wherever there is overlap, the 'MonadTardis' version is preferred.)--- --- The recommended usage of a Tardis is to import this module.-module Control.Monad.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---import Control.Monad.Tardis.Class-import Control.Monad.Trans.Tardis-  ( TardisT-  , runTardisT-  , evalTardisT-  , execTardisT--  , Tardis-  , runTardis-  , evalTardis-  , execTardis-  -  , noState-  )---{- $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
@@ -1,108 +0,0 @@-{-# OPTIONS_GHC -Wall -fno-warn-warnings-deprecations   #-}-{-# LANGUAGE DoRec                           #-}-{-# LANGUAGE MultiParamTypeClasses  #-}-{-# LANGUAGE FunctionalDependencies #-}-{-# LANGUAGE FlexibleInstances      #-}----- | 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-  ( -- * The MonadTardis class-    MonadTardis (..)-    -- * Composite Tardis operations-  , modifyForwards-  , modifyBackwards-  , getsPast-  , getsFuture-  ) where--import Control.Applicative-import Control.Monad.Fix--import qualified Control.Monad.Trans.Tardis as T---- | 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))-  getFuture      = tardis $ \ ~(bw, fw)  -> (bw, (bw, fw))-  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-      let (a, (future', past')) = f (future, past)-      sendPast future'-      past <- getPast-      future <- getFuture-      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 = 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---instance MonadFix m => MonadTardis bw fw (T.TardisT bw fw m) where-  getPast    = T.getPast-  getFuture  = T.getFuture-  sendPast   = T.sendPast-  sendFuture = T.sendFuture-  tardis     = T.tardis
− Control/Monad/Trans/Tardis.hs
@@ -1,212 +0,0 @@-{-# OPTIONS_GHC -Wall -fno-warn-warnings-deprecations #-}-{-# 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 (TardisT, runTardisT)-  , evalTardisT-  , execTardisT--    -- * The Tardis monad-  , Tardis-  , runTardis-  , evalTardis-  , execTardis--    -- * Primitive Tardis operations-  , tardis--  , getPast-  , getFuture-  , sendPast-  , sendFuture--    -- * Composite Tardis operations-  , modifyForwards-  , modifyBackwards--  , getsPast-  , getsFuture--    -- * Other-  , mapTardisT-  , noState-  ) where--import Control.Applicative-import Control.Monad.Identity-import Control.Monad.Trans-import Control.Monad.Morph----- 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))-    -- ^ 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----- 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----- | A function that operates on the internal representation of a Tardis--- can also be used on a Tardis.-mapTardisT :: (m (a, (bw, fw)) -> n (b, (bw, fw)))-           -> TardisT bw fw m a -> TardisT bw fw n b-mapTardisT f m = TardisT $ f . runTardisT m---- | 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)----- Instances----------------------------------------------------instance MonadFix m => Monad (TardisT bw fw m) where-  return x = tardis $ \s -> (x, s)-  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''))--instance MonadFix m => Functor (TardisT bw fw m) where-  fmap = liftM--instance MonadFix m => Applicative (TardisT bw fw m) where-  pure = return-  (<*>) = ap---instance MonadTrans (TardisT bw fw) where-  lift m = TardisT $ \s -> do-    x <- m-    return (x, s)--instance MonadFix m => MonadFix (TardisT bw fw m) where-  mfix f = TardisT $ \s -> do-    rec (x, s') <- runTardisT (f x) s-    return (x, s')--instance MFunctor (TardisT bw fw) where-  hoist f = mapTardisT f---- 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 = 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-
+ src/Control/Monad/Tardis.hs view
@@ -0,0 +1,128 @@+{-# OPTIONS_GHC -Wall #-}++-- | This module re-exports both 'MonadTardis' and 'TardisT'+-- (Wherever there is overlap, the 'MonadTardis' version is preferred.)+-- +-- The recommended usage of a Tardis is to import this module.+module Control.Monad.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+++import Control.Monad.Tardis.Class+import Control.Monad.Trans.Tardis+  ( TardisT+  , runTardisT+  , evalTardisT+  , execTardisT++  , Tardis+  , runTardis+  , evalTardis+  , execTardis+  +  , noState+  )+++{- $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)++-}+
+ src/Control/Monad/Tardis/Class.hs view
@@ -0,0 +1,106 @@+{-# LANGUAGE RecursiveDo            #-}+{-# LANGUAGE MultiParamTypeClasses  #-}+{-# LANGUAGE FunctionalDependencies #-}+{-# LANGUAGE FlexibleInstances      #-}++-- | 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+  ( -- * The MonadTardis class+    MonadTardis (..)+    -- * Composite Tardis operations+  , modifyForwards+  , modifyBackwards+  , getsPast+  , getsFuture+  ) where++import Control.Applicative+import Control.Monad.Fix++import qualified Control.Monad.Trans.Tardis as T++-- | 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))+  getFuture      = tardis $ \ ~(bw, fw)  -> (bw, (bw, fw))+  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+      let (a, (future', past')) = f (future, past)+      sendPast future'+      past <- getPast+      future <- getFuture+      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 = 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+++instance MonadFix m => MonadTardis bw fw (T.TardisT bw fw m) where+  getPast    = T.getPast+  getFuture  = T.getFuture+  sendPast   = T.sendPast+  sendFuture = T.sendFuture+  tardis     = T.tardis
+ src/Control/Monad/Trans/Tardis.hs view
@@ -0,0 +1,209 @@+{-# LANGUAGE RecursiveDo                     #-}++-- | 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 (TardisT, runTardisT)+  , evalTardisT+  , execTardisT++    -- * The Tardis monad+  , Tardis+  , runTardis+  , evalTardis+  , execTardis++    -- * Primitive Tardis operations+  , tardis++  , getPast+  , getFuture+  , sendPast+  , sendFuture++    -- * Composite Tardis operations+  , modifyForwards+  , modifyBackwards++  , getsPast+  , getsFuture++    -- * Other+  , mapTardisT+  , noState+  ) where++import Control.Applicative+import Control.Monad.Identity+import Control.Monad.Trans+import Control.Monad.Morph+++-- 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))+    -- ^ 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+++-- 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+++-- | A function that operates on the internal representation of a Tardis+-- can also be used on a Tardis.+mapTardisT :: (m (a, (bw, fw)) -> n (b, (bw, fw)))+           -> TardisT bw fw m a -> TardisT bw fw n b+mapTardisT f m = TardisT $ f . runTardisT m++-- | 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)+++-- Instances+-------------------------------------------------++instance MonadFix m => Monad (TardisT bw fw m) where+  return x = tardis $ \s -> (x, s)+  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''))++instance MonadFix m => Functor (TardisT bw fw m) where+  fmap = liftM++instance MonadFix m => Applicative (TardisT bw fw m) where+  pure = return+  (<*>) = ap+++instance MonadTrans (TardisT bw fw) where+  lift m = TardisT $ \s -> do+    x <- m+    return (x, s)++instance MonadFix m => MonadFix (TardisT bw fw m) where+  mfix f = TardisT $ \s -> do+    rec (x, s') <- runTardisT (f x) s+    return (x, s')++instance MFunctor (TardisT bw fw) where+  hoist f = mapTardisT f++-- 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 = 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.4.2.0+version:             0.4.3.0 synopsis:            Bidirectional state monad transformer homepage:            https://github.com/DanBurton/tardis bug-reports:         https://github.com/DanBurton/tardis/issues@@ -22,6 +22,7 @@  library   default-language:  Haskell2010+  hs-source-dirs:    src   exposed-modules:     Control.Monad.Tardis                      , Control.Monad.Tardis.Class                      , Control.Monad.Trans.Tardis@@ -30,6 +31,14 @@                      , mtl==2.*                      , mmorph==1.* +test-suite tardis-tests+  default-language:  Haskell2010+  type: exitcode-stdio-1.0+  hs-source-dirs:  test+  main-is:         Main.hs+  build-depends:     base >= 4.8 && < 5+                   , tardis+  other-modules:   Example  source-repository head   type:     git
+ test/Example.hs view
@@ -0,0 +1,69 @@+{-# LANGUAGE RecursiveDo #-}++module Example where++import Control.Monad.Tardis++data BowlingGame = BowlingGame+  { frames :: ![Frame]  -- should be 9, too tedious to type restrict+  , lastFrame :: LFrame }++data Frame = Strike+           | Spare { firstThrow :: !Int }+           | Frame { firstThrow, secondThrow :: !Int }++data LFrame = LStrike { bonus1, bonus2 :: !Int }+            | LSpare { throw1, bonus1 :: !Int }+            | LFrame { throw1, throw2 :: !Int }++sampleGame :: BowlingGame+sampleGame = BowlingGame+  { frames =+    [ Strike    , Spare 9+    , Strike    , Strike+    , Strike    , Frame 8 1+    , Spare 7   , Strike+    , Strike+    ]+  , lastFrame = LStrike 10 10+  }++newtype PreviousScores = PreviousScores [Int]+newtype NextThrows = NextThrows (Int, Int)++toScores :: BowlingGame -> [Int]+toScores game = flip evalTardis initState $ go (frames game) where+  go :: [Frame] -> Tardis NextThrows PreviousScores [Int]+  go [] = do+    PreviousScores scores <- getPast+    let score = head scores+    return $ (finalFrameScore + score) : scores+  go (f : fs) = do+    rec+      sendPast $ NextThrows throws'+      PreviousScores scores <- getPast+      let score = head scores+      sendFuture $ PreviousScores (score' : scores)+      NextThrows ~(nextThrow1, nextThrow2) <- getFuture+      let (score', throws') = case f of+            Strike    -> (score + 10 + nextThrow1 + nextThrow2, (10, nextThrow1))+            Spare n   -> (score + 10 + nextThrow1,              (n, 10 - n))+            Frame n m -> (score + n + m,                        (n, m))+    go fs++  finalFrameScore = case lastFrame game of+    LStrike n m -> 10 + n + m+    LSpare _n m -> 10 + m+    LFrame  n m -> n  + m++  initState = (NextThrows $ case lastFrame game of+    LStrike n _m -> (10, n)+    LSpare  n _m -> (n,  10 - n)+    LFrame  n  m -> (n,  m)+    , PreviousScores [0])++expectedScores :: [Int]+expectedScores = [236,206,176,146,126,117,98,70,40,20,0]++actualScores :: [Int]+actualScores = toScores sampleGame
+ test/Main.hs view
@@ -0,0 +1,10 @@+import Example+import System.Exit++main :: IO ()+main = case actualScores == expectedScores of+  False -> do+    putStrLn $ "Expected: " <> show expectedScores+    putStrLn $ "Actual: " <> show actualScores+    exitFailure+  True -> exitSuccess