The purpose of the
snap-predicates package is to facilitate the
convenient definition of safe Snap handlers. Here safety
means that a handler can declare all pre-conditions which must be
fulfilled such that the handler can produce a successful response.
It is then statically guaranteed that the handler will not be
invoked if any of these pre-conditions fails.
Delta can in most instances be ignored, i.e. set to 0.
It's purpose is as a measure of distance for those predicates which evaluate
T but some may be "closer" in some way than others. An
example is for instance HTTP content-negotiations (cf.
Predicatep a where type
TValp apply :: p -> a ->
All predicates are instances of this type-class, which does not
specify the type against which the predicate is evaluated, nor the types
of actual meta-data for the true/false case of the Boolean returned.
Snap related predicates are normally defined against
and in case they fail, they return a status code and an optional message.
Predicates may utilise the stateful
Env to cache intermediate
results accross multiple evaluations, i.e. a resource may be declared multiple
times with different sets of predicates which means that in case a predicate
is part of more than one set it is evaluated multiple times for the same
input data. As an optimisation it may be beneficial to store intermediate
Env and re-use them later (cf. the implementation
Besides these type definitions, there are some ways to connect two
Predicates to form a new one as the logical
OR or the
AND of its parts. These are:
:&: evaluates to
T it has to combine
the meta-data of both predicates, and it uses the product type
:*: for this. This type also has a right-associative
data constructor using the same symbol, so one can combine many predicates
without having to nest the meta-data pairs.
OR case, the two predicates have potentially meta-data of
different types, so we use a sum type
Either whenever we combine
two predicates with
:||:. For convenience a type-alias
:+: is defined for
Either, which allows simple infix
notation. However, for the common case where both predicates have
meta-data of the same type, there is often no need to distinguish which
OR-branch was true. In this case, the
can be used.
As an example of how these operators are used, see below in section "Routes".
data Param = Param
ByteStringderiving Eq instance
TValParam = ByteString apply (Param x) r = case params r x of  -> return (F (
Error400 (Just $ "Expected parameter '" <> x <> "'."))) (v:_) -> return (T  v)
This is a simple example looking for the existence of a
with the given name. In the success case, the parameter value is returned.
As mentioned before, Snap predicates usually fix the type
Predicate above to
Request. The associated
TVal denote the meta-data
types of the predicate. In this example, the meta-date type is
Error which contains a status
code and an optional message.
So how are
Predicates used in some Snap application?
One way is to just evaluate them against a given request inside a snap handler, e.g.
Snap() someHandler = do req <-
Param"baz") req of
:*:bazValue) -> ...
Errorst msg)) -> ...
FNothing -> ...
RoutesSnap () sitemap = do
get"/a" handlerA $
get"/b" handlerB $
get"/c" handlerC $
Error410 (Just "Gone."))
post"/d" handlerD $
post"/e" handlerE $
The handlers then encode their pre-conditions in their type-signature:
:*:ByteString -> Snap () handlerB ::
:*:ByteString -> Snap () handlerC ::
:*:Char -> Snap () handlerD ::
Protobuf-> Snap () handlerE ::
Xml-> Snap ()
The type-declaration of a handler has to match the corresponding predicate,
i.e. the type of the predicate's
T meta-data value:
Given the route and handler definitions above, one can then integrate
with Snap via
expandRoutes, which turns the
Routes monad into a list of
Additionally routes can be turned into Strings via
MonadSnap m => [(ByteString, m ())]