
Hi Yesod developers. Attached are two modules to implement CSS size wrappers for Hamlet. It implements several types for different CSS units: * em - EmSize * ex - ExSize * px - PixelSize * % - PercentageSize * cm, in, mm, pc, pt - AbsoluteSize You can create them using an mkSize Template Haskell function, e.g. let size = $(mkSize "100%") All types are instances of Show, Eq, Ord, Num, Fractional and ToCss. This allows you to do math and interpolate it to Hamlet. Ideally, I wanted to have only one type and make an implicit conversion from string literals. But I do not know how to forbid mixing of incompatible units in math (e.g. 10px + 10cm) with a single type. And I do not know how to make implicit conversion from string literals with multiple types. I would appreciate any comments. Including on naming style, type design, API and overall usefulness. Keep in mind that this is the first time I use Template Haskell, so the code may be not optimal and plain ugly (e.g. can we use quasi quotes instead of directly messing with TH?) BTW Michael, one more thing I miss in Hamlet is defining variables in templates. I would prefer to put simple CSS-related constants directly in Cassius instead of a dedicated module. Is it possible to implement? Regards, Dmitry

Small change to fix a bug in show for em, ex and px types. Regards, Dmitry

It looks good. Instead of the mkSize TH function, if you just define
an IsString instance, then anyone using OverloadedStrings will be able
to use string literals. I haven't confirmed this yet, but it might
even be possible to embed those string literals inside Cassius and GHC
will still apply fromString appropriately.
As far as variables inside templates: I personally think that's
crossing the line again into stuff templates shouldn't be dealing
with, but I'm open for discussions. Since templates tie in so well
with Haskell, I just don't think it's worth adding a whole bunch of
extra code and syntax to make it work.
Michael
On Tue, Feb 15, 2011 at 10:55 PM, Dmitry Kurochkin
Hi Yesod developers.
Attached are two modules to implement CSS size wrappers for Hamlet. It implements several types for different CSS units:
* em - EmSize * ex - ExSize * px - PixelSize * % - PercentageSize * cm, in, mm, pc, pt - AbsoluteSize
You can create them using an mkSize Template Haskell function, e.g.
let size = $(mkSize "100%")
All types are instances of Show, Eq, Ord, Num, Fractional and ToCss. This allows you to do math and interpolate it to Hamlet.
Ideally, I wanted to have only one type and make an implicit conversion from string literals. But I do not know how to forbid mixing of incompatible units in math (e.g. 10px + 10cm) with a single type. And I do not know how to make implicit conversion from string literals with multiple types.
I would appreciate any comments. Including on naming style, type design, API and overall usefulness. Keep in mind that this is the first time I use Template Haskell, so the code may be not optimal and plain ugly (e.g. can we use quasi quotes instead of directly messing with TH?)
BTW Michael, one more thing I miss in Hamlet is defining variables in templates. I would prefer to put simple CSS-related constants directly in Cassius instead of a dedicated module. Is it possible to implement?
Regards, Dmitry

