]>
Commit | Line | Data |
---|---|---|
675085c2 JG |
1 | # blazeT [![Build Status](https://travis-ci.org/johannesgerer/blazeT.svg?branch=master)](https://travis-ci.org/johannesgerer/blazeT) [![Hackage](https://img.shields.io/hackage/v/blazeT.svg)](https://hackage.haskell.org/package/blazeT) |
2 | ||
3 | A true monad (transformer) version of the | |
4 | [blaze-markup](https://hackage.haskell.org/package/blaze-markup) and | |
5 | [blaze-html](https://hackage.haskell.org/package/blaze-html) | |
bd93b7c0 | 6 | libraries: |
675085c2 | 7 | |
bd93b7c0 JG |
8 | > BlazeHtml is a blazingly fast HTML combinator library for the |
9 | > Haskell programming language. It embeds HTML templates in Haskell | |
10 | > code for optimal efficiency and composability. | |
11 | ||
12 | — from https://jaspervdj.be/blaze/. | |
13 | ||
14 | ## What’s wrong with blaze? | |
15 | ||
16 | Blaze’s `Markup` and `Html` **cannot be used as Monads**, let alone Monad transformers. | |
675085c2 JG |
17 | |
18 | While blaze's `Markup` and `Html` types have `Monad` instances and can | |
bd93b7c0 JG |
19 | leverage the concise `do` notation, they do not satisfy the |
20 | [Monad Laws](https://hackage.haskell.org/package/base-4.8.0.0/docs/Control-Monad.html#t:Monad). | |
675085c2 | 21 | |
bd93b7c0 | 22 | ## How do Monads help? - Use Cases |
675085c2 JG |
23 | |
24 | The `MarkupT` Monad Transformer enables us to write Markup (e.g. HTML) | |
25 | templates that have access to all those Monads you cannot live without | |
26 | anymore. | |
27 | ||
bd93b7c0 JG |
28 | The first things that come to mind: |
29 | ||
30 | * Accessing an environment | |
31 | ([MonadReader](https://hackage.haskell.org/package/mtl-2.2.1/docs/Control-Monad-Reader-Class.html)) | |
32 | * Logging and other diagnostic output | |
675085c2 | 33 | ([MonadWriter](https://hackage.haskell.org/package/mtl-2.2.1/docs/Control-Monad-Writer-Class.html)), |
bd93b7c0 JG |
34 | * `IO` (e.g. for database access) |
35 | ||
675085c2 | 36 | |
bd93b7c0 | 37 | The reason for the existence of this library is its use |
675085c2 JG |
38 | in [Lykah](http://johannesgerer.com/Lykah), which powers my personal |
39 | website | |
40 | [http://johannesgerer.com](http://johannesgerer.com/johannesgerer.com). In | |
41 | Lykah, the HTML templates have access to the whole site structure (to | |
95eb4d6a JG |
42 | build things like menus or blog post lists) and automatically check, |
43 | insert and keep track of referenced pages and assets, which turns out | |
44 | to be very useful functionality of a static website generator. | |
675085c2 | 45 | |
bd93b7c0 | 46 | ## Usage |
675085c2 | 47 | |
bd93b7c0 | 48 | ### Integrating with your existing code |
675085c2 JG |
49 | |
50 | The library is intended to serve as a drop-in replacement for the | |
51 | `blaze-markup` and `blaze-html` libraries and should be backwards | |
52 | compatible: | |
53 | ||
54 | Simply replace your `module Text.Blaze.*` imports with `module | |
55 | Text.BlazeT.*` and it should give the same results. | |
56 | ||
57 | For usage of blaze check out | |
58 | their [documentation](https://jaspervdj.be/blaze/). | |
59 | ||
bd93b7c0 | 60 | ### Unleash the monads |
675085c2 | 61 | |
95eb4d6a JG |
62 | [Text.BlazeT](https://hackage.haskell.org/package/blazeT/docs/Text-BlazeT.html) |
63 | exports `runWith` and `execWith`, which work on any | |
64 | `Text.BlazeT.Renderer.*`. The rendered markup will be returned within | |
65 | the base monad, whose actions can be | |
66 | [`lift`ed](https://hackage.haskell.org/package/transformers-0.5.2.0/docs/Control-Monad-Trans-Class.html) | |
67 | into the Markup, as shown in the following example (from | |
68 | [here](src/Readme.hs)): | |
675085c2 | 69 | |
95eb4d6a JG |
70 | ```Haskell |
71 | {-# LANGUAGE OverloadedStrings #-} | |
72 | ||
73 | import Data.Time (getCurrentTime) | |
74 | import Text.BlazeT.Html5 hiding (main) | |
75 | import Text.BlazeT.Renderer.String | |
76 | import Control.Monad.Trans.Class (lift) | |
77 | ||
78 | -- Backwords compatible Blaze HTML | |
79 | old :: Markup | |
80 | old = do | |
81 | p $ "created with blaze-html" | |
82 | ||
83 | -- BlazeT HTML with lifted IO actions | |
84 | new :: MarkupT IO () | |
85 | new = do | |
86 | time <- lift getCurrentTime | |
87 | p $ string $ "created with blazeT at " ++ show time | |
88 | ||
89 | main :: IO () | |
90 | main = do | |
91 | putStrLn $ renderMarkup old | |
92 | putStrLn =<< execWith renderMarkup new | |
93 | ||
94 | ``` | |
95 | ||
96 | prints: | |
97 | ||
98 | ```HTML | |
99 | <p>created with blaze-html</p> | |
100 | <p>created with blazeT at 2016-10-26 01:09:16.969147361 UTC</p> | |
101 | ``` | |
102 | ||
bd93b7c0 | 103 | ## Installation |
95eb4d6a JG |
104 | |
105 | 1. To make it available on your system (or sandbox) use `cabal install blazeT`. | |
106 | ||
107 | 2. To play around with the source, obtain by cloning this repo or use | |
108 | `cabal get blazet`, enter the directory and run: | |
109 | ||
110 | ```bash | |
111 | cabal sandbox init #optional | |
112 | cabal install | |
113 | ``` | |
114 | ||
bd93b7c0 | 115 | ## Documentation on [Hackage](https://hackage.haskell.org/package/blazeT) |
675085c2 | 116 | |
bd93b7c0 | 117 | ## Implementation |
675085c2 | 118 | |
95eb4d6a | 119 | ... is contained |
675085c2 JG |
120 | in |
121 | [Text.BlazeT.Internals](https://hackage.haskell.org/package/blazeT/docs/Text-BlazeT-Internals.html). | |
122 | ||
95eb4d6a JG |
123 | Everything is build around the simple `newtype` definition of the |
124 | `MarkupT` transformer, which makes use | |
125 | the | |
126 | [Monoid](https://hackage.haskell.org/package/base-4.7.0.2/docs/Data-Monoid.html) instance | |
bd93b7c0 | 127 | of `Blaze.Markup` and is simply a `WriterT` writing `Blaze.Markup`: |
675085c2 JG |
128 | |
129 | ```Haskell | |
130 | newtype MarkupT m a = MarkupT { fromMarkupT :: WriterT B.Markup m a } | |
131 | ``` | |
132 | ||
95eb4d6a JG |
133 | The old `Text.Blaze.Markup` type is replaced by a rank-2 version of |
134 | the transformer: | |
135 | ||
136 | ```Haskell | |
137 | type Markup = forall m . Monad m => MarkupT m () | |
138 | ``` | |
139 | ||
675085c2 | 140 | Wrappers used to lift all `Blaze` entities into `BlazeT` are trivially |
95eb4d6a JG |
141 | expressible using basic `WriterT` class methods. Wrapping |
142 | `Blaze.Markup` is simply `WriterT.tell`: | |
675085c2 JG |
143 | |
144 | ```Haskell | |
145 | wrapMarkupT :: Monad m => B.Markup -> MarkupT m () | |
146 | wrapMarkupT = tell | |
147 | ``` | |
148 | Wrapping functions that modify `Blaze.Markup` is simply `WriterT.censor`: | |
149 | ||
150 | ```Haskell | |
151 | wrapMarkupT2 :: Monad m => (B.Markup -> B.Markup) -> MarkupT m a -> MarkupT m a | |
152 | wrapMarkupT2 = censor | |
153 | ``` | |
154 |