Moduleoperational-0.2.4.2Haskell2010
Control.Monad.Operational
- 4 types
- 8 values
- Packageoperational-0.2.4.2
- Exports12
- LanguageHaskell2010
- LicenceBSD-3-Clause
- SourceOperational.hs
Synopsis
0 declarationsOverview
0 declarationsThe 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 UserInputwhich 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 aThe 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
5 declarationsProgram made from a single primitive instruction.
View type for inspecting the first instruction.
It has two constructors Return and :>>=.
(For technical reasons, they are documented at ProgramViewT.)
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 = aIn this example, the type signature for the eval helper function is optional.
Utility function that extends a given interpretation of instructions as monadic actions to an interpration of Programs 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
7 declarationsThe 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 :: * -> *.mis the base monad, embedded with lift.ais the return type of a program.
ProgramT instr m is a monad transformer and
automatically obeys both the monad and the lifting laws.
Instances7MonadReader, MonadState, MonadTrans, Monad, Functor, Applicative, …
MonadReader r m => MonadReader r (ProgramT instr m)Defined in operational-0.2.4.2 · Control.Monad.OperationalMonadState s m => MonadState s (ProgramT instr m)Defined in operational-0.2.4.2 · Control.Monad.OperationalMonadTrans (ProgramT instr)Defined in operational-0.2.4.2 · Control.Monad.OperationalMonad m => Monad (ProgramT instr m)Defined in operational-0.2.4.2 · Control.Monad.OperationalMonad m => Functor (ProgramT instr m)Defined in operational-0.2.4.2 · Control.Monad.OperationalMonad m => Applicative (ProgramT instr m)Defined in operational-0.2.4.2 · Control.Monad.OperationalMonadIO m => MonadIO (ProgramT instr m)Defined in operational-0.2.4.2 · Control.Monad.Operational
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 resulta.The case
(someInstruction :>>= k)means that the first instruction issomeInstructionand the remaining program is given by the functionk.
Constructors
Return :: a -> ProgramViewT instr m a(:>>=) :: instr b -> (b -> ProgramT instr m a) -> ProgramViewT instr m a
Instances3Monad, Functor, Applicative
Monad m => Monad (ProgramViewT instr m)Defined in operational-0.2.4.2 · Control.Monad.OperationalMonad m => Functor (ProgramViewT instr m)Defined in operational-0.2.4.2 · Control.Monad.OperationalMonad m => Applicative (ProgramViewT instr m)Defined in operational-0.2.4.2 · Control.Monad.Operational
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.
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
Extend a mapping of instructions to a mapping of ProgramT.
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)
Utility function that extends a given interpretation of instructions as monadic actions to an interpration of ProgramTs as monadic actions.
Ideally, you would not use another monad, but write a custom interpreter directly with viewT. See the remark at interpretWithMonad.