Safe Haskell  Safe 

Language  Haskell98 
Use the Model
 View
 Controller
pattern to separate impure inputs
and outputs from pure application logic so that you can:
 Equationally reason about your model
 Exercise your model with propertybased testing (like
QuickCheck
)  Reproducibly replay your model
The mvc
library uses the type system to statically enforce the separation
of impure View
s and Controller
s from the pure Model
.
Here's a small example program written using the mvc
library to illustrate
the core types and concepts:
import MVC import qualified MVC.Prelude as MVC import qualified Pipes.Prelude as Pipes external :: Managed (View String, Controller String) external = do c1 < MVC.stdinLines c2 < MVC.tick 1 return (MVC.stdoutLines, c1 <> fmap show c2) model :: Model () String String model = asPipe (Pipes.takeWhile (/= "quit")) main :: IO () main = runMVC () model external
This program has three components:
 A
Controller
that interleaves lines from standard input with periodic ticks  A
View
that writes lines to standard output  A pure
Model
, which forwards lines until the user inputs "quit"
runMVC
connects them into a complete program, which outputs a ()
every
second and also echoes standard input to standard output until the user
enters "quit":
>>>
main
() Test<Enter> Test () () 42<Enter> 42 () quit<enter>>>>
The following sections give extended guidance for how to structure mvc
programs. Additionally, there is an MVC.Prelude module, which provides
several utilities and provides a more elaborate code example using the
sdl
library.
Synopsis
 data Controller a
 asInput :: Input a > Controller a
 keeps :: ((b > Constant (First b) b) > a > Constant (First b) a) > Controller a > Controller b
 data View a
 asSink :: (a > IO ()) > View a
 asFold :: FoldM IO a () > View a
 handles :: HandlerM IO a b > View b > View a
 type Model = ModelM Identity
 data ModelM m s a b
 asPipe :: Pipe a b (StateT s m) () > ModelM m s a b
 runMVC :: s > Model s a b > Managed (View b, Controller a) > IO s
 generalizeMVC :: Monad m => (forall x. m x > IO x) > s > ModelM m s a b > Managed (View b, Controller a) > IO s
 data Managed a
 managed :: (forall r. (a > IO r) > IO r) > Managed a
 loop :: Monad m => (a > ListT m b) > Pipe a b m r
 newtype Constant a (b :: k) :: forall k. * > k > * = Constant {
 getConstant :: a
 class Contravariant (f :: * > *) where
 (<>) :: Semigroup a => a > a > a
 class Semigroup a => Monoid a where
 data First a
 module Pipes
 module Pipes.Concurrent
Controllers
Controller
s represent concurrent inputs to your system. Use the Functor
and Monoid
instances for Controller
and Managed
to unify multiple
Managed
Controller
s together into a single Managed
Controller
:
controllerA :: Managed (Controller A) controllerB :: Managed (Controller B) controllerC :: Managed (Controller C) data TotalInput = InA A  InB B  InC C controllerTotal :: Managed (Controller TotalInput) controllerTotal = fmap (fmap InA) controllerA <> fmap (fmap InB) controllerB <> fmap (fmap InC) controllerC
Combining Controller
s interleaves their values.
data Controller a Source #
A concurrent source
fmap f (c1 <> c2) = fmap f c1 <> fmap f c2 fmap f mempty = mempty
Instances
Functor Controller Source #  
Defined in MVC fmap :: (a > b) > Controller a > Controller b # (<$) :: a > Controller b > Controller a #  
Semigroup (Controller a) Source #  
Defined in MVC (<>) :: Controller a > Controller a > Controller a # sconcat :: NonEmpty (Controller a) > Controller a # stimes :: Integral b => b > Controller a > Controller a #  
Monoid (Controller a) Source #  
Defined in MVC mempty :: Controller a # mappend :: Controller a > Controller a > Controller a # mconcat :: [Controller a] > Controller a # 
asInput :: Input a > Controller a Source #
Create a Controller
from an Input
:: ((b > Constant (First b) b) > a > Constant (First b) a)  
> Controller a  
> Controller b 
Think of the type as one of the following types:
keeps :: Prism' a b > Controller a > Controller b keeps :: Traversal' a b > Controller a > Controller b
(keeps prism controller)
only emits values if the prism
matches the
controller
's output.
keeps (p1 . p2) = keeps p2 . keeps p1 keeps id = id
keeps p (c1 <> c2) = keeps p c1 <> keeps p c2 keeps p mempty = mempty
Views
View
s represent outputs of your system. Use handles
and the Monoid
instance of View
to unify multiple View
s together into a single View
:
viewD :: Managed (View D) viewE :: Managed (View E) viewF :: Managed (View F) data TotalOutput = OutD D  OutE E  OutF F makePrisms ''TotalOutput  Generates _OutD, _OutE, and _OutF prisms viewTotal :: Managed (View TotalOutput) viewTotal = fmap (handles _OutD) viewD <> fmap (handles _OutE) viewE <> fmap (handles _OutF) viewF
Combining View
s sequences their outputs.
If a lens
dependency is too heavyweight, then you can manually generate
Traversal
s, which handles
will also accept. Here is an example of how
you can generate Traversal
s by hand with no dependencies:
 _OutD :: Traversal' TotalOutput D _OutD :: Applicative f => (D > f D) > (TotalOutput > f TotalOutput) _OutD k (OutD d) = fmap OutD (k d) _OutD k t = pure t  _OutE :: Traversal' TotalOutput E _OutE :: Applicative f => (E > f E) > (TotalOutput > f TotalOutput) _OutE k (OutE d) = fmap OutE (k d) _OutE k t = pure t  _OutF :: Traversal' TotalOutput F _OutF :: Applicative f => (F > f F) > (TotalOutput > f TotalOutput) _OutF k (OutF d) = fmap OutF (k d) _OutF k t = pure t
An effectful sink
contramap f (v1 <> v2) = contramap f v1 <> contramap f v2 contramap f mempty = mempty
Think of the type as one of the following types:
handles :: Prism' a b > View b > View a handles :: Traversal' a b > View b > View a
(handles prism view)
only runs the view
if the prism
matches the
input.
handles (p1 . p2) = handles p1 . handles p2 handles id = id
handles p (v1 <> v2) = handles p v1 <> handles p v2 handles p mempty = mempty
Models
Model
s are stateful streams and they sit in between Controller
s and
View
s.
Use State
to internally communicate within the Model
.
Read the "ListT" section which describes why you should prefer ListT
over Pipe
when possible.
Also, try to defer converting your Pipe
to a Model
until you call
runMVC
, because the conversion is not reversible and Pipe
is strictly
more featureful than Model
.
A (Model s a b)
converts a stream of (a)
s into a stream of (b)
s while
interacting with a state (s)
MVC
Connect a Model
, View
, and Controller
and an initial state
together using runMVC
to complete your application.
runMVC
is the only way to consume View
s and Controller
s. The types
forbid you from mixing View
and Controller
logic with your Model
logic.
Note that runMVC
only accepts one View
and one Controller
. This
enforces a single entry point and exit point for your Model
so that you
can cleanly separate your Model
logic from your View
logic and
Controller
logic. The way you add more View
s and Controller
s to your
program is by unifying them into a single View
or Controller
by using
their Monoid
instances. See the "Controllers" and "Views" sections
for more details on how to do this.
:: s  Initial state 
> Model s a b  Program logic 
> Managed (View b, Controller a)  Effectful output and input 
> IO s  Returns final state 
Connect a Model
, View
, and Controller
and initial state into a
complete application.
:: Monad m  
=> (forall x. m x > IO x)  Monad morphism 
> s  Initial state 
> ModelM m s a b  Program logic 
> Managed (View b, Controller a)  Effectful output and input 
> IO s  Returns final state 
Connect a Model
, View
, and Controller
and initial state into a
complete application over arbitrary monad given a morphism to IO.
Managed resources
Use managed
to create primitive Managed
resources and use the Functor
,
Applicative
, Monad
, and Monoid
instances for Managed
to bundle
multiple Managed
resources into a single Managed
resource.
See the source code for the "Utilities" section below for several examples
of how to create Managed
resources.
A managed resource that you acquire using with
Instances
ListT
ListT
computations can be combined in more ways than Pipe
s, so try to
program in ListT
as much as possible and defer converting it to a Pipe
as late as possible using loop
.
You can combine ListT
computations even if their inputs and outputs are
completely different:
 Independent computations modelAToD :: A > ListT (State S) D modelBToE :: B > ListT (State S) E modelCToF :: C > ListT (State s) F modelInToOut :: TotalInput > ListT (State S) TotalOutput modelInToOut totalInput = case totalInput of InA a > fmap OutD (modelAToD a) InB b > fmap OutE (modelBToE b) InC c > fmap OutF (modelCToF c)
Sometimes you have multiple computations that handle different inputs but the same output, in which case you don't need to unify their outputs:
 Overlapping outputs modelAToOut :: A > ListT (State S) Out modelBToOut :: B > ListT (State S) Out modelCToOut :: C > ListT (State S) Out modelInToOut :: TotalInput > ListT (State S) TotalOutput modelInToOut totalInput = case totalInput of InA a > modelAToOut a InB b > modelBToOut b InC c > modelCToOut c
Other times you have multiple computations that handle the same input but
produce different outputs. You can unify their outputs using the Monoid
and Functor
instances for ListT
:
 Overlapping inputs modelInToA :: TotalInput > ListT (State S) A modelInToB :: TotalInput > ListT (State S) B modelInToC :: TotalInput > ListT (State S) C modelInToOut :: TotalInput > ListT (State S) TotalOutput modelInToOut totalInput = fmap OutA (modelInToA totalInput) <> fmap OutB (modelInToB totalInput) <> fmap OutC (modelInToC totalInput)
You can also chain ListT
computations, feeding the output of the first
computation as the input to the next computation:
 Endtoend modelInToMiddle :: TotalInput > ListT (State S) MiddleStep modelMiddleToOut :: MiddleStep > ListT (State S) TotalOutput modelInToOut :: TotalInput > ListT (State S) TotalOutput modelInToOut = modelInToMiddle >=> modelMiddleToOut
... or you can just use do
notation if you prefer.
However, the Pipe
type is more general than ListT
and can represent
things like termination. Therefore you should consider mixing Pipe
s with
ListT
when you need to take advantage of these extra features:
 Mix ListT with Pipes pipe :: Pipe TotalInput TotalOutput (State S) () pipe = Pipes.takeWhile (not . isC)) >> loop modelInToOut where isC (InC _) = True isC _ = False
