operational-0.2.4.2: Implementation of difficult monads made easy with operational semantics.

Safe Haskell | Safe-Inferred
Language | Haskell2010

Control.Monad.Operational

Contents

- Synopsis
- Overview
- Monad
- Monad transformer

Synopsis

- type Program (instr :: Type -> Type) = ProgramT instr Identity
- singleton :: forall instr a (m :: Type -> Type). instr a -> ProgramT instr m a
- type ProgramView (instr :: Type -> Type) = ProgramViewT instr Identity
- view :: forall (instr :: Type -> Type) a. Program instr a -> ProgramView instr a
- interpretWithMonad :: forall instr m b. Monad m => (forall a. instr a -> m a) -> Program instr b -> m b
- data ProgramT (instr :: Type -> Type) (m :: Type -> Type) a
- data ProgramViewT (instr :: Type -> Type) (m :: Type -> Type) a where
  - Return :: forall a (instr :: Type -> Type) (m :: Type -> Type). a -> ProgramViewT instr m a
  - (:>>=) :: forall (instr :: Type -> Type) b (m :: Type -> Type) a. instr b -> (b -> ProgramT instr m a) -> ProgramViewT instr m a
- viewT :: forall m (instr :: Type -> Type) a. Monad m => ProgramT instr m a -> m (ProgramViewT instr m a)
- liftProgram :: forall (m :: Type -> Type) (instr :: Type -> Type) a. Monad m => Program instr a -> ProgramT instr m a
- mapInstr :: forall instr1 instr2 (m :: Type -> Type) a. Monad m => (forall x. instr1 x -> instr2 x) -> ProgramT instr1 m a -> ProgramT instr2 m a
- unviewT :: forall (m :: Type -> Type) (instr :: Type -> Type) a. Monad m => ProgramViewT instr m a -> ProgramT instr m a
- interpretWithMonadT :: Monad m => (forall x. instr x -> m x) -> ProgramT instr m a -> m a

# Synopsis

To write a monad, use the `Program` type.

To write a monad transformer, use the `ProgramT` type.

For easier interoperability, the `Program` type is actually a type synonym and defined in terms of `ProgramT`.

# Overview

The basic idea for implementing monads with this libary is to think of monads as sequences of primitive instructions. For instance, imagine that you want to write a web application with a custom monad that features an instruction

    askUserInput :: CustomMonad UserInput

which sends a form to the remote user and waits for the user to send back his input

To implement this monad, you decide that this instruction is a primitive, i.e. should not be implemented in terms of other, more basic instructions. Once you have chosen your primitives, collect them in a data type

    data CustomMonadInstruction a where
        AskUserInput :: CustomMonadInstruction UserInput

Then, obtain your custom monad simply by applying the `Program` type constructor

    type CustomMonad a = Program CustomMonadInstruction a

The library makes sure that it is an instance of the `Monad` class and fulfills all the required laws.

Essentially, the monad you now obtained is just a fancy list of primitive instructions. In particular, you can pattern match on the first element of this "list". This is how you implement an `interpret` or `run` function for your monad. Note that pattern matching is done using the `view` function

    runCustomMonad :: CustomMonad a -> IO a
    runCustomMonad m = case view m of
        Return a            -> return a -- done, return the result
        AskUserInput :>>= k -> do
            b <- waitForUserInput       -- wait for external user input
            runCustomMonad (k b)        -- proceed with next instruction

The point is that you can now proceed in any way you like: 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 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 be a monad at all.

In essence, your custom monad allows you to express your web application as a simple imperative program, while the underlying implementation can freely map this to an event-drived model or some other control flow architecture of your choice.

The possibilities are endless. More usage examples can be found here: https://github.com/HeinrichApfelmus/operational/tree/master/doc/examples#readme

# Monad

type Program (instr :: Type -> Type) = ProgramT instr Identity Source #

The abstract data type ``Program` instr a` represents programs, 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.

