Safe Haskell | None |
---|---|
Language | Haskell2010 |
With type-of-html are three main goals:
- Type safety
- Modularity
- Performance
Let's check out the type safety in ghci:
Html> td_ (tr_ "a") <interactive>:1:1: error: • 'Tr is not a valid child of 'Td • In the expression: td_ (tr_ "a") In an equation for ‘it’: it = td_ (tr_ "a") <interactive>:1:6: error: • 'Tr can't contain a string • In the first argument of ‘td_’, namely ‘(tr_ "a")’ In the expression: td_ (tr_ "a") In an equation for ‘it’: it = td_ (tr_ "a")
For every child, it is checked if it could possibly be lawful.
The checking is a bit lenient at the moment:
- some elements can't contain itself as any descendant: at the moment we look only at direct children. This allows some (quite exotic) invalid html documents.
- some elements change their permitted content based on attributes: we don't know at compile time the attributes, therefore we always allow content as if all relevant attributes are set.
- some elements can't be brethren: we look only at parent child relations, therefore if you don't specify the parent, it'll compile
Never the less: these cases are seldom. In the vast majority of the time you're only allowed to construct valid html.
Let's talk about modularity:
Rosetrees of html are just ordinary haskell values which can be composed or abstracted over:
Html> let table = table_ . map (tr_ . map td_) Html> :t table table :: ('Td ?> a) => [[a]] -> 'Table > ['Tr > ['Td > a]] Html> table [["A","B"],["C"]] <table><tr><td>A<td>B<tr><td>C</table> Html> import Data.Char Html Data.Char> html_ . body_ . table $ map (\c -> [[c], show $ ord c]) ['a'..'d'] <html><body><table><tr><td>a<td>97<tr><td>b<td>98<tr><td>c<td>99<tr><td>d<td>100</table></body></html>
And here's an example module
{-# LANGUAGE TypeOperators #-} {-# LANGUAGE DataKinds #-} module Main where import Html import Data.Text.Lazy.IO as T import Data.Text.Lazy.Builder as T main :: IO () main = T.putStrLn . T.toLazyText . render . page $ map td_ [1..(10::Int)] page :: 'Tr ?> a => a -> 'Div > ( 'Div > [Char] # 'Div > [Char] # 'Table > 'Tr > a ) page tds = div_ ( div_ "foo" # div_ "bar" # table_ (tr_ tds) )
Please note that the type of page is inferable, so ask ghc-mod or whatever you use to write it for you. If you choose not to write the types, you don't need the language extensions.
Last and fast: performance!
Don't look any further, there is no option for faster html generation. type-of-html up to 10 times faster than blaze-html, which is until now the fastest generation library and the foundation block of lucid and shakespeare.
Wait! 10 times faster? How is this possible? We supercompile lots of parts of the generation process. This is possible thanks to the new features of GHC 8.2: AppendSymbol. We represent tags as kinds and remove according to the html specification omittable closing tags with type families. Afterwards we map these tags to (a :: [Symbol]) and then fold all neighbouring Proxies with AppendSymbol. Afterwards we retrieve the Proxies with symbolVal which will be embedded in the executable as CString. All this happens at compile time. At runtime we do only generate the content and mconcat.
For example, if you write:
render $ div_ "a"
The compiler does actually optimize it to the following:
mconcat [ fromString $ symbolVal (Proxy :: Proxy "<div>") , fromString "a" , fromString $ symbolVal (Proxy :: Proxy "</div>") ]
If you write
render $ div_ (div_ ())
The compiler does actually optimize it to the following:
mconcat [ fromString $ symbolVal (Proxy :: Proxy "<div><div></div></div>") ]
If you write
render $ tr_ (td_ "test")
The compiler does actually optimize it to the following:
mconcat [ fromString $ symbolVal (Proxy :: Proxy "<tr><td>") , fromString "test" , fromString $ symbolVal (Proxy :: Proxy "</tr>") ]
Let's look at core:
We take an extremely simple library
module Minimal where import Html minimal :: String minimal = render ( div_ "a" # div_ "b" # table_ (tr_ (td_ "c")) )
compile it with
ghc -O2 Minimal.hs -ddump-to-file -ddump-simpl -dsuppress-idinfo -dsuppress-module-prefixes -dsuppress-type-applications -dsuppress-uniques
and clean up a bit:
minimal1 :: Addr# minimal1 = "<div>a</div><div>b</div><table><tr><td>c</table>"# minimal :: String minimal = unpackCString# minimal1
Well, that's a perfect optimization! Not only was *all* overhead removed, optional ending tags were chopped off (tr, td). This sort of compiletime optimization isn't for free. Running ghc with -v says that desugaring resulted in 675 types and 5507 coercions: Compile times will increase, some medium size html documents will take 10 secs to compile.
- render :: forall a b. (Document a, Escape b, Monoid b, IsString b) => a -> b
- data (a :: Element) > b where
- data (a :: Element) :> b where
- WithAttributes :: a ?> b => Attributes -> b -> a :> b
- addAttributes :: a ?> b => [(String, String)] -> (a > b) -> a :> b
- data a # b = (:#:) a b
- (#) :: a -> b -> a # b
- type family (a :: Element) ?> b :: Constraint where ...
- class Convert a where
- class (IsString a, Monoid a) => Escape a where
- data Element
- = DOCTYPE
- | A
- | Abbr
- | Acronym
- | Address
- | Applet
- | Area
- | Article
- | Aside
- | Audio
- | B
- | Base
- | Basefont
- | Bdi
- | Bdo
- | Bgsound
- | Big
- | Blink
- | Blockquote
- | Body
- | Br
- | Button
- | Canvas
- | Caption
- | Center
- | Cite
- | Code
- | Col
- | Colgroup
- | Command
- | Content
- | Data
- | Datalist
- | Dd
- | Del
- | Details
- | Dfn
- | Dialog
- | Dir
- | Div
- | Dl
- | Dt
- | Element
- | Em
- | Embed
- | Fieldset
- | Figcaption
- | Figure
- | Font
- | Footer
- | Form
- | Frame
- | Frameset
- | H1
- | H2
- | H3
- | H4
- | H5
- | H6
- | Head
- | Header
- | Hgroup
- | Hr
- | Html
- | I
- | Iframe
- | Image
- | Img
- | Input
- | Ins
- | Isindex
- | Kbd
- | Keygen
- | Label
- | Legend
- | Li
- | Link
- | Listing
- | Main
- | Map
- | Mark
- | Marquee
- | Math
- | Menu
- | Menuitem
- | Meta
- | Meter
- | Multicol
- | Nav
- | Nextid
- | Nobr
- | Noembed
- | Noframes
- | Noscript
- | Object
- | Ol
- | Optgroup
- | Option
- | Output
- | P
- | Param
- | Picture
- | Plaintext
- | Pre
- | Progress
- | Q
- | Rp
- | Rt
- | Rtc
- | Ruby
- | S
- | Samp
- | Script
- | Section
- | Select
- | Shadow
- | Slot
- | Small
- | Source
- | Spacer
- | Span
- | Strike
- | Strong
- | Style
- | Sub
- | Summary
- | Sup
- | Svg
- | Table
- | Tbody
- | Td
- | Template
- | Textarea
- | Tfoot
- | Th
- | Thead
- | Time
- | Title
- | Tr
- | Track
- | Tt
- | U
- | Ul
- | Var
- | Video
- | Wbr
- | Xmp
- doctype_ :: DOCTYPE > ()
- a_ :: A ?> a => a -> A > a
- a_A :: A ?> a => [(String, String)] -> a -> A :> a
- abbr_ :: Abbr ?> a => a -> Abbr > a
- abbr_A :: Abbr ?> a => [(String, String)] -> a -> Abbr :> a
- acronym_ :: Acronym ?> a => a -> Acronym > a
- acronym_A :: Acronym ?> a => [(String, String)] -> a -> Acronym :> a
- address_ :: Address ?> a => a -> Address > a
- address_A :: Address ?> a => [(String, String)] -> a -> Address :> a
- applet_ :: Applet ?> a => a -> Applet > a
- applet_A :: Applet ?> a => [(String, String)] -> a -> Applet :> a
- area_ :: Area > ()
- area_A :: [(String, String)] -> Area :> ()
- article_ :: Article ?> a => a -> Article > a
- article_A :: Article ?> a => [(String, String)] -> a -> Article :> a
- aside_ :: Aside ?> a => a -> Aside > a
- aside_A :: Aside ?> a => [(String, String)] -> a -> Aside :> a
- audio_ :: Audio ?> a => a -> Audio > a
- audio_A :: Audio ?> a => [(String, String)] -> a -> Audio :> a
- b_ :: B ?> a => a -> B > a
- b_A :: B ?> a => [(String, String)] -> a -> B :> a
- base_ :: Base > ()
- base_A :: [(String, String)] -> Base :> ()
- basefont_ :: Basefont ?> a => a -> Basefont > a
- basefont_A :: Basefont ?> a => [(String, String)] -> a -> Basefont :> a
- bdi_ :: Bdi ?> a => a -> Bdi > a
- bdi_A :: Bdi ?> a => [(String, String)] -> a -> Bdi :> a
- bdo_ :: Bdo ?> a => a -> Bdo > a
- bdo_A :: Bdo ?> a => [(String, String)] -> a -> Bdo :> a
- bgsound_ :: Bgsound ?> a => a -> Bgsound > a
- bgsound_A :: Bgsound ?> a => [(String, String)] -> a -> Bgsound :> a
- big_ :: Big ?> a => a -> Big > a
- big_A :: Big ?> a => [(String, String)] -> a -> Big :> a
- blink_ :: Blink ?> a => a -> Blink > a
- blink_A :: Blink ?> a => [(String, String)] -> a -> Blink :> a
- blockquote_ :: Blockquote ?> a => a -> Blockquote > a
- blockquote_A :: Blockquote ?> a => [(String, String)] -> a -> Blockquote :> a
- body_ :: Body ?> a => a -> Body > a
- body_A :: Body ?> a => [(String, String)] -> a -> Body :> a
- br_ :: Br > ()
- br_A :: [(String, String)] -> Br :> ()
- button_ :: Button ?> a => a -> Button > a
- button_A :: Button ?> a => [(String, String)] -> a -> Button :> a
- canvas_ :: Canvas ?> a => a -> Canvas > a
- canvas_A :: Canvas ?> a => [(String, String)] -> a -> Canvas :> a
- caption_ :: Caption ?> a => a -> Caption > a
- caption_A :: Caption ?> a => [(String, String)] -> a -> Caption :> a
- center_ :: Center ?> a => a -> Center > a
- center_A :: Center ?> a => [(String, String)] -> a -> Center :> a
- cite_ :: Cite ?> a => a -> Cite > a
- cite_A :: Cite ?> a => [(String, String)] -> a -> Cite :> a
- code_ :: Code ?> a => a -> Code > a
- code_A :: Code ?> a => [(String, String)] -> a -> Code :> a
- col_ :: Col > ()
- col_A :: [(String, String)] -> Col :> ()
- colgroup_ :: Colgroup ?> a => a -> Colgroup > a
- colgroup_A :: Colgroup ?> a => [(String, String)] -> a -> Colgroup :> a
- command_ :: Command ?> a => a -> Command > a
- command_A :: Command ?> a => [(String, String)] -> a -> Command :> a
- content_ :: Content ?> a => a -> Content > a
- content_A :: Content ?> a => [(String, String)] -> a -> Content :> a
- data_ :: Data ?> a => a -> Data > a
- data_A :: Data ?> a => [(String, String)] -> a -> Data :> a
- datalist_ :: Datalist ?> a => a -> Datalist > a
- datalist_A :: Datalist ?> a => [(String, String)] -> a -> Datalist :> a
- dd_ :: Dd ?> a => a -> Dd > a
- dd_A :: Dd ?> a => [(String, String)] -> a -> Dd :> a
- del_ :: Del ?> a => a -> Del > a
- del_A :: Del ?> a => [(String, String)] -> a -> Del :> a
- details_ :: Details ?> a => a -> Details > a
- details_A :: Details ?> a => [(String, String)] -> a -> Details :> a
- dfn_ :: Dfn ?> a => a -> Dfn > a
- dfn_A :: Dfn ?> a => [(String, String)] -> a -> Dfn :> a
- dialog_ :: Dialog ?> a => a -> Dialog > a
- dialog_A :: Dialog ?> a => [(String, String)] -> a -> Dialog :> a
- dir_ :: Dir ?> a => a -> Dir > a
- dir_A :: Dir ?> a => [(String, String)] -> a -> Dir :> a
- div_ :: Div ?> a => a -> Div > a
- div_A :: Div ?> a => [(String, String)] -> a -> Div :> a
- dl_ :: Dl ?> a => a -> Dl > a
- dl_A :: Dl ?> a => [(String, String)] -> a -> Dl :> a
- dt_ :: Dt ?> a => a -> Dt > a
- dt_A :: Dt ?> a => [(String, String)] -> a -> Dt :> a
- element_ :: Element ?> a => a -> Element > a
- element_A :: Element ?> a => [(String, String)] -> a -> Element :> a
- em_ :: Em ?> a => a -> Em > a
- em_A :: Em ?> a => [(String, String)] -> a -> Em :> a
- embed_ :: Embed > ()
- embed_A :: [(String, String)] -> Embed :> ()
- fieldset_ :: Fieldset ?> a => a -> Fieldset > a
- fieldset_A :: Fieldset ?> a => [(String, String)] -> a -> Fieldset :> a
- figcaption_ :: Figcaption ?> a => a -> Figcaption > a
- figcaption_A :: Figcaption ?> a => [(String, String)] -> a -> Figcaption :> a
- figure_ :: Figure ?> a => a -> Figure > a
- figure_A :: Figure ?> a => [(String, String)] -> a -> Figure :> a
- font_ :: Font ?> a => a -> Font > a
- font_A :: Font ?> a => [(String, String)] -> a -> Font :> a
- footer_ :: Footer ?> a => a -> Footer > a
- footer_A :: Footer ?> a => [(String, String)] -> a -> Footer :> a
- form_ :: Form ?> a => a -> Form > a
- form_A :: Form ?> a => [(String, String)] -> a -> Form :> a
- frame_ :: Frame ?> a => a -> Frame > a
- frame_A :: Frame ?> a => [(String, String)] -> a -> Frame :> a
- frameset_ :: Frameset ?> a => a -> Frameset > a
- frameset_A :: Frameset ?> a => [(String, String)] -> a -> Frameset :> a
- h1_ :: H1 ?> a => a -> H1 > a
- h1_A :: H1 ?> a => [(String, String)] -> a -> H1 :> a
- h2_ :: H2 ?> a => a -> H2 > a
- h2_A :: H2 ?> a => [(String, String)] -> a -> H2 :> a
- h3_ :: H3 ?> a => a -> H3 > a
- h3_A :: H3 ?> a => [(String, String)] -> a -> H3 :> a
- h4_ :: H4 ?> a => a -> H4 > a
- h4_A :: H4 ?> a => [(String, String)] -> a -> H4 :> a
- h5_ :: H5 ?> a => a -> H5 > a
- h5_A :: H5 ?> a => [(String, String)] -> a -> H5 :> a
- h6_ :: H6 ?> a => a -> H6 > a
- h6_A :: H6 ?> a => [(String, String)] -> a -> H6 :> a
- head_ :: Head ?> a => a -> Head > a
- head_A :: Head ?> a => [(String, String)] -> a -> Head :> a
- header_ :: Header ?> a => a -> Header > a
- header_A :: Header ?> a => [(String, String)] -> a -> Header :> a
- hgroup_ :: Hgroup ?> a => a -> Hgroup > a
- hgroup_A :: Hgroup ?> a => [(String, String)] -> a -> Hgroup :> a
- hr_ :: Hr > ()
- hr_A :: [(String, String)] -> Hr :> ()
- html_ :: Html ?> a => a -> Html > a
- html_A :: Html ?> a => [(String, String)] -> a -> Html :> a
- i_ :: I ?> a => a -> I > a
- i_A :: I ?> a => [(String, String)] -> a -> I :> a
- iframe_ :: Iframe > ()
- iframe_A :: [(String, String)] -> Iframe :> ()
- image_ :: Image ?> a => a -> Image > a
- image_A :: Image ?> a => [(String, String)] -> a -> Image :> a
- img_ :: Img > ()
- img_A :: [(String, String)] -> Img :> ()
- input_ :: Input ?> a => a -> Input > a
- input_A :: Input ?> a => [(String, String)] -> a -> Input :> a
- ins_ :: Ins ?> a => a -> Ins > a
- ins_A :: Ins ?> a => [(String, String)] -> a -> Ins :> a
- isindex_ :: Isindex ?> a => a -> Isindex > a
- isindex_A :: Isindex ?> a => [(String, String)] -> a -> Isindex :> a
- kbd_ :: Kbd ?> a => a -> Kbd > a
- kbd_A :: Kbd ?> a => [(String, String)] -> a -> Kbd :> a
- keygen_ :: Keygen ?> a => a -> Keygen > a
- keygen_A :: Keygen ?> a => [(String, String)] -> a -> Keygen :> a
- label_ :: Label ?> a => a -> Label > a
- label_A :: Label ?> a => [(String, String)] -> a -> Label :> a
- legend_ :: Legend ?> a => a -> Legend > a
- legend_A :: Legend ?> a => [(String, String)] -> a -> Legend :> a
- li_ :: Li ?> a => a -> Li > a
- li_A :: Li ?> a => [(String, String)] -> a -> Li :> a
- link_ :: Link > ()
- link_A :: [(String, String)] -> Link :> ()
- listing_ :: Listing ?> a => a -> Listing > a
- listing_A :: Listing ?> a => [(String, String)] -> a -> Listing :> a
- main_ :: Main ?> a => a -> Main > a
- main_A :: Main ?> a => [(String, String)] -> a -> Main :> a
- map_ :: Map ?> a => a -> Map > a
- map_A :: Map ?> a => [(String, String)] -> a -> Map :> a
- mark_ :: Mark ?> a => a -> Mark > a
- mark_A :: Mark ?> a => [(String, String)] -> a -> Mark :> a
- marquee_ :: Marquee ?> a => a -> Marquee > a
- marquee_A :: Marquee ?> a => [(String, String)] -> a -> Marquee :> a
- math_ :: Math ?> a => a -> Math > a
- math_A :: Math ?> a => [(String, String)] -> a -> Math :> a
- menu_ :: Menu ?> a => a -> Menu > a
- menu_A :: Menu ?> a => [(String, String)] -> a -> Menu :> a
- menuitem_ :: Menuitem > ()
- menuitem_A :: [(String, String)] -> Menuitem :> ()
- meta_ :: Meta > ()
- meta_A :: [(String, String)] -> Meta :> ()
- meter_ :: Meter ?> a => a -> Meter > a
- meter_A :: Meter ?> a => [(String, String)] -> a -> Meter :> a
- multicol_ :: Multicol ?> a => a -> Multicol > a
- multicol_A :: Multicol ?> a => [(String, String)] -> a -> Multicol :> a
- nav_ :: Nav ?> a => a -> Nav > a
- nav_A :: Nav ?> a => [(String, String)] -> a -> Nav :> a
- nextid_ :: Nextid ?> a => a -> Nextid > a
- nextid_A :: Nextid ?> a => [(String, String)] -> a -> Nextid :> a
- nobr_ :: Nobr ?> a => a -> Nobr > a
- nobr_A :: Nobr ?> a => [(String, String)] -> a -> Nobr :> a
- noembed_ :: Noembed ?> a => a -> Noembed > a
- noembed_A :: Noembed ?> a => [(String, String)] -> a -> Noembed :> a
- noframes_ :: Noframes ?> a => a -> Noframes > a
- noframes_A :: Noframes ?> a => [(String, String)] -> a -> Noframes :> a
- noscript_ :: Noscript ?> a => a -> Noscript > a
- noscript_A :: Noscript ?> a => [(String, String)] -> a -> Noscript :> a
- object_ :: Object ?> a => a -> Object > a
- object_A :: Object ?> a => [(String, String)] -> a -> Object :> a
- ol_ :: Ol ?> a => a -> Ol > a
- ol_A :: Ol ?> a => [(String, String)] -> a -> Ol :> a
- optgroup_ :: Optgroup ?> a => a -> Optgroup > a
- optgroup_A :: Optgroup ?> a => [(String, String)] -> a -> Optgroup :> a
- option_ :: Option ?> a => a -> Option > a
- option_A :: Option ?> a => [(String, String)] -> a -> Option :> a
- output_ :: Output ?> a => a -> Output > a
- output_A :: Output ?> a => [(String, String)] -> a -> Output :> a
- p_ :: P ?> a => a -> P > a
- p_A :: P ?> a => [(String, String)] -> a -> P :> a
- param_ :: Param > ()
- param_A :: [(String, String)] -> Param :> ()
- picture_ :: Picture ?> a => a -> Picture > a
- picture_A :: Picture ?> a => [(String, String)] -> a -> Picture :> a
- plaintext_ :: Plaintext ?> a => a -> Plaintext > a
- plaintext_A :: Plaintext ?> a => [(String, String)] -> a -> Plaintext :> a
- pre_ :: Pre ?> a => a -> Pre > a
- pre_A :: Pre ?> a => [(String, String)] -> a -> Pre :> a
- progress_ :: Progress ?> a => a -> Progress > a
- progress_A :: Progress ?> a => [(String, String)] -> a -> Progress :> a
- q_ :: Q ?> a => a -> Q > a
- q_A :: Q ?> a => [(String, String)] -> a -> Q :> a
- rp_ :: Rp ?> a => a -> Rp > a
- rp_A :: Rp ?> a => [(String, String)] -> a -> Rp :> a
- rt_ :: Rt ?> a => a -> Rt > a
- rt_A :: Rt ?> a => [(String, String)] -> a -> Rt :> a
- rtc_ :: Rtc ?> a => a -> Rtc > a
- rtc_A :: Rtc ?> a => [(String, String)] -> a -> Rtc :> a
- ruby_ :: Ruby ?> a => a -> Ruby > a
- ruby_A :: Ruby ?> a => [(String, String)] -> a -> Ruby :> a
- s_ :: S ?> a => a -> S > a
- s_A :: S ?> a => [(String, String)] -> a -> S :> a
- samp_ :: Samp ?> a => a -> Samp > a
- samp_A :: Samp ?> a => [(String, String)] -> a -> Samp :> a
- script_ :: Script ?> a => a -> Script > a
- script_A :: Script ?> a => [(String, String)] -> a -> Script :> a
- section_ :: Section ?> a => a -> Section > a
- section_A :: Section ?> a => [(String, String)] -> a -> Section :> a
- select_ :: Select ?> a => a -> Select > a
- select_A :: Select ?> a => [(String, String)] -> a -> Select :> a
- shadow_ :: Shadow ?> a => a -> Shadow > a
- shadow_A :: Shadow ?> a => [(String, String)] -> a -> Shadow :> a
- slot_ :: Slot ?> a => a -> Slot > a
- slot_A :: Slot ?> a => [(String, String)] -> a -> Slot :> a
- small_ :: Small ?> a => a -> Small > a
- small_A :: Small ?> a => [(String, String)] -> a -> Small :> a
- source_ :: Source > ()
- source_A :: [(String, String)] -> Source :> ()
- spacer_ :: Spacer ?> a => a -> Spacer > a
- spacer_A :: Spacer ?> a => [(String, String)] -> a -> Spacer :> a
- span_ :: Span ?> a => a -> Span > a
- span_A :: Span ?> a => [(String, String)] -> a -> Span :> a
- strike_ :: Strike ?> a => a -> Strike > a
- strike_A :: Strike ?> a => [(String, String)] -> a -> Strike :> a
- strong_ :: Strong ?> a => a -> Strong > a
- strong_A :: Strong ?> a => [(String, String)] -> a -> Strong :> a
- style_ :: Style ?> a => a -> Style > a
- style_A :: Style ?> a => [(String, String)] -> a -> Style :> a
- sub_ :: Sub ?> a => a -> Sub > a
- sub_A :: Sub ?> a => [(String, String)] -> a -> Sub :> a
- summary_ :: Summary ?> a => a -> Summary > a
- summary_A :: Summary ?> a => [(String, String)] -> a -> Summary :> a
- sup_ :: Sup ?> a => a -> Sup > a
- sup_A :: Sup ?> a => [(String, String)] -> a -> Sup :> a
- svg_ :: Svg ?> a => a -> Svg > a
- svg_A :: Svg ?> a => [(String, String)] -> a -> Svg :> a
- table_ :: Table ?> a => a -> Table > a
- table_A :: Table ?> a => [(String, String)] -> a -> Table :> a
- tbody_ :: Tbody ?> a => a -> Tbody > a
- tbody_A :: Tbody ?> a => [(String, String)] -> a -> Tbody :> a
- td_ :: Td ?> a => a -> Td > a
- td_A :: Td ?> a => [(String, String)] -> a -> Td :> a
- template_ :: Template ?> a => a -> Template > a
- template_A :: Template ?> a => [(String, String)] -> a -> Template :> a
- textarea_ :: Textarea ?> a => a -> Textarea > a
- textarea_A :: Textarea ?> a => [(String, String)] -> a -> Textarea :> a
- tfoot_ :: Tfoot ?> a => a -> Tfoot > a
- tfoot_A :: Tfoot ?> a => [(String, String)] -> a -> Tfoot :> a
- th_ :: Th ?> a => a -> Th > a
- th_A :: Th ?> a => [(String, String)] -> a -> Th :> a
- thead_ :: Thead ?> a => a -> Thead > a
- thead_A :: Thead ?> a => [(String, String)] -> a -> Thead :> a
- time_ :: Time ?> a => a -> Time > a
- time_A :: Time ?> a => [(String, String)] -> a -> Time :> a
- title_ :: Title ?> a => a -> Title > a
- title_A :: Title ?> a => [(String, String)] -> a -> Title :> a
- tr_ :: Tr ?> a => a -> Tr > a
- tr_A :: Tr ?> a => [(String, String)] -> a -> Tr :> a
- track_ :: Track > ()
- track_A :: [(String, String)] -> Track :> ()
- tt_ :: Tt ?> a => a -> Tt > a
- tt_A :: Tt ?> a => [(String, String)] -> a -> Tt :> a
- u_ :: U ?> a => a -> U > a
- u_A :: U ?> a => [(String, String)] -> a -> U :> a
- ul_ :: Ul ?> a => a -> Ul > a
- ul_A :: Ul ?> a => [(String, String)] -> a -> Ul :> a
- var_ :: Var ?> a => a -> Var > a
- var_A :: Var ?> a => [(String, String)] -> a -> Var :> a
- video_ :: Video ?> a => a -> Video > a
- video_A :: Video ?> a => [(String, String)] -> a -> Video :> a
- wbr_ :: Wbr > ()
- wbr_A :: [(String, String)] -> Wbr :> ()
- xmp_ :: Xmp ?> a => a -> Xmp > a
- xmp_A :: Xmp ?> a => [(String, String)] -> a -> Xmp :> a
Documentation
render :: forall a b. (Document a, Escape b, Monoid b, IsString b) => a -> b Source #
Render a html document. The resulting type can be a String, strict Text, lazy Text, or Builder. For performance it is recommended to use a lazy Text or a Builder.
>>>
render "a" :: String
"a"
>>>
render (div_ "a") :: Text
"<div>a</div>"
For prototyping, there's as well a Show instance:
>>>
i_ "a"
<i>a</i>
Please note the extra quotes for String when using show:
>>>
show "a" == render "a"
False
>>>
show img_ == render img_
True
data (a :: Element) > b where infixr 8 Source #
Descend to a valid child of an element. It is recommended to use the predefined elements.
>>>
Child "a" :: 'Div > String
<div>a</div>
>>>
div_ "a"
<div>a</div>
data (a :: Element) :> b where infixr 8 Source #
Decorate an element with attributes and descend to a valid child.
>>>
WithAttributes [("foo","bar")] "a" :: 'Div :> String
<div foo=bar>a</div>
WithAttributes :: a ?> b => Attributes -> b -> a :> b |
Combine two elements sequentially.
>>>
render (i_ () # div_ ()) :: String
"<i></i><div></div>"
(:#:) a b |
type family (a :: Element) ?> b :: Constraint where ... Source #
Check whether b
is a valid child of a
. You'll propably never
need to call this directly. Through a GADT, it is enforced that
every child is lawful.
The only way to circumvent this would be to use undefined
or
error
in combination with only type level values.
>>>
undefined :: 'Div > ('Html > ())
<div><html></html></div>
>>>
undefined :: 'Div > ('Html > Proxy "a")
<div><html>a</html></div>
>>>
undefined :: 'Div > ('Html > String)
<div><html>*** Exception: Prelude.undefined
a ?> (b # c) = (a ?> b, a ?> c) | |
a ?> (b > _) = MaybeTypeError a b (TestPaternity (SingleElement b) (GetInfo a) (GetInfo b)) | |
a ?> (b :> _) = MaybeTypeError a b (TestPaternity (SingleElement b) (GetInfo a) (GetInfo b)) | |
a ?> (Maybe b) = a ?> b | |
a ?> (Either b c) = (a ?> b, a ?> c) | |
a ?> (f (b > c)) = a ?> (b > c) | |
a ?> (f (b :> c)) = a ?> (b > c) | |
a ?> (f (b # c)) = a ?> (b # c) | |
a ?> () = () | |
a ?> (b -> c) = TypeError (Text "Html elements can't contain functions") | |
a ?> b = CheckString a |
The data type of all html elements and the kind of elements.
DOCTYPE | |
A | |
Abbr | |
Acronym | Deprecated: This is an obsolete html element and should not be used. |
Address | |
Applet | Deprecated: This is an obsolete html element and should not be used. |
Area | |
Article | |
Aside | |
Audio | |
B | |
Base | |
Basefont | Deprecated: This is an obsolete html element and should not be used. |
Bdi | |
Bdo | |
Bgsound | |
Big | Deprecated: This is an obsolete html element and should not be used. |
Blink | Deprecated: This is an obsolete html element and should not be used. |
Blockquote | |
Body | |
Br | |
Button | |
Canvas | |
Caption | |
Center | Deprecated: This is an obsolete html element and should not be used. |
Cite | |
Code | |
Col | |
Colgroup | |
Command | Deprecated: This is an obsolete html element and should not be used. |
Content | Deprecated: This is an obsolete html element and should not be used. |
Data | |
Datalist | |
Dd | |
Del | |
Details | |
Dfn | |
Dialog | |
Dir | Deprecated: This is an obsolete html element and should not be used. |
Div | |
Dl | |
Dt | |
Element | |
Em | |
Embed | |
Fieldset | |
Figcaption | |
Figure | |
Font | Deprecated: This is an obsolete html element and should not be used. |
Footer | |
Form | |
Frame | Deprecated: This is an obsolete html element and should not be used. |
Frameset | Deprecated: This is an obsolete html element and should not be used. |
H1 | |
H2 | |
H3 | |
H4 | |
H5 | |
H6 | |
Head | |
Header | |
Hgroup | |
Hr | |
Html | |
I | |
Iframe | |
Image | |
Img | |
Input | |
Ins | |
Isindex | Deprecated: This is an obsolete html element and should not be used. |
Kbd | |
Keygen | Deprecated: This is an obsolete html element and should not be used. |
Label | |
Legend | |
Li | |
Link | |
Listing | Deprecated: This is an obsolete html element and should not be used. |
Main | |
Map | |
Mark | |
Marquee | Deprecated: This is an obsolete html element and should not be used. |
Math | |
Menu | |
Menuitem | |
Meta | |
Meter | |
Multicol | Deprecated: This is an obsolete html element and should not be used. |
Nav | |
Nextid | Deprecated: This is an obsolete html element and should not be used. |
Nobr | |
Noembed | Deprecated: This is an obsolete html element and should not be used. |
Noframes | |
Noscript | |
Object | |
Ol | |
Optgroup | |
Option | |
Output | |
P | |
Param | |
Picture | |
Plaintext | Deprecated: This is an obsolete html element and should not be used. |
Pre | |
Progress | |
Q | |
Rp | |
Rt | |
Rtc | |
Ruby | |
S | |
Samp | |
Script | |
Section | |
Select | |
Shadow | Deprecated: This is an obsolete html element and should not be used. |
Slot | |
Small | |
Source | |
Spacer | Deprecated: This is an obsolete html element and should not be used. |
Span | |
Strike | Deprecated: This is an obsolete html element and should not be used. |
Strong | |
Style | |
Sub | |
Summary | |
Sup | |
Svg | |
Table | |
Tbody | |
Td | |
Template | |
Textarea | |
Tfoot | |
Th | |
Thead | |
Time | |
Title | |
Tr | |
Track | |
Tt | Deprecated: This is an obsolete html element and should not be used. |
U | |
Ul | |
Var | |
Video | |
Wbr | |
Xmp | Deprecated: This is an obsolete html element and should not be used. |
blockquote_ :: Blockquote ?> a => a -> Blockquote > a Source #
blockquote_A :: Blockquote ?> a => [(String, String)] -> a -> Blockquote :> a Source #
figcaption_ :: Figcaption ?> a => a -> Figcaption > a Source #
figcaption_A :: Figcaption ?> a => [(String, String)] -> a -> Figcaption :> a Source #