
You can do similar things with Haskell EDSL-style template systems such as Blaze, except that Haskell is not an OOP language, so you will be using different abstractions instead. Still, the big picture with these is that template building blocks are language elements, and template code and regular code can be mixed freely. The `h1` function from Blaze is just another Haskell function; it takes a MarkdownM () value and returns a MarkdownM (), and because MarkdownM happens to be a Monad, you can use do notation to sequence HTML elements. MarkdownM () is also an instance of IsString, so if you enable -XOverloadedStrings, you can use string literals to produce HTML text nodes. This means that you can write, in plain Haskell: h1 $ do span "Hello, " span "world!" ...and it'll produce <h1><span>Hello, </span><span>world!</span></h1>. For the common case where you have a "master" template and an incarnation, you could write a master template that takes its inner blocks as arguments, e.g.: masterT :: Text -> Html -> Html -> Html masterT titleText headerExtra content = do html $ do header $ do title $ toHtml titleText headerExtra body $ do h1 $ toHtml titleText div ! class_ "content" $ content footer $ "This is my super footer!" If you want something that behaves more like inheritance, the best way IMO would be to define the available blocks as a record type, e.g.: data TemplateBlocks = TemplateBlocks { tbTitleText :: Text , tbHeaderExtra :: Html , tbContent :: Html } -- Then provide some sensible defaults: defTemplateBlocks :: TemplateBlocks defTemplateBlocks = TemplateBlocks { tbTitleText = "My super web app" , tbHeaderExtra = return () , tbContent = return () } -- and then your master template becomes: masterT :: TemplateBlocks -> Html masterT tb = do html $ do header $ do title $ toHtml $ tbTitleText tb tbHeaderExtra tb body $ do h1 $ toHtml $ tbTitleText tb div ! class_ "content" $ tbContent tb footer $ "This is my super footer!" -- And then you can call it with the defaults, overriding -- as needed: masterT $ defTemplateBlocks { tbTitleText = "Homepage" , tbContent = div "Hello, world!" } Now, if you want to use template blocks *inside* template blocks, you need to alter your blocks a tiny bit in order to read other blocks into them: data TemplateBlocks = TemplateBlocks { tbTitleText :: Text , tbHeaderExtra :: TemplateBlocks -> Html , tbContent :: TemplateBlocks -> Html } -- now you could do, for example: masterT $ defTemplateBlocks { tbTitleText = "Homepage" , tbContent tb = div $ do "Hello, world!" "You are here: " toHtml $ tbTitleText tb } Now if MarkupM were implemented as a monad transformer, we could even stack it on top of a MonadReader TemplateBlocks to avoid passing the tb parameter explicitly, and lensify the whole thing, but oh well. (BTW, is anyone aware of any efforts in making Blaze into a transformer?) On Fri, May 09, 2014 at 02:34:52AM -0500, Mike Meyer wrote:
On Fri, May 9, 2014 at 2:11 AM, Tobias Florek
wrote: hi,
The objects generated by the templates don't do much of
anything to let the application author leverage the type system, which is what makes Cheetah stand out from other web template systems.
can you elaborate on that point? i would really like to see an example on what you mean.
for another datapoint, have a look at hastache's use of generics hackage.haskell.org/package/hastache/docs/Text-Hastache.html
I mentioned that briefly in the original message: A Cheetah template is a Python class. It can inherit from Python classes, and Python classes can inherit from it.
A standard usage is to have a page template that sets up headers and footers, and make your pages are all children of that class. While header/footer support is a stock feature of any modern templating system, Cheetah does it by leveraging it's integration into the Python class system instead of providing a specific mechanism for it.
If you need something to happen on the server when the page renders - that doesn't leave any traces in the output - you can subclass the template with a standard Python class and provide that functionality. You then use the Python subclass just like a regular template. Again, the behavior isn't special, but doing it takes no special machinery, just leveraging Cheetah templates being classes.
You can find simple examples of this in the Cheetah docs: http://www.cheetahtemplate.org/docs/users_guide_html_multipage/howWorks.objo...
_______________________________________________ Haskell-Cafe mailing list Haskell-Cafe@haskell.org http://www.haskell.org/mailman/listinfo/haskell-cafe