On Wed, 16 Feb 2011 06:25:54 +0200, Michael Snoyman
It looks good. Instead of the mkSize TH function, if you just define an IsString instance, then anyone using OverloadedStrings will be able to use string literals. I haven't confirmed this yet, but it might even be possible to embed those string literals inside Cassius and GHC will still apply fromString appropriately.
I was thinking about it. But as I understand, it would not work unless you explicitly specify the resulting size type. E.g. let s = fromString "100px" How would GHC know that in this case fromString for PixelSize should be used? This would force you to add explicit (s :: PixelSize) type. This is something I want to avoid: Type should be determined from string constant. Or am I missing something?
As far as variables inside templates: I personally think that's crossing the line again into stuff templates shouldn't be dealing with, but I'm open for discussions. Since templates tie in so well with Haskell, I just don't think it's worth adding a whole bunch of extra code and syntax to make it work.
I understand this is a feature which may be used inappropriately. But here is mine justification for it: Consider you want to write a simple Cassius template: #a height: 100px #b width: 100px #a and #b are related and their width and height should be always the same. So it makes perfect sense to define (commonSize = $(mkSize "100px")) and use it instead of literals. I know that commonSize is needed and used only for that Cassius template, but I have to define it in an external Haskell module. That makes template not self-contained and harder to read, pollutes Haskell code with declarations that should be local to a template. IMO this cases illustrates how local template variables may be the right solution: #{let commonSize = $(mkSize "100px")} #a height: #{commonSize} #b width: #{commonSize} Regards, Dmitry
Michael
On Tue, Feb 15, 2011 at 10:55 PM, Dmitry Kurochkin
wrote: Hi Yesod developers.
Attached are two modules to implement CSS size wrappers for Hamlet. It implements several types for different CSS units:
* em - EmSize * ex - ExSize * px - PixelSize * % - PercentageSize * cm, in, mm, pc, pt - AbsoluteSize
You can create them using an mkSize Template Haskell function, e.g.
let size = $(mkSize "100%")
All types are instances of Show, Eq, Ord, Num, Fractional and ToCss. This allows you to do math and interpolate it to Hamlet.
Ideally, I wanted to have only one type and make an implicit conversion from string literals. But I do not know how to forbid mixing of incompatible units in math (e.g. 10px + 10cm) with a single type. And I do not know how to make implicit conversion from string literals with multiple types.
I would appreciate any comments. Including on naming style, type design, API and overall usefulness. Keep in mind that this is the first time I use Template Haskell, so the code may be not optimal and plain ugly (e.g. can we use quasi quotes instead of directly messing with TH?)
BTW Michael, one more thing I miss in Hamlet is defining variables in templates. I would prefer to put simple CSS-related constants directly in Cassius instead of a dedicated module. Is it possible to implement?
Regards, Dmitry

On Wed, Feb 16, 2011 at 11:49 AM, Dmitry Kurochkin
On Wed, 16 Feb 2011 06:25:54 +0200, Michael Snoyman
wrote: It looks good. Instead of the mkSize TH function, if you just define an IsString instance, then anyone using OverloadedStrings will be able to use string literals. I haven't confirmed this yet, but it might even be possible to embed those string literals inside Cassius and GHC will still apply fromString appropriately.
I was thinking about it. But as I understand, it would not work unless you explicitly specify the resulting size type. E.g.
let s = fromString "100px"
How would GHC know that in this case fromString for PixelSize should be used? This would force you to add explicit (s :: PixelSize) type. This is something I want to avoid: Type should be determined from string constant. Or am I missing something?
No, you're right, TH is the way to go here.
As far as variables inside templates: I personally think that's crossing the line again into stuff templates shouldn't be dealing with, but I'm open for discussions. Since templates tie in so well with Haskell, I just don't think it's worth adding a whole bunch of extra code and syntax to make it work.
I understand this is a feature which may be used inappropriately. But here is mine justification for it:
Consider you want to write a simple Cassius template:
#a height: 100px #b width: 100px
#a and #b are related and their width and height should be always the same. So it makes perfect sense to define (commonSize = $(mkSize "100px")) and use it instead of literals. I know that commonSize is needed and used only for that Cassius template, but I have to define it in an external Haskell module. That makes template not self-contained and harder to read, pollutes Haskell code with declarations that should be local to a template. IMO this cases illustrates how local template variables may be the right solution:
#{let commonSize = $(mkSize "100px")} #a height: #{commonSize} #b width: #{commonSize}
Actually, I think this code snippet proves the opposite point. It's not really possible to embed TH in a template. I think adding in a whole bunch of Haskell features to Cassius (et al) will simply start people wishing they *were* programming in Haskell instead of templates. My opinion: keep the templates simple and to the point, put the logic in Haskell where you have more power. Michael

