Disclaimer
Before I begin, let me share my personal mantra: “everyone is entitled to their own (wrong) opinions.” This includes me. This post is purely opinion. It’s an editorial. It means nothing unless you care what my opinion is. If you’re easily offended, or if you just love HAML, or if you’re a closed minded bigot, or whatever, just move on. Really.
I really don’t care what your opinion is on this topic, I just want to express mine, for the record, for posterity sake. And I’m not trying to convince anyone to use or not use HAML. I simply want to state the things I dislike about it.
I’m a fundamentalist, and I prefer simplicity in almost all cases. My bias toward performance, customization, control, optimization, and other such ethics should be evident from this blog already. This automatically makes me suspicious of “all-in-one” anythings. I’m the anti-framework guy. I’m the anti-platform guy. I’m the guy who wants the bare minimum in place to get my task done effectively. And I believe that in doing so, with as few layers of abstraction (and processing) as feasible and possible, you will end up with more performant code.
But this doesn’t have to come at the price of less maintainable code. It’s just that for years, the development community has strived for more maintainable code, and the only answer we’ve ever figured out collectively is to put higher and higher levels of abstractions on things.
Some abstraction is good and smart (we don’t write 0′s and 1′s, thanks @brandonaaron!!), but to the level that a lot of people take it, I liken it to using a pictures-only menu at a fast food restaurant. No offense to the foreign or illiterate, who I’m sure that is helpful to, but if you can read, you should. Pointing to the picture of a hamburger, then the picture of a ketchup bottle, then… this is inferior to just saying “Hamburger with ketchup”, if you can say that. Just my opinion.
Anyway, my goal with this post is to identify the fundamental approaches (not just nit-picking) of HAML that I dislike. Actually, to be fair, HAML is not the problem itself, HAML is just a symptom of the larger problem, convenient to help me illustrate some thoughts on templating as a discipline.
I’ll continue the discourse in subsequent post(s) with other opinions, and even some suggestions I have for solutions. Hopefully you’ll stick around and get something out of it. If not, that’s fine, too.
Brief overview of HAML
For those who aren’t aware, HAML is a templating language, apparently most closely associated with Ruby, that uses some “shorthand” to express HTML structures. It’s important to note that it’s more involved than a simple shorthand such as Wiki/markdown, as it appears aimed to be a full-fledged templating engine.
As it includes a templating “language” (a DSL, technically), it has the usual suspects, including variable replacement, logical selection operators, “sub-template” includes, comments, etc. It also allows/includes Ruby (or Ruby-like, not quite sure) syntax for more complicated logic, such as function definitions, looping, math operations, etc.
Let’s identify first the most important characteristics of HAML (at least, those relevant to my points):
- HAML uses CSS-like shorthand to reverse-declare HTML tags. (HAML assumes a “<div>” tag type unless otherwise specified).
Example:
#mydiv %span.message Hello WorldWould result in:
<div id="mydiv"> <span class="message">Hello World</span> </div>It’s easy to see how in this respect, the “shorthand” for the resultant HTML seems easier to write/maintain.
- HAML uses whitespace indentation nesting to signal scope of tag blocks. This is critical for HAML being able to know where a block ends without needing closing tags, a big part of the “shorthand” being achieved.
#mydiv %ol#mylist %li.myitem Hello %li.myitem WorldResults in:
<div id="mydiv"> <ol id="mylist"> <li class="myitem">Hello</li> <li class="myitem">World</li> </ol> </div>Again, clearly the “shorthand” syntax has some appealing aspects, so far. If you like/don’t mind “whitespace sensitive languages” like Python and VB, you’ll be ok with using indentation in place of closing tags.
-
For more complex tasks, like math operations, dropping in variable replacements, calling functions, executing logic, etc, HAML allows you to use an = sign to signal a line of Ruby code to be executed in the context of the template parsing, with the output being added to the template at that point. (You use a – to indicate Ruby that should execute but which returns no desired output to include in the template).
Example:
- (42...45).each do |i| %p= iResults in:
<p>42</p> <p>43</p> <p>44</p> <p>45</p>
There are other variations of how to include/execute Ruby code as well. The point is, HAML tries to make it pretty easy for you to execute logic in your template, as long as that logic is Ruby. Also, they make one little buried warning in the documentation about encouraging you not to run Ruby logic which should be in the Controller. It’s not enforced in anyway, but I guess if you read and obey, that’s helpful.
There’s certainly more (a lot more, in fact) to HAML than this short list. I’m not writing a book on HAML (or anti-HAML) here, so I’m just trying to keep it to a minimum. But let’s just suffice it to say, there’s a few dozen different syntax rules, tag types, practices, etc that are required to use a HAML template for a non-trivial task like building a full dynamic web app page. Also, you’re going to have to know Ruby to get much more than simple “shorthand” HTML out of the deal.
My opinions
I’m going to assume if you’re still reading this blog post at this point, you’re here because you a) care about my opinions, or b) care enough to want to discredit my opinions. If you’re in group (a), read on. If you’re in group (b)… well… refer back to the first sentence of the post about my personal mantra.
Whitspace/indentation scoping
So, first off, I hate, absolutely detest, cannot stand, whitespace-sensitive languages. I cannot think of hardly anything in programming paradigms that irks me more than having to try and determine a set of logic context and having to count the number of indented columns (if they are spaces and not tabs, for instance) to figure out which blocks go together. This is doubly true if you choose to use like 1 or 2 space indentation, which I’ve seen at a couple of my previous jobs.
I suppose if you used 10 spaces for each indentation mark, it might make things obvious enough, but then you’re defeating the purpose of trying to use shorthand if you have to add and manage a bunch of extra characters to help make scope identifiable.
This brings up my counter opinion: languages like C and JavaScript use just 2 characters to identify blocks; the “{” and the “}”. This also makes it easier for code editors to give you helpful hints like highlighting closing and opening “tags”, and helping you know if you have unbalanced/unclosed elements, etc. I like this in JavaScript and C styled languages, so it natually should extend that I like this in HTML.
I don’t like complications
The above examples look really nice, and are probably the sweet spot for how HAML shines. But if you read the long form documentation, you’ll quickly see that there are lots of other complicating scenarios which you must learn to deal with. Indeed, HAML is not just shorthand convenient HTML, it’s a DSL with sytnax and rules and exceptions and gotchas all its own.
For instance, consider the case of wanting to add some attributes to a tag. Here’s an example from the documentation of the proper way to do it with HAML.
Say you want this HTML:
<sandwich bread='whole wheat' delicious='true' filling='peanut butter and jelly' />
Just use this “little” snippet of goodness (makes me hungry just reading it):
def hash1
{:bread => 'white', :filling => 'peanut butter and jelly'}
end
def hash2
{:bread => 'whole wheat'}
end
%sandwich{hash1, hash2, :delicious => true}/
Uhhh, wait a second. I thought HAML was supposed to be shorter and simpler. I dunno about you, but this does not appear shorter or simpler to me. Maybe that’s just because I’m not a Ruby developer. Or maybe that’s just because at the point where I want to write HTML, I want to write stuff that looks like… HTML.
Moreover, if I have someone whose specialty is HTML and not Ruby, I really don’t want them trying to write such Ruby code. But maybe that’s just me.
Update 4/27/2010: John commented to suggest a shorthand for the above in HAML:
%sandwich{:bread => 'whole wheat', :filling => 'peanut butter and jelly',
:delicious => true}/
Certainly this is better than the above (although still inferior to HTML itself IMHO), which by the way came from the official HAML documentation (not me that made that up). I’m sure they did that to demonstrate how you can override properties with subsequent hashes, etc. But as I said in response to John, the fact that the more complicated form is what’s found in the HAML documentation will probably lead to more “newbies” like me thinking this is the standard way to do HAML. I have the same problem with the thousands of .NET MVC tutorials on the first few pages of Google SERP’s that tell people the completely wrong way to pass data from the Model to your View. If you document the bad practices enough, the bad practices will become the de facto standard. This is bad. Very bad.
When writing HTML, I should be writing… HTML
Perhaps I should state rather than just imply a bias I have here: if I’m in the mode of doing front-end (HTML) development (’cause I know a lot of us wear many different hats from front-end dev to back-end dev in our jobs), I want to think in HTML (not something else) at that point in time. Just like when doing back-end app development, or when building out database schemas, I darn sure don’t want to care (at that moment) about the eccentricities of cross-browser CSS and box-model support.
I think this kind of role separation (and thus skill separation) makes my job easier. It makes is clearer what I’m doing at any given point, instead of trying to blur syntactical lines between various technologies in the name of convenience.
I just find I work more effectively when I do only what is necessary for a given task at any one time.
Templates and Code Logic should not mix
I feel like this point is almost so self-obvious, I’m boring you by mentioning it. But yet time after time I see various platforms and frameworks (everything from .NET to Java to PHP to RoR to HAML to everything else in between) making exceptions to these rules, in whole or in part, and allowing (or even encouraging!) you to add some host language logic directly inside your templates.
I guess the logic follows that if you’re a Ruby developer, and you’re also tasked with doing front-end (HTML) development, you won’t mind mixing the two in your mind, using Ruby code logic to assemble your templates and data.
Well, that’s great until you hire a rock-star HTML/CSS guy to make templates who has zero knowledge of Ruby. I suppose you just hand him a Ruby book and say, “good luck.”? Or what about if you decide at some point to change the back-end platform technology you’re using from Ruby to say Scala or Erlang or PHP or whatever. What becomes of all your templates? Do you keep the Ruby based stuff, or do you ditch them entirely and re-write all your presentation layer as well?
And, btw, how does your development team make decisions effectively about what’s ok to be Ruby embedded in your templates, and what must be in the controllers? How do you train new developers to your team how to effectively make these decisions quickly right along side all your seasoned developers who’ve stubbed their toes on those topics for years? How do you decide, philosophically, if logic related to formatting (say a date or currency) is logic that should belong in the templates or in the controllers?
You see, in my opinion, all these questions are harder because you’re using a system that’s more flexible, more powerful, more complicated than it needs to be or should be.
Just because I can hammer a nail in with a sledge hammer doesn’t mean that’s the most effective tool for the job. If I have a little hammer suitable for the task, I think it’ll be more accurate and efficient to use it than the big sledge hammer. And that’ll also be true when I hand off the task of nailing things in to a new person who’s never used the sledge hammer before.
Point: You don’t need the power of Ruby to construct templates. If you do, you’re doing templating wrong — again, in my opinion. Templating only needs a few simple constructs to be effective. Anything beyond that should be in your Controllers. At least, that’s what we were all taught in our CS classes when we learned about the proper Object-Oriented approach to MVC and we had it drilled into us that the “M”, the “V”, and the “C” should be separate. How quickly we abandon “separation” when it becomes too convenient in the heat of the moment.
I love this quote I just read on twitter: “good programmers appreciate the features you put in. expert programmers appreciate the features you left out.” (via KentBeck)
Portability
I’m also not a fan of having to use more than one templating system. By this, I mean, it is a reality of most (even mildly) complex web apps that there are two classes of templating: build-time (on the server) and dynamic (run-time, in the browser, with user input). Of course, various web apps have a different mix, and if you’re in the small class that is entirely one or the other and always will be, just skip over this point.
However, for the rest of us, almost nothing upsets me more than having to repeat myself. We’ve all heard DRY (don’t repeat yourself), but I don’t just quote that as a nice-to-have, I try hard to live by it in my development career. I am not 100% sure, but I’d hazard a good guess that it’s impossible to use the exact same templates (with Ruby logic and all) in both a server environment and a client environment. Moreover, even if there is a port (to JavaScript) of the HAML interpreter that could run in the browser (with alternate templates of course), this is of course two different separate copies of a templating engine that must be independently maintained.
A fundamental “law” in Computer Science is that anytime there is more than one copy of something, one copy is always wrong. That’s where DRY was born from. And it goes for tools as well as content.
You may be saying to yourself, “wait, that’s not possible with any templating engine.” Ahh, but it is! Stay tuned, in a (soon) future blog post here I’ll be talking about a templating engine I’ve built which can do just that.
You know what technologies can and do run in both contexts? JavaScript and HTML. Anything other than that, and you’ve almost certainly failed this point. Game over.
One more thing
One last point of contention: I am unable to confirm how HAML handles “compilation” of templates. If it does so on-the-fly, every time, at run time (since it also has to execute Ruby code to drop in data), then this is more inefficient to convert from shorthand code to raw HTML each time.
If however it has some sort of two-pass compilation, where at build time you compile the shorthand HAML to raw HTML, and then at run-time you execute the embedded Ruby to drop in data, then this is probably a better approach efficiency wise.
Still, the point remains, that type of technique is important for performance, and it’s not very common amongst most templating solutions.
Enough already
OK, that’s enough ranting for one post. Again, the purpose here was to lay out what I don’t like about HAML. The underyling frustrations however are not specific to HAML but are true of many, many templating solutions. So up coming will be posts where I try to be more constructive and offer some solutions. Hopefully you’ll find this dialogue helpful. That is of course if you haven’t already written me off as being a crazy, eccentric, opinionated technology bigot.