So promote your ListT
logic to a Pipe
when you need to take advantage of
these Pipe
specific features.
Reexports
Data.Functor.Constant reexports Constant
Data.Functor.Contravariant reexports Contravariant
Data.Monoid reexports Monoid
, (<>
), mconcat
, and First
(the type
only)
Pipes reexports everything
Pipes.Concurrent reexports everything
newtype Constant a (b :: k) :: forall k. * > k > * #
Constant functor.
Constant  

Instances
class Contravariant (f :: * > *) where #
The class of contravariant functors.
Whereas in Haskell, one can think of a Functor
as containing or producing
values, a contravariant functor is a functor that can be thought of as
consuming values.
As an example, consider the type of predicate functions a > Bool
. One
such predicate might be negative x = x < 0
, which
classifies integers as to whether they are negative. However, given this
predicate, we can reuse it in other situations, providing we have a way to
map values to integers. For instance, we can use the negative
predicate
on a person's bank balance to work out if they are currently overdrawn:
newtype Predicate a = Predicate { getPredicate :: a > Bool } instance Contravariant Predicate where contramap f (Predicate p) = Predicate (p . f)  ` First, map the input... ` then apply the predicate. overdrawn :: Predicate Person overdrawn = contramap personBankBalance negative
Any instance should be subject to the following laws:
contramap id = id contramap f . contramap g = contramap (g . f)
Note, that the second law follows from the free theorem of the type of
contramap
and the first law, so you need only check that the former
condition holds.
Instances
class Semigroup a => Monoid a where #
The class of monoids (types with an associative binary operation that has an identity). Instances should satisfy the following laws:
x
<>
mempty
= xmempty
<>
x = xx
(<>
(y<>
z) = (x<>
y)<>
zSemigroup
law)mconcat
=foldr
'(<>)'mempty
The method names refer to the monoid of lists under concatenation, but there are many other instances.
Some types can be viewed as a monoid in more than one way,
e.g. both addition and multiplication on numbers.
In such cases we often define newtype
s and make those instances
of Monoid
, e.g. Sum
and Product
.
NOTE: Semigroup
is a superclass of Monoid
since base4.11.0.0.
Identity of mappend
An associative operation
NOTE: This method is redundant and has the default
implementation
since base4.11.0.0.mappend
= '(<>)'
Fold a list using the monoid.
For most types, the default definition for mconcat
will be
used, but the function is included in the class definition so
that an optimized version can be provided for specific types.
Instances
Monoid Ordering  Since: base2.1 
Monoid ()  Since: base2.1 
Monoid All  Since: base2.1 
Monoid Any  Since: base2.1 
Monoid [a]  Since: base2.1 
Semigroup a => Monoid (Maybe a)  Lift a semigroup into Since 4.11.0: constraint on inner Since: base2.1 
Monoid a => Monoid (IO a)  Since: base4.9.0.0 
(Semigroup a, Monoid a) => Monoid (Concurrently a)  Since: async2.1.0 
Defined in Control.Concurrent.Async mempty :: Concurrently a # mappend :: Concurrently a > Concurrently a > Concurrently a # mconcat :: [Concurrently a] > Concurrently a #  
(Ord a, Bounded a) => Monoid (Min a)  Since: base4.9.0.0 
(Ord a, Bounded a) => Monoid (Max a)  Since: base4.9.0.0 
Monoid m => Monoid (WrappedMonoid m)  Since: base4.9.0.0 
Defined in Data.Semigroup mempty :: WrappedMonoid m # mappend :: WrappedMonoid m > WrappedMonoid m > WrappedMonoid m # mconcat :: [WrappedMonoid m] > WrappedMonoid m #  
Semigroup a => Monoid (Option a)  Since: base4.9.0.0 
Monoid a => Monoid (Identity a)  
Monoid (First a)  Since: base2.1 
Monoid (Last a)  Since: base2.1 
Monoid a => Monoid (Dual a)  Since: base2.1 
Monoid (Endo a)  Since: base2.1 
Num a => Monoid (Sum a)  Since: base2.1 
Num a => Monoid (Product a)  Since: base2.1 
Monoid (Predicate a)  
Monoid (Comparison a)  
Defined in Data.Functor.Contravariant mempty :: Comparison a # mappend :: Comparison a > Comparison a > Comparison a # mconcat :: [Comparison a] > Comparison a #  
Monoid (Equivalence a)  
Defined in Data.Functor.Contravariant mempty :: Equivalence a # mappend :: Equivalence a > Equivalence a > Equivalence a # mconcat :: [Equivalence a] > Equivalence a #  
Monoid a => Monoid (Managed a)  
Monoid (Input a)  
Monoid (Output a)  
Monoid (View a) #  
Monoid (Controller a) #  
Defined in MVC mempty :: Controller a # mappend :: Controller a > Controller a > Controller a # mconcat :: [Controller a] > Controller a #  
Monoid b => Monoid (a > b)  Since: base2.1 
(Monoid a, Monoid b) => Monoid (a, b)  Since: base2.1 
Monoid a => Monoid (Op a b)  
Monoid b => Monoid (Fold a b)  
Monad m => Monoid (EndoM m a)  
Monad m => Monoid (ListT m a)  
(Monoid a, Monoid b, Monoid c) => Monoid (a, b, c)  Since: base2.1 
Monoid a => Monoid (Const a b)  
Alternative f => Monoid (Alt f a)  Since: base4.8.0.0 
(Monoid b, Monad m) => Monoid (FoldM m a b)  
Monoid a => Monoid (Constant a b)  
(Monoid a, Monoid b, Monoid c, Monoid d) => Monoid (a, b, c, d)  Since: base2.1 
(Monoid a, Monoid b, Monoid c, Monoid d, Monoid e) => Monoid (a, b, c, d, e)  Since: base2.1 
(Monad m, Monoid r, Semigroup r) => Monoid (Proxy a' a b' b m r)  
Maybe monoid returning the leftmost nonNothing value.
is isomorphic to First
a
, but precedes it
historically.Alt
Maybe
a
>>>
getFirst (First (Just "hello") <> First Nothing <> First (Just "world"))
Just "hello"
Instances
Monad First  
Functor First  
Applicative First  
Foldable First  Since: base4.8.0.0 
Defined in Data.Foldable fold :: Monoid m => First m > m # foldMap :: Monoid m => (a > m) > First a > m # foldr :: (a > b > b) > b > First a > b # foldr' :: (a > b > b) > b > First a > b # foldl :: (b > a > b) > b > First a > b # foldl' :: (b > a > b) > b > First a > b # foldr1 :: (a > a > a) > First a > a # foldl1 :: (a > a > a) > First a > a # elem :: Eq a => a > First a > Bool # maximum :: Ord a => First a > a # minimum :: Ord a => First a > a #  
Traversable First  Since: base4.8.0.0 
Eq a => Eq (First a)  
Ord a => Ord (First a)  
Read a => Read (First a)  
Show a => Show (First a)  
Generic (First a)  
Semigroup (First a)  Since: base4.9.0.0 
Monoid (First a)  Since: base2.1 
Generic1 First  
type Rep (First a)  
Defined in Data.Monoid  
type Rep1 First  
Defined in Data.Monoid 
module Pipes
module Pipes.Concurrent