On Wed, 16 Feb 2011 12:02:34 +0200, Michael Snoyman
On Wed, Feb 16, 2011 at 11:49 AM, Dmitry Kurochkin
wrote: On Wed, 16 Feb 2011 06:25:54 +0200, Michael Snoyman
wrote: It looks good. Instead of the mkSize TH function, if you just define an IsString instance, then anyone using OverloadedStrings will be able to use string literals. I haven't confirmed this yet, but it might even be possible to embed those string literals inside Cassius and GHC will still apply fromString appropriately.
I was thinking about it. But as I understand, it would not work unless you explicitly specify the resulting size type. E.g.
let s = fromString "100px"
How would GHC know that in this case fromString for PixelSize should be used? This would force you to add explicit (s :: PixelSize) type. This is something I want to avoid: Type should be determined from string constant. Or am I missing something?
No, you're right, TH is the way to go here.
How is it better to integrate to Hamlet? I guess MkSizeType should go to Text/Hamlet/ directory and Size.hs will be part of Text/Cassius.hs.
As far as variables inside templates: I personally think that's crossing the line again into stuff templates shouldn't be dealing with, but I'm open for discussions. Since templates tie in so well with Haskell, I just don't think it's worth adding a whole bunch of extra code and syntax to make it work.
I understand this is a feature which may be used inappropriately. But here is mine justification for it:
Consider you want to write a simple Cassius template:
#a height: 100px #b width: 100px
#a and #b are related and their width and height should be always the same. So it makes perfect sense to define (commonSize = $(mkSize "100px")) and use it instead of literals. I know that commonSize is needed and used only for that Cassius template, but I have to define it in an external Haskell module. That makes template not self-contained and harder to read, pollutes Haskell code with declarations that should be local to a template. IMO this cases illustrates how local template variables may be the right solution:
#{let commonSize = $(mkSize "100px")} #a height: #{commonSize} #b width: #{commonSize}
Actually, I think this code snippet proves the opposite point. It's not really possible to embed TH in a template. I think adding in a whole bunch of Haskell features to Cassius (et al) will simply start people wishing they *were* programming in Haskell instead of templates. My opinion: keep the templates simple and to the point, put the logic in Haskell where you have more power.
Sigh. It is possible to use TH in QQ, hence I thought it is possible to use it in Hamlet as well. Regards, Dmitry
Michael

On Wed, Feb 16, 2011 at 12:37 PM, Dmitry Kurochkin
On Wed, 16 Feb 2011 12:02:34 +0200, Michael Snoyman
wrote: On Wed, Feb 16, 2011 at 11:49 AM, Dmitry Kurochkin
wrote: On Wed, 16 Feb 2011 06:25:54 +0200, Michael Snoyman
wrote: It looks good. Instead of the mkSize TH function, if you just define an IsString instance, then anyone using OverloadedStrings will be able to use string literals. I haven't confirmed this yet, but it might even be possible to embed those string literals inside Cassius and GHC will still apply fromString appropriately.
I was thinking about it. But as I understand, it would not work unless you explicitly specify the resulting size type. E.g.
let s = fromString "100px"
How would GHC know that in this case fromString for PixelSize should be used? This would force you to add explicit (s :: PixelSize) type. This is something I want to avoid: Type should be determined from string constant. Or am I missing something?
No, you're right, TH is the way to go here.
How is it better to integrate to Hamlet? I guess MkSizeType should go to Text/Hamlet/ directory and Size.hs will be part of Text/Cassius.hs.
That sounds fine, frankly it doesn't matter to me where MkSizeType goes since it won't be an exposed module (right?). And Size.hs's code would go in Text.Cassius, correct.
As far as variables inside templates: I personally think that's crossing the line again into stuff templates shouldn't be dealing with, but I'm open for discussions. Since templates tie in so well with Haskell, I just don't think it's worth adding a whole bunch of extra code and syntax to make it work.
I understand this is a feature which may be used inappropriately. But here is mine justification for it:
Consider you want to write a simple Cassius template:
#a height: 100px #b width: 100px
#a and #b are related and their width and height should be always the same. So it makes perfect sense to define (commonSize = $(mkSize "100px")) and use it instead of literals. I know that commonSize is needed and used only for that Cassius template, but I have to define it in an external Haskell module. That makes template not self-contained and harder to read, pollutes Haskell code with declarations that should be local to a template. IMO this cases illustrates how local template variables may be the right solution:
#{let commonSize = $(mkSize "100px")} #a height: #{commonSize} #b width: #{commonSize}
Actually, I think this code snippet proves the opposite point. It's not really possible to embed TH in a template. I think adding in a whole bunch of Haskell features to Cassius (et al) will simply start people wishing they *were* programming in Haskell instead of templates. My opinion: keep the templates simple and to the point, put the logic in Haskell where you have more power.
Sigh. It is possible to use TH in QQ, hence I thought it is possible to use it in Hamlet as well.
A QQ block is just a String that needs to be interpreted by a Haskell function into a "Q Exp". Each and every added feature needs to be coded from scratch, and as such, will never be done as well as features included in Haskell itself. That's a large part of my reluctance to adding everything and the kitchen sink (besides the fact that I think it's not a good idea for a templating language anyway). Michael