singleton :: forall instr a (m :: Type -> Type). instr a -> ProgramT instr m a Source #

Program made from a single primitive instruction.

type ProgramView (instr :: Type -> Type) = ProgramViewT instr Identity Source #

View type for inspecting the first instruction. It has two constructors `Return` and `:>>=`. (For technical reasons, they are documented at `ProgramViewT`.)

view :: forall (instr :: Type -> Type) a. Program instr a -> ProgramView instr a Source #

View function for inspecting the first instruction.

Example usage

Stack machine from "The Operational Monad Tutorial".

       data StackInstruction a where
           Push :: Int -> StackInstruction ()
           Pop  :: StackInstruction Int

       type StackProgram a = Program StackInstruction a
       type Stack b        = [b]

       interpret :: StackProgram a -> (Stack Int -> a)
       interpret = eval . view
           where
           eval :: ProgramView StackInstruction a -> (Stack Int -> a)
           eval (Push a :>>= is) stack     = interpret (is ()) (a:stack)
           eval (Pop    :>>= is) (a:stack) = interpret (is a ) stack
           eval (Return a)       stack     = a

In this example, the type signature for the `eval` helper function is optional.

interpretWithMonad :: forall instr m b. Monad m => (forall a. instr a -> m a) -> Program instr b -> m b Source #

Utility function that extends a given interpretation of instructions as monadic actions to an interpration of `Program`s as monadic actions.

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, you should write your interpreter directly with `view` instead.

# Monad transformer

data ProgramT (instr :: Type -> Type) (m :: Type -> Type) a Source #

The abstract data type ``ProgramT` instr m a` represents programs over a base monad `m`, 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.

#### Instances

Instances details

MonadReader r m => MonadReader r (ProgramT instr m) Source # |
Instance details Defined in Control.Monad.Operational Methods ask :: ProgramT instr m r Source # local :: (r -> r) -> ProgramT instr m a -> ProgramT instr m a Source # reader :: (r -> a) -> ProgramT instr m a Source #
MonadState s m => MonadState s (ProgramT instr m) Source # |
Instance details Defined in Control.Monad.Operational Methods get :: ProgramT instr m s Source # put :: s -> ProgramT instr m () Source # state :: (s -> (a, s)) -> ProgramT instr m a Source #
MonadTrans (ProgramT instr) Source # |
Instance details Defined in Control.Monad.Operational Methods lift :: Monad m => m a -> ProgramT instr m a Source #
MonadIO m => MonadIO (ProgramT instr m) Source # |
Instance details Defined in Control.Monad.Operational Methods liftIO :: IO a -> ProgramT instr m a Source #
Monad m => Applicative (ProgramT instr m) Source # |

Instance details

Defined in Control.Monad.Operational

Methods

pure :: a -> ProgramT instr m a Source #

(<*>) :: ProgramT instr m (a -> b) -> ProgramT instr m a -> ProgramT instr m b Source #

liftA2 :: (a -> b -> c) -> ProgramT instr m a -> ProgramT instr m b -> ProgramT instr m c Source #

(*>) :: ProgramT instr m a -> ProgramT instr m b -> ProgramT instr m b Source #

(<*) :: ProgramT instr m a -> ProgramT instr m b -> ProgramT instr m a Source #

Monad m => Functor (ProgramT instr m) Source # |
Instance details Defined in Control.Monad.Operational Methods fmap :: (a -> b) -> ProgramT instr m a -> ProgramT instr m b Source # (<$) :: a -> ProgramT instr m b -> ProgramT instr m a Source #
Monad m => Monad (ProgramT instr m) Source # |
Instance details Defined in Control.Monad.Operational Methods (>>=) :: ProgramT instr m a -> (a -> ProgramT instr m b) -> ProgramT instr m b Source # (>>) :: ProgramT instr m a -> ProgramT instr m b -> ProgramT instr m b Source # return :: a -> ProgramT instr m a Source #

