packages feed

operational 0.2.3.5 → 0.2.4.0

raw patch · 4 files changed

+79/−20 lines, 4 filesdep ~base

Dependency ranges changed: base

Files

CHANGELOG.md view
@@ -1,6 +1,12 @@ Changelog for the `operational` package --------------------------------------- +**0.2.4.0** -- Feature release.++* Update to build with GHC 9.0.1.+* Add utility functions `interpretWithMonadT`, `unviewT` and `mapInstr`+* Add utility `Functor`, `Applicative`, and `Monad` instances for `ProgramViewT` type.+ **0.2.3.5** -- Maintenance release.  * Update references to other packages.
doc/examples/TicTacToe.hs view
@@ -20,7 +20,7 @@ import Control.Monad.State  import Data.Either-import Data.List+import Data.List (transpose, intersperse)      -- external libraries needed import System.Random
operational.cabal view
@@ -1,5 +1,5 @@ Name:               operational-Version:            0.2.3.5+Version:            0.2.4.0 Synopsis:           Implementation of difficult monads made easy                     with operational semantics. Description:
src/Control/Monad/Operational.hs view
@@ -5,25 +5,25 @@ module Control.Monad.Operational (     -- * Synopsis     -- $synopsis-    +     -- * Overview     -- $intro-    +     -- * Monad     Program, singleton, ProgramView, view,     -- $example     interpretWithMonad,-    +     -- * Monad transformer     ProgramT, ProgramViewT(..), viewT,     -- $exampleT-    liftProgram,-    +    liftProgram, mapInstr,+    unviewT, interpretWithMonadT,+     ) where  import Control.Monad.Identity import Control.Monad.Trans-import Control.Applicative      -- mtl  classes to instantiate.     -- Those commented out cannot be instantiated. For reasons see below.@@ -95,7 +95,7 @@ you can wait for the user to return input as shown, or you store the continuation @k@ and retrieve it when your web application receives another HTTP request,-or you can keep a log of all user inputs on the client side an replay them,+or you can keep a log of all user inputs on the client side and replay them, and so on. Moreover, you can implement different @run@ functions for one and the same custom monad, which is useful for testing. Also note that the result type of the @run@ function does not need to@@ -120,9 +120,9 @@     i.e. sequences of primitive instructions.      * The /primitive instructions/ are given by the type constructor @instr :: * -> *@.-    +     * @a@ is the return type of a program.-    +     @'Program' instr@ is always a monad and     automatically obeys the monad laws. -}@@ -144,7 +144,7 @@ -- -- This function can be useful if you are mainly interested in -- mapping a 'Program' to different standard monads, like the state monad.--- For implementing a truly custom monad, +-- For implementing a truly custom monad, -- you should write your interpreter directly with 'view' instead. interpretWithMonad :: forall instr m b.     Monad m => (forall a. instr a -> m a) -> (Program instr b -> m b)@@ -187,11 +187,11 @@     i.e. sequences of primitive instructions and actions from the base monad.      * The /primitive instructions/ are given by the type constructor @instr :: * -> *@.-    +     * @m@ is the base monad, embedded with 'lift'.      * @a@ is the return type of a program.-    +     @'ProgramT' instr m@ is a monad transformer and     automatically obeys both the monad and the lifting laws. -}@@ -234,6 +234,19 @@            -> (b -> ProgramT instr m a)            -> ProgramViewT instr m a +instance Monad m => Functor (ProgramViewT instr m) where+    fmap f (Return a) = Return $ f a+    fmap f (instr :>>= cont) = instr :>>= (fmap f . cont)++instance Monad m => Applicative (ProgramViewT instr m) where+    pure = return+    (<*>) = ap++instance Monad m => Monad (ProgramViewT instr m) where+    return = Return+    Return a >>= cont = cont a+    (instr :>>= cont1) >>= cont2 = instr :>>= (cont1 >=> unviewT . cont2)+ -- | View function for inspecting the first instruction. viewT :: Monad m => ProgramT instr m a -> m (ProgramViewT instr m a) viewT (Lift m)                = m >>= return . Return@@ -245,7 +258,7 @@ {-| Lift a plain sequence of instructions to a sequence     of instructions over a monad 'm'.     This is the counterpart of the 'lift' function from 'MonadTrans'.-    +     It can be defined as follows:  @@@ -255,7 +268,7 @@         eval (Return a) = return a         eval (i :>>= k) = singleton i >>= liftProgram . k @-    + -} liftProgram :: Monad m => Program instr a -> ProgramT instr m a liftProgram (Lift m)     = return (runIdentity m)@@ -263,6 +276,47 @@ liftProgram (Instr i)    = Instr i  +-- | Utility function that extends+-- a given interpretation of instructions as monadic actions+-- to an interpration of 'ProgramT's as monadic actions.+--+-- Ideally, you would not use another monad,+-- but write a custom interpreter directly with `viewT`.+-- See the remark at 'interpretWithMonad'.+interpretWithMonadT :: Monad m => (forall x . instr x -> m x) -> ProgramT instr m a -> m a+interpretWithMonadT interpreter = go+  where+    go program = do+      firstInstruction <- viewT program+      case firstInstruction of+        Return a -> return a+        instruction :>>= continuation -> interpreter instruction >>= (go . continuation)++-- | Utilitiy function for mapping a 'ProgramViewT' back into a 'ProgramT'.+-- +-- Semantically, the function 'unviewT' is an inverse of 'viewT',+-- e.g. we have+--+-- @+--   viewT (singleton i) >>= unviewT = return (singleton i)+-- @+unviewT :: Monad m => ProgramViewT instr m a -> ProgramT instr m a+unviewT (Return a) = return a+unviewT (instruction :>>= continuation) =+    (Instr instruction) `Bind` continuation++-- | Extend a mapping of instructions to a mapping of 'ProgramT'.+mapInstr ::+    forall instr1 instr2 m a . Monad m+    => (forall x . instr1 x -> instr2 x)+    -> ProgramT instr1 m a -> ProgramT instr2 m a+mapInstr f = go+    where+        go :: forall x. ProgramT instr1 m x -> ProgramT instr2 m x+        go (Lift action) = Lift action+        go (Bind action continuation) = Bind (go action) (go . continuation)+        go (Instr instruction) = Instr $ f instruction+ {- $exampleT  /Example usage/@@ -290,11 +344,11 @@  {------------------------------------------------------------------------------     mtl instances-    +   * All of these instances need UndecidableInstances,     because they do not satisfy the coverage condition.     Most of the instance in the  mtl  package itself have the same issue.-    +   * Lifting algebraic operations is easy,     lifting control operations is more elaborate, but sometimes possible.     See the design notes in  `doc/design.md`.@@ -308,8 +362,7 @@  instance (MonadReader r m) => MonadReader r (ProgramT instr m) where     ask = lift ask-    +     local r (Lift m)     = Lift (local r m)     local r (m `Bind` k) = local r m `Bind` (local r . k)     local _ (Instr i)    = Instr i-