On Wed, 16 Feb 2011 12:42:26 +0200, Michael Snoyman
On Wed, Feb 16, 2011 at 12:37 PM, Dmitry Kurochkin
wrote: On Wed, 16 Feb 2011 12:02:34 +0200, Michael Snoyman
wrote: On Wed, Feb 16, 2011 at 11:49 AM, Dmitry Kurochkin
wrote: On Wed, 16 Feb 2011 06:25:54 +0200, Michael Snoyman
wrote: It looks good. Instead of the mkSize TH function, if you just define an IsString instance, then anyone using OverloadedStrings will be able to use string literals. I haven't confirmed this yet, but it might even be possible to embed those string literals inside Cassius and GHC will still apply fromString appropriately.
I was thinking about it. But as I understand, it would not work unless you explicitly specify the resulting size type. E.g.
let s = fromString "100px"
How would GHC know that in this case fromString for PixelSize should be used? This would force you to add explicit (s :: PixelSize) type. This is something I want to avoid: Type should be determined from string constant. Or am I missing something?
No, you're right, TH is the way to go here.
How is it better to integrate to Hamlet? I guess MkSizeType should go to Text/Hamlet/ directory and Size.hs will be part of Text/Cassius.hs.
That sounds fine, frankly it doesn't matter to me where MkSizeType goes since it won't be an exposed module (right?). And Size.hs's code would go in Text.Cassius, correct.
Yeah, MkSizeType module is not exported. Patch for Hamlet attached.
As far as variables inside templates: I personally think that's crossing the line again into stuff templates shouldn't be dealing with, but I'm open for discussions. Since templates tie in so well with Haskell, I just don't think it's worth adding a whole bunch of extra code and syntax to make it work.
I understand this is a feature which may be used inappropriately. But here is mine justification for it:
Consider you want to write a simple Cassius template:
#a height: 100px #b width: 100px
#a and #b are related and their width and height should be always the same. So it makes perfect sense to define (commonSize = $(mkSize "100px")) and use it instead of literals. I know that commonSize is needed and used only for that Cassius template, but I have to define it in an external Haskell module. That makes template not self-contained and harder to read, pollutes Haskell code with declarations that should be local to a template. IMO this cases illustrates how local template variables may be the right solution:
#{let commonSize = $(mkSize "100px")} #a height: #{commonSize} #b width: #{commonSize}
Actually, I think this code snippet proves the opposite point. It's not really possible to embed TH in a template. I think adding in a whole bunch of Haskell features to Cassius (et al) will simply start people wishing they *were* programming in Haskell instead of templates. My opinion: keep the templates simple and to the point, put the logic in Haskell where you have more power.
Sigh. It is possible to use TH in QQ, hence I thought it is possible to use it in Hamlet as well.
A QQ block is just a String that needs to be interpreted by a Haskell function into a "Q Exp". Each and every added feature needs to be coded from scratch, and as such, will never be done as well as features included in Haskell itself. That's a large part of my reluctance to adding everything and the kitchen sink (besides the fact that I think it's not a good idea for a templating language anyway).
Thanks for explanation. I think I am convinced for now :) Regards, Dmitry
Michael

