| Safe Haskell | None |
|---|
Control.Applicative.Operational
Description
Applicative programs over an operational-style instruction
set, implemented on top of the Ap free Applicative type.
- module Control.Operational.Class
- newtype ProgramAp instr a = ProgramAp {}
- interpretAp :: forall instr f a. Applicative f => (forall x. instr x -> f x) -> ProgramAp instr a -> f a
- fromProgramAp :: (Operational instr f, Applicative f) => ProgramAp instr a -> f a
- data ProgramViewAp instr a where
- Pure :: a -> ProgramViewAp instr a
- :<**> :: instr a -> ProgramViewAp instr (a -> b) -> ProgramViewAp instr b
- viewAp :: ProgramAp instr a -> ProgramViewAp instr a
- compileAp :: ProgramViewAp instr a -> ProgramAp instr a
- foldProgramViewAp :: (forall x. instr x -> r -> r) -> r -> ProgramViewAp instr a -> r
- instructions :: ProgramAp instr a -> [AnyInstr instr]
- data AnyInstr instr = forall a . AnyInstr (instr a)
Documentation
module Control.Operational.Class
newtype ProgramAp instr a Source
An Applicative program over instruction set instr. This is
modeled after the Program type from operational
(http://hackage.haskell.org/package/operational), but this one is
an Applicative, not a Monad. This makes it less powerful, but
in exchange for the sacrificed power ProgramAp is suceptible to
much stronger static analysis.
For examples of this (though applied to free applicatives), see:
- http://gergo.erdi.hu/blog/2012-12-01-static_analysis_with_applicatives/
- http://paolocapriotti.com/blog/2013/04/03/free-applicative-functors/
See also the examples in Control.Alternative.Operational.
Constructors
| ProgramAp | |
Instances
| Operational instr (ProgramAp instr) | |
| Functor (ProgramAp instr) | |
| Applicative (ProgramAp instr) |
interpretAp :: forall instr f a. Applicative f => (forall x. instr x -> f x) -> ProgramAp instr a -> f aSource
Evaluate a ProgramAp by interpreting each instruction as an
Applicative action. Example Reader implementation:
type Reader r a = ProgramAp (ReaderI r) a
data ReaderI r a where
Ask :: ReaderI r r
ask :: Reader r r
ask = singleton Ask
runReader :: forall r a. Reader r a -> r -> a
runReader = interpretAp evalI
where evalI :: forall a. ReaderI r a -> r -> a
evalI Ask = id
fromProgramAp :: (Operational instr f, Applicative f) => ProgramAp instr a -> f aSource
Lift a ProgramAp into any other Operational program type that
is at least as strong as Applicative; e.g., lift an applicative
program into a monadic one. Note that not all applicatives are
monads, so a lifted program may "lose" some of the
interpretations that the original could be given.
data ProgramViewAp instr a whereSource
A friendly concrete tree view type for ProgramAp. Unlike the
:>>= constructor in the ProgramView type of
Control.Monad.Operational, whose second data member is a function
that consumes an instruction result to generate the rest of the
program, our :<**> constructor exposes the rest of program
immediately.
Note that the ProgramViewAp type normalizes the program into a
different ordering and bracketing than the applicative <*>
operator does. The :<**> constructor is an analogue of from
Control.Applicative. The normalization means that you get a
list-like structure with instructions as the elements (in the same
order as their effects) and <**>
:: Applicative f => f a -> f (a -> b) -> f bPure as the terminator.
A static analysis example, based on Capriotti and Kaposi (2013, http://paolocapriotti.com/blog/2013/04/03/free-applicative-functors/):
{-# LANGUAGE GADTs, RankNTypes, ScopedTypeVariables #-}
import Control.Operational.Applicative
data FileSystemI a where
Read :: FilePath -> FileSystemI String
Write :: FilePath -> String -> FileSystemI ()
-- | Count how many file accesses a program does.
count :: ProgramAp FileSystemI a -> Int
count = count' . viewAp
where count' :: forall x. ProgramViewAp FileSystemI x -> Int
count' (Pure _) = 0
count' (_ :<**> k) = succ (count' k)
Or actually, just this:
count :: ProgramAp FileSystemI a -> Int count = length . instructions
You can also use the ProgramViewAp to interpret the program, in
the style of the operational package. Example implementation of
a simple terminal language in this style:
data TermI a where
Say :: String -> TermI ()
Get :: TermI String
say :: String -> ProgramAp TermI ()
say = singleton . Say
get :: ProgramAp TermI String
get = singleton Get
prompt :: String -> ProgramAp TermI String
prompt str = say str *> get
runTerm :: ProgramAp TermI a -> IO a
runTerm = eval . viewAp
where eval :: forall x. ProgramViewAp TermI x -> IO x
eval (Pure a) = pure a
eval (Say str :<**> k) = putStr str <**> eval k
eval (Get :<**> k) = getLine <**> eval k
example :: ProgramAp TermI (String, String)
example = (,) <$> prompt "First question: " <*> prompt "Second question: "
-- example = Say "First question: " :<**> (Get :<**> (Say "Second question: " :<**> (Get :<**> Pure (\_ a _ b -> (a, b)))))
But as a general rule, interpretAp makes for shorter, less
repetitive, fooler-proof interpreters:
runTerm :: ProgramAp TermI a -> IO a
runTerm = interpretAp evalI
where evalI :: forall x. TermI x -> IO x
evalI (Say str) = putStr str
evalI Get = getLine
Constructors
| Pure :: a -> ProgramViewAp instr a | |
| :<**> :: instr a -> ProgramViewAp instr (a -> b) -> ProgramViewAp instr b |
viewAp :: ProgramAp instr a -> ProgramViewAp instr aSource
Materialize a ProgramAp as a concrete tree. Note that
ProgramAp's Functor and Applicative instances normalize their
programs, so the view term may not look like the code that created
it. Instructions however will appear in the order that their
effects should happen, from left to right.
compileAp :: ProgramViewAp instr a -> ProgramAp instr aSource
Compile a ProgramViewAp back into a ProgramAp.
foldProgramViewAp :: (forall x. instr x -> r -> r) -> r -> ProgramViewAp instr a -> rSource
instructions :: ProgramAp instr a -> [AnyInstr instr]Source