Using Hamlet for templating

17 views
Skip to first unread message

Nolan Darilek

unread,
Aug 12, 2014, 10:04:15 PM8/12/14
to ha...@googlegroups.com
I apologize for the quantity of newbie questions and the length of this.
I want to get into Haskell, web development is what I do, so it seemed
like the best avenue to start out. Yesod overwhelmed me with
quasi-quotes and TH, and I really just want to build everything from the
ground up, but that's also leading to lots of frustrations.

I like Hamlet as a good compromise between writing HTML by hand but
omitting unnecessary aspects like closing tags. I can't get Hamlet
working in a route, though. I have:

home :: RouteT Sitemap (ServerPartT IO) Response

home = do
ok $ toResponse [hamlet|
$doctype 5
<html>
|]

This throws:

UI.hs:34:10:
No instance for (ToMessage (t0 -> Markup))
arising from a use of `toResponse'
In the second argument of `($)', namely
`toResponse
(\ _render_a8mn
-> id
((Text.Blaze.Internal.preEscapedText . pack)
"<!DOCTYPE html>\n\
\<html></html>"))'
In a stmt of a 'do' block:
ok
$ toResponse
(\ _render_a8mn
-> id
((Text.Blaze.Internal.preEscapedText . pack)
"<!DOCTYPE html>\n\
\<html></html>"))
In the expression:
do { ok
$ toResponse
(\ _render_a8mn
-> id
((Text.Blaze.Internal.preEscapedText . pack)
"<!DOCTYPE html>\n\
\<html></html>")) }

So I looked into Hamlet a bit more, and noticed that it uses blaze-html
for output. Markup appears to be an internal blaze-html class. Looking
at Happstack.Server.Response, it seems like it has Response instances
for at least Text.Blaze.Html, but I don't see how to get from Markup to
Html.

I found this blog post from a bit over 2 years ago:

http://tazj.in/en/1335123720

but I don't know if it's still accurate. In particular, I don't
understand why urlF is necessary:

appRoot = do
urlF <- renderFunction
ok $ toResponse $ siteLayout ([hamlet|
<p>Small demo application for Happstack, web-routes, Hamlet and Lucius.
<p>Add two numbers by going to /plus/int1/int2, e.g.
<a href=@{Plus 13 37}> here.
<p>Also other boring stuff, for example #
<a href=@{Echo Reverse "Penguins!"}>this.
|]) urlF

I found the happstack-hamlet library, but a comment in the source seems
to imply that it probably isn't necessary anymore. I don't know if that
is due to changes in Hamlet, or because it's so simple.

I guess if I had to boil my confusion down to one question, it's that
Hamlet seems to output Blaze. How do I go from the Markup type that it
outputs, to the Response type Happstack expects?

And, more generally, does this ever get easier? :) I don't think I'm
*this* bad of a coder, and I've written Scala for a number of years so
am used to thinking somewhat functionally. It just seems like everything
needs 8 million types to get something done. I understand the concept of
currying, but every function I encounter seems to specify a bunch of
parameters, type restrictions, etc. I've been doing lots of JavaScript
lately, and at least there I can type out function calls by hand and
from memory. I don't know that I'll ever fully remember:

home :: RouteT Sitemap (ServerPartT IO) Response

and if I don't specify that my routes don't compile, so seems like I'll
be doing lots of cutting and pasting. I really do want to like Haskell
since I like type safety and native compilation,, but this is the most
I've struggled with a language in a *long* time. :)

Michael Snoyman

unread,
Aug 12, 2014, 11:30:32 PM8/12/14
to ha...@googlegroups.com
Markup is the same as Html; the latter is a type synonym for the former. The problem you're running into is that the `hamlet` quasiquoter allows you to use type-safe URLs, and therefore requires a URL rendering function as an argument. Your choices are:

* Provide such a function as an argument to the result of `hamlet`, e.g. `[hamlet|...|] myRenderFunction`.
* Given that you probably don't have any type safe URLs, you can instead use `shamlet`, which was designed for this purpose.

You may want to read up on Shakespeare a bit. There's a chapter on it in the Yesod book[1], which is actually framework-agnostic.

Michael

[1] http://www.yesodweb.com/book/shakespearean-templates




--
You received this message because you are subscribed to the Google Groups "HAppS" group.
To unsubscribe from this group and stop receiving emails from it, send an email to happs+unsubscribe@googlegroups.com.
To post to this group, send email to ha...@googlegroups.com.
Visit this group at http://groups.google.com/group/happs.
For more options, visit https://groups.google.com/d/optout.

Nolan Darilek

unread,
Aug 13, 2014, 11:51:16 AM8/13/14
to ha...@googlegroups.com
Ah, thanks, this plus some clarification on IRC helped. I'm feeling a bit more confident today. :)

For anyone stumbling on this thread in the future, here's what I've done to implement a minimal template test:


home :: RouteT Sitemap (ServerPartT IO) Response

home = do
    fn <- askRouteFn
    ok $ toResponse $ [hamlet|
        $doctype 5
        <html>
    |]  fn

Can't seem to get that to work for hamletFile, though:

home = do
    fn <- askRouteFn
    ok $ toResponse ($(hamletFile "views/index.hamlet") fn)

:t hamletFile outputs various TH types, so I'm not sure what it's expecting back. Thought it'd be another Markup instance, which is why I was surprised that [hamlet|...] and $(hamletFile...) weren't mostly interchangeable.

Thanks!
To unsubscribe from this group and stop receiving emails from it, send an email to happs+un...@googlegroups.com.

To post to this group, send email to ha...@googlegroups.com.
Visit this group at http://groups.google.com/group/happs.
For more options, visit https://groups.google.com/d/optout.
--
You received this message because you are subscribed to the Google Groups "HAppS" group.
To unsubscribe from this group and stop receiving emails from it, send an email to happs+un...@googlegroups.com.

To post to this group, send email to ha...@googlegroups.com.
Visit this group at http://groups.google.com/group/happs.
For more options, visit https://groups.google.com/d/optout.
!DSPAM:53eadbf1268371219530973!

Michael Snoyman

unread,
Aug 13, 2014, 12:49:04 PM8/13/14
to ha...@googlegroups.com



On Wed, Aug 13, 2014 at 6:50 PM, Nolan Darilek <no...@thewordnerd.info> wrote:
Ah, thanks, this plus some clarification on IRC helped. I'm feeling a bit more confident today. :)

For anyone stumbling on this thread in the future, here's what I've done to implement a minimal template test:


home :: RouteT Sitemap (ServerPartT IO) Response

home = do
    fn <- askRouteFn
    ok $ toResponse $ [hamlet|
        $doctype 5
        <html>
    |]  fn

Can't seem to get that to work for hamletFile, though:

home = do
    fn <- askRouteFn
    ok $ toResponse ($(hamletFile "views/index.hamlet") fn)

:t hamletFile outputs various TH types, so I'm not sure what it's expecting back. Thought it'd be another Markup instance, which is why I was surprised that [hamlet|...] and $(hamletFile...) weren't mostly interchangeable.

Thanks!



How these kinds of things, it's invaluable to see the actual error message. The only thing I can guess at is that the second parameter from askRouteFn is [(Text, Maybe Text)] whereas Hamlet's rendering function works on [(Text, Text)].

Michael

Nolan Darilek

unread,
Aug 13, 2014, 4:01:18 PM8/13/14
to ha...@googlegroups.com
Figured this one out too after learning about a) how to use breakpoints in GHCI and b) using default-extensions in my .cabal file so "cabal repl" lets me interactively type. Guessing hamletFile returns an IO monad. This did the trick:


home = do
    fn <- askRouteFn
    res <- $(hamletFile "views/index.hamlet") fn
    ok $ toResponse res
--
You received this message because you are subscribed to the Google Groups "HAppS" group.
To unsubscribe from this group and stop receiving emails from it, send an email to happs+un...@googlegroups.com.
To post to this group, send email to ha...@googlegroups.com.
Visit this group at http://groups.google.com/group/happs.
For more options, visit https://groups.google.com/d/optout.
!DSPAM:53eb9719315081237516703!

Reply all
Reply to author
Forward
0 new messages