ginger- An implementation of the Jinja2 template language in Haskell

Safe HaskellNone




GVal is a generic unitype value, representing the kind of values that Ginger can understand.

Most of the types in this module are parametrized over an m type, which is the host monad for template execution, as passed to runGingerT. For most kinds of values, m is transparent, and in many cases a ToGVal instance can be written that works for all possible m; the reason we need to parametrize the values themselves over the carrier monad is because we want to support impure functions, which requires access to the underlying carrier monad (e.g. IO).


The Ginger Value type

data GVal m Source

A variant type designed as the unitype for the template language. Any value referenced in a template, returned from within a template, or used in a template context, will be a GVal. m, in most cases, should be a Monad.

| Some laws apply here, most notably: - when isNull is True, then all of asFunction, asText, asNumber, asHtml, asList, asDictItems, and length should produce Nothing - when isNull is True, then asBoolean should produce False - when asNumber is not Nothing, then asBoolean should only return False for exactly zero - Nothing-ness of length should match one or both of asList / asDictItems




asList :: Maybe [GVal m]

Convert value to list, if possible

asDictItems :: Maybe [(Text, GVal m)]

Convert value to association list ("dictionary"), if possible

asLookup :: Maybe (Text -> Maybe (GVal m))

Convert value to a lookup function

asHtml :: Html

Render value as HTML

asText :: Text

Render value as plain-text

asBoolean :: Bool

Get value's truthiness

asNumber :: Maybe Scientific

Convert value to a number, if possible

asFunction :: Maybe (Function m)

Access value as a callable function, if it is one

length :: Maybe Int

Get length of value, if it is a collection (list/dict)

isNull :: Bool

Check if the value is null


ToGVal m (GVal m) Source

Trivial instance for GVal itself.

Show (GVal m) Source

For convenience, Show is implemented in a way that looks similar to JavaScript / JSON

IsString (GVal m) Source

String -> GVal conversion uses the IsString class; because String is an alias for '[Char]', there is also a ToGVal instance for String, but it marshals strings as lists of characters, i.e., calling toGVal on a string produces a list of characters on the GVal side.

Default (GVal m) Source

The default GVal is equivalent to NULL.

ToHtml (GVal m) Source

Converting to HTML hooks into the ToHtml instance for Text for most tags. Tags that have no obvious textual representation render as empty HTML.

Representing functions as GVals

type Function m = [(Maybe Text, GVal m)] -> m (GVal m) Source

A function that can be called from within a template execution context.

matchFuncArgs :: [Text] -> [(Maybe Text, GVal m)] -> (HashMap Text (GVal m), [GVal m], HashMap Text (GVal m)) Source

Match arguments passed to a function at runtime against a list of declared argument names. matchFuncArgs argNames argsPassed returns (matchedArgs, positionalArgs, namedArgs), where matchedArgs is a list of arguments matched against declared names (by name or by position), positionalArgs are the unused positional (unnamed) arguments, and namedArgs are the unused named arguments.

Marshalling from Haskell to GVal

class ToGVal m a where Source

Types that implement conversion to GVal.


toGVal :: a -> GVal m Source


ToGVal m Value Source

Convert Aeson Values to GVals over an arbitrary host monad. Because JSON cannot represent functions, this conversion will never produce a Function.

ToGVal m Html Source

This instance is slightly wrong; the asBoolean, asNumber, and asText methods all treat the HTML source as plain text. We do this to avoid parsing the HTML back into a Text (and dealing with possible parser errors); the reason this instance exists at all is that we still want to be able to pass pre-rendered HTML around sometimes, and as long as we don't call any numeric or string functions on it, everything is fine. When such HTML values accidentally do get used as strings, the HTML source will bleed into the visible text, but at least this will not introduce an XSS vulnerability.

It is therefore recommended to avoid passing Html values into templates, and also to avoid calling any string functions on Html values inside templates (e.g. capturing macro output and then passing it through a textual filter).

ToGVal m Text Source 
ToGVal m Text Source 
ToGVal m Char Source

Single characters are treated as length-1 Texts.

ToGVal m Bool Source

Booleans render as 1 or empty string, and otherwise behave as expected.

ToGVal m Scientific Source 
ToGVal m Integer Source 
ToGVal m Int Source 
ToGVal m v => ToGVal m [v] Source

Haskell lists become list-like GVals

ToGVal m v => ToGVal m (Maybe v) Source

Nothing becomes NULL, Just unwraps.

ToGVal m (GVal m) Source

Trivial instance for GVal itself.

ToGVal m v => ToGVal m (HashMap Text v) Source

HashMap of Text becomes a dictionary-like GVal

scientificToText :: Scientific -> Text Source

Silly helper function, needed to bypass the default Show instance of Scientific in order to make integral Scientifics look like integers.

fromFunction :: Function m -> GVal m Source

Turn a Function into a GVal

Inspecting GVals / Marshalling GVal to Haskell

isList :: GVal m -> Bool Source

Check if the given GVal is a list-like object

isDict :: GVal m -> Bool Source

Check if the given GVal is a dictionary-like object

lookupIndex :: Int -> GVal m -> Maybe (GVal m) Source

Treat a GVal as a flat list and look up a value by integer index. If the value is not a List, or if the index exceeds the list length, return Nothing.

lookupIndexMay :: Maybe Int -> GVal m -> Maybe (GVal m) Source

Helper function; look up a value by an integer index when the index may or may not be available. If no index is given, return Nothing.

lookupKey :: Text -> GVal m -> Maybe (GVal m) Source

Strictly-typed lookup: treat value as a dictionary-like object and look up the value at a given key.

lookupLoose :: GVal m -> GVal m -> Maybe (GVal m) Source

Loosely-typed lookup: try dictionary-style lookup first (treat index as a string, and container as a dictionary), if that doesn't yield anything (either because the index is not string-ish, or because the container doesn't provide dictionary-style access), try index-based lookup.

keys :: GVal m -> Maybe [Text] Source

Treat a GVal as a dictionary and list all the keys, with no particular ordering.

toNumber :: GVal m -> Maybe Scientific Source

Convert a GVal to a number.

toInt :: GVal m -> Maybe Int Source

Convert a GVal to an Int. The conversion will fail when the value is not numeric, and also if it is too large to fit in an Int.

toBoolean :: GVal m -> Bool Source

Loose cast to boolean.

Numeric zero, empty strings, empty lists, empty objects, Null, and boolean False are considered falsy, anything else (including functions) is considered true-ish.

toFunction :: GVal m -> Maybe (Function m) Source

Dynamically cast to a function. This yields Just a Function if the value is a function, Nothing if it's not.