data ProgramViewT (instr :: Type -> Type) (m :: Type -> Type) a where Source #

View type for inspecting the first instruction. This is very similar to pattern matching on lists.

- The case `(Return a)` means that the program contains no instructions and just returns the result `a`.
- The case `(someInstruction :>>= k)` means that the first instruction is `someInstruction` and the remaining program is given by the function `k`.

Constructors

Return :: forall a (instr :: Type -> Type) (m :: Type -> Type). a -> ProgramViewT instr m a |
(:>>=) :: forall (instr :: Type -> Type) b (m :: Type -> Type) a. instr b -> (b -> ProgramT instr m a) -> ProgramViewT instr m a |

#### Instances

Instances details

Monad m => Applicative (ProgramViewT instr m) Source # |

Instance details

Defined in Control.Monad.Operational

Methods

pure :: a -> ProgramViewT instr m a Source #

(<*>) :: ProgramViewT instr m (a -> b) -> ProgramViewT instr m a -> ProgramViewT instr m b Source #

liftA2 :: (a -> b -> c) -> ProgramViewT instr m a -> ProgramViewT instr m b -> ProgramViewT instr m c Source #

(*>) :: ProgramViewT instr m a -> ProgramViewT instr m b -> ProgramViewT instr m b Source #

(<*) :: ProgramViewT instr m a -> ProgramViewT instr m b -> ProgramViewT instr m a Source #

Monad m => Functor (ProgramViewT instr m) Source # |
Instance details Defined in Control.Monad.Operational Methods fmap :: (a -> b) -> ProgramViewT instr m a -> ProgramViewT instr m b Source # (<$) :: a -> ProgramViewT instr m b -> ProgramViewT instr m a Source #
Monad m => Monad (ProgramViewT instr m) Source # |
Instance details Defined in Control.Monad.Operational Methods (>>=) :: ProgramViewT instr m a -> (a -> ProgramViewT instr m b) -> ProgramViewT instr m b Source # (>>) :: ProgramViewT instr m a -> ProgramViewT instr m b -> ProgramViewT instr m b Source # return :: a -> ProgramViewT instr m a Source #

viewT :: forall m (instr :: Type -> Type) a. Monad m => ProgramT instr m a -> m (ProgramViewT instr m a) Source #

View function for inspecting the first instruction.

Example usage

List monad transformer.

       data PlusI m a where
           Zero :: PlusI m a
           Plus :: ListT m a -> ListT m a -> PlusI m a

       type ListT m a = ProgramT (PlusI m) m a

       runList :: Monad m => ListT m a -> m [a]
       runList = eval <=< viewT
           where
           eval :: Monad m => ProgramViewT (PlusI m) m a -> m [a]
           eval (Return x)        = return [x]
           eval (Zero     :>>= k) = return []
           eval (Plus m n :>>= k) =
               liftM2 (++) (runList (m >>= k)) (runList (n >>= k))

In this example, the type signature for the `eval` helper function is optional.

liftProgram :: forall (m :: Type -> Type) (instr :: Type -> Type) a. Monad m => Program instr a -> ProgramT instr m a Source #

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:

        liftProgram = eval . view
            where
            eval :: ProgramView instr a -> ProgramT instr m a
            eval (Return a) = return a
            eval (i :>>= k) = singleton i >>= liftProgram . k

mapInstr :: forall instr1 instr2 (m :: Type -> Type) a. Monad m => (forall x. instr1 x -> instr2 x) -> ProgramT instr1 m a -> ProgramT instr2 m a Source #

Extend a mapping of instructions to a mapping of `ProgramT`.

unviewT :: forall (m :: Type -> Type) (instr :: Type -> Type) a. Monad m => ProgramViewT instr m a -> ProgramT instr m a Source #

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)

interpretWithMonadT :: Monad m => (forall x. instr x -> m x) -> ProgramT instr m a -> m a Source #

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`.