On Wed, Feb 16, 2011 at 5:02 PM, Dmitry Kurochkin
On Wed, 16 Feb 2011 12:42:26 +0200, Michael Snoyman
wrote: On Wed, Feb 16, 2011 at 12:37 PM, Dmitry Kurochkin
wrote: On Wed, 16 Feb 2011 12:02:34 +0200, Michael Snoyman
wrote: On Wed, Feb 16, 2011 at 11:49 AM, Dmitry Kurochkin
wrote: On Wed, 16 Feb 2011 06:25:54 +0200, Michael Snoyman
wrote: It looks good. Instead of the mkSize TH function, if you just define an IsString instance, then anyone using OverloadedStrings will be able to use string literals. I haven't confirmed this yet, but it might even be possible to embed those string literals inside Cassius and GHC will still apply fromString appropriately.
I was thinking about it. But as I understand, it would not work unless you explicitly specify the resulting size type. E.g.
let s = fromString "100px"
How would GHC know that in this case fromString for PixelSize should be used? This would force you to add explicit (s :: PixelSize) type. This is something I want to avoid: Type should be determined from string constant. Or am I missing something?
No, you're right, TH is the way to go here.
How is it better to integrate to Hamlet? I guess MkSizeType should go to Text/Hamlet/ directory and Size.hs will be part of Text/Cassius.hs.
That sounds fine, frankly it doesn't matter to me where MkSizeType goes since it won't be an exposed module (right?). And Size.hs's code would go in Text.Cassius, correct.
Yeah, MkSizeType module is not exported.
Patch for Hamlet attached.
As far as variables inside templates: I personally think that's crossing the line again into stuff templates shouldn't be dealing with, but I'm open for discussions. Since templates tie in so well with Haskell, I just don't think it's worth adding a whole bunch of extra code and syntax to make it work.
I understand this is a feature which may be used inappropriately. But here is mine justification for it:
Consider you want to write a simple Cassius template:
#a height: 100px #b width: 100px
#a and #b are related and their width and height should be always the same. So it makes perfect sense to define (commonSize = $(mkSize "100px")) and use it instead of literals. I know that commonSize is needed and used only for that Cassius template, but I have to define it in an external Haskell module. That makes template not self-contained and harder to read, pollutes Haskell code with declarations that should be local to a template. IMO this cases illustrates how local template variables may be the right solution:
#{let commonSize = $(mkSize "100px")} #a height: #{commonSize} #b width: #{commonSize}
Actually, I think this code snippet proves the opposite point. It's not really possible to embed TH in a template. I think adding in a whole bunch of Haskell features to Cassius (et al) will simply start people wishing they *were* programming in Haskell instead of templates. My opinion: keep the templates simple and to the point, put the logic in Haskell where you have more power.
Sigh. It is possible to use TH in QQ, hence I thought it is possible to use it in Hamlet as well.
A QQ block is just a String that needs to be interpreted by a Haskell function into a "Q Exp". Each and every added feature needs to be coded from scratch, and as such, will never be done as well as features included in Haskell itself. That's a large part of my reluctance to adding everything and the kitchen sink (besides the fact that I think it's not a good idea for a templating language anyway).
Thanks for explanation. I think I am convinced for now :)
Regards, Dmitry
Michael
Thanks, patch applied. I'd like to look at this just a little bit more before releasing to Hackage, but it looks like a nice addition. Thank you! Michael
participants (2)
-
Dmitry Kurochkin
-
Michael Snoyman