Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

How to avoid toValue and toHtml? #88

Open
johannesgerer opened this issue Jun 11, 2014 · 7 comments
Open

How to avoid toValue and toHtml? #88

johannesgerer opened this issue Jun 11, 2014 · 7 comments

Comments

@johannesgerer
Copy link

How can I avaoid having to use toValue/toHtml so often. It comes up basically in any template, that has parameters. As an example take:

myLink url name = a ! href (toValue url) $ toHtml $ name

What do you think about providingth following dashed version of the tags (either manually or with e.g. Template Haskell):

href' :: ToValue a => a -> Attribute
href' = href . toValue

a' :: ToMarkup a => a -> Html
a' = a . toHtml

They even work with literate strings, if OverloadedString is swithed off (as String is in ToMarkup and ToValue).

What do you think about that? Should I send a pull request?

@johannesgerer
Copy link
Author

I implemented the following TH code that does the job for me and is type-safe:

{-# LANGUAGE TemplateHaskell #-}
module BlazeTH where

import           Language.Haskell.TH
import           Language.Haskell.TH.Lib
import           Text.Blaze
import           Text.Blaze.Internal

dash :: Q Exp -> Q Type -> [Name] -> Q [Dec]
dash f t = mapM $ \n -> funD (mkName $ nameBase n ++ "'")
                        [clause [varP x] (normalB
                        [| $(varE n) $ $f $(varE x) :: $t |]) []]
  where x = mkName "x"


dashAttr = dash [|toValue|] [t|Attribute|]
dashTag = dash [|toMarkup|] [t|Markup|]

Usage

dashAttr ['href,'A.id,'A.style]
dashTag ['a,'b,'h1]

myLink url name = a' ! href' url $ name

This solution needs #89

@jaspervdj
Copy link
Owner

I'm not sure if avoiding using toHtml/toValue is a good purpose on its own. I think it's similar to using fromIntegral a lot if you're doing calculations, or toJSON when you're constructing Aeson objects.

@johannesgerer
Copy link
Author

For me using toHtml/toValue feels different from the others. It feels like boilerplate, which has to be written everytime I use href. Plus, I do not see such an easy way to get rid of fromIntegral or toJSON.

EDIT: Aeson actually puts toJSON into encode, so users to not need to put it in front of every encode call. The type encode :: ToJSON a => a -> ByteString makes sense and I think href :: ToValue a => a -> Attribute would make sense.

@simon-nicholls
Copy link

I was surprised by this just now, playing with Blaze for the first time. It felt like boilerplate to me too, as I thought Blaze would be merrily converting my types to attributes as appropriate, like with the literal String.

Specifically I wanted to see how web-routes would integrate with Blaze, and thought I would just throw a route in the Blaze e.g. a ! href Foo $ "Foo me!", then create a relevant Blaze instance for my AppUrl. At that point I looked at the docs and learned otherwise.

@meiersi
Copy link
Collaborator

meiersi commented Oct 3, 2014

There is an easy way to get rid of it in your templates. Just define your
own combinator, e.g., appHref that combines your conversion and the use of
href. This works best if you import blaze-html qualified. The key benefit
is that blaze-html remains decoupled from the different application
scenarios and doesn't have to make opinionated choices.
Am 03.10.2014 12:45 schrieb "Si" notifications@github.com:

I was surprised by this just now, playing with Blaze for the first time.
It felt like boilerplate to me too, as I thought Blaze would be merrily
converting my types to attributes as appropriate, like with the literal
String.

Specifically I wanted to see how web-routes would integrate with Blaze,
and thought I would just throw a route in the Blaze e.g. a ! href Foo $
"Foo me!", then create a relevant Blaze instance for my AppUrl. At that
point I looked at the docs and learned otherwise.


Reply to this email directly or view it on GitHub
#88 (comment).

@simon-nicholls
Copy link

johannesgerer already shows my prior expectations in a technical way. I figured href would have the type signature that he details for href'. Why should String have all the easy living fun?

It seemed natural that AttributeValue conversions are represented by toValue implementations, so I'd look to do that in a workaround module. But that module wouldn't be application specific, only my ToValue instances would be, and it raises the question as to why not provide this in Blaze itself.

So I was throwing in a solidarity comment. Now that I know to use toValue and toHtml, then sure, I'd like to save some typing now. Perhaps I'll even grow to love those twins of typing. But to me as a first time user, my toX-less blaze already looked sweet (expressing everything necessary); it's this user experience that I wished to (overly by now) convey.

@peti
Copy link

peti commented May 7, 2015

The problem with attribute values is that they have wildly differing structures and acceptable values. AttributeValue doesn't capture that complexity in its type. This makes toValue into a dangerous sledge hammer, like unsafePerformIO: it's fine to use the function if you know what you're doing; but if you misuse the function, then you'll end up constructing invalid output and the type system cannot help you catch those errors. For this reason, toValue has to be explicit, because its use implies a proof-obligation. The compiler cannot prove or disprove its correctness; the programmer has to do that. Thus, the compiler cannot add toValues implicitly, because it has no way of knowing which cases are correct and which ones are not.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

5 participants