Hi,
i said all of these things too when I first tried HAML. It does take about an hour or two to get used to it.
I disagree with you entirely, but the one point I want to refute you on is your sandwich thing –
%sandwich{:bread => “whole wheat”, :delicious => true, etc. — works fine. There’s no reason or need to define a hash separately.
As a person with a similar background to you, I think, I was mortified by the whitespace rules. Now, if I look at a page of ERB I get dizzy and confused and I basically can’t read it.
HAML encourages me to keep code out of views.
HAML forces me to indent my HTML “logic” so it’s easily readable.
Yeah, whitespace is contrary to everything we believe in, but at the same time, two white spaces is not too much, you can configure most decent editors to do it with soft tabs, and I can’t even begin to tell you how much time I’ve saved by having that mandatory indenting keeping track of my logic.
Not to mention never having to close tags, which is a waste of time.
Nice post though — I hope you come around to see the light one day!
John
@John–
Thanks for your comments.
The sandwich example is ripped directly from the HAML documentation. Sure, there may be shorthand notation as you suggest, but if the official documentation is suggesting it that way, odds are most people who start off with HAML end up doing it that “wrong way”. Would be a lot better, IMHO, if such ridiculousness was not only not in the documentation, but was flat out not possible in the “language”.
On the topic of whitespace, i doubt if i’ll ever come to “see the light” on that. i’m just so fundamentally in disagreement with it. do you use an editor that can automatically highlight the relevant scoped indented block in some way? what do you do when you have “blocks” that are taller than the viewport of your editor — doesn’t that make tracing where a block starts/ends much more difficult than if there was explicit start/end markers?
I just use Textmate, and the indent levels are pretty obvious to me from a quick glance. If I’m not sure which column the line starts on, I guess I can put the cursor on the first character and look at the column/row values. Either way, it’s never been a problem or hassle for me.
That’s odd that HAML tells you to define a hash in the documentation, but maybe they are just trying to illustrate how this can be done, which I guess could be incredibly useful if, say, you have a long succession of HTML values that you need to repeatedly pass the same attributes to.
Anyway, documentation is never the strong suit of anything in the ruby/rails world.
How long did you try HAML before decided you didn’t like it? I am not trying to be a jerk, I’m just curious because I did say all of these same things until I forced myself to try it again — it was the extreme frustration of
horribly ugly ERB code, cluttered up with everywhere, and all those useless tags — that made me give it another shot. And it only took me, I dunno, two hours or so — before I became somewhat evangelical about it. (And I’m not really particularly evangelical about anything!)
@John — it didn’t take very long reading the documentation for me to see the parts I don’t like about HAML. Were HAML my only option and I was still stubbing my toes on all those “ui architecture” issues you refer to, I might be inclined to take a second look.
But instead, I decided to not just complain but do something about it, so I wrote something that worked the way I do like, which is what turned out to be the HandlebarJS templating engine. I can say pretty surely that writing HTML templates with JSON data and minimal templating logic is the way I think templating should be, so I consider HAML to be an inferior option in most ways.
But I do appreciate your perspective and comments — keep ‘em coming!
I don’t know if I would agree with what you say, but I do agree in your philosophy that we should keep things as simple as possible.
Your post begs the question on what is a better way to template. Seeing that you have been working on HandlebarJS for a while, why don’t you also include other posts where you describe how to use it together with your technical justification? If you don’t have that, I would love to read that future entry.
@Hugo- You’re absolutely correct, I’ve been lax in my writing of blog posts lately… and especially so in terms of describing how to use HandlebarJS. It *is* under active development, but I just haven’t been writing about it much, and I should. I’ll add that topic to my queue and try to get to that soon. Thanks for the feedback.
I also disagree entirely, and about the hash “sandwich”, since Ruby 1.9 you can just do the following:
%sandwich{bread: ‘whole wheat’, filling: ‘peanut butter and jelly’,
delicious: true}
And most text editors / IDEs highlight the indentation you are in, so there is no need to have a closing tag.
And HAML precompiles to ERB.