Hey folks,
We covered a lot of ground yesterday and I wanted to write up some notes on a few points that helped me get up the CoffeeScript learning curve.
I spend most of my time in Ruby and ObjC and this is one of those language features I truly envy.
Let's say we have some DOM elements that need to be sortable via drag-and-drop. It'll work like the re-orderable queue on Netflix (if you still get DVDs mailed to you like a chump.)
<ul id="my-list" class="ui-sortable">
<li><input type="text" name="Mad Max" value="0" /> Mad Max</li>
<li><input type="text" name="Ghostbusters" value="1" /> Ghostbusters</li>
<li><input type="text" name="Bridget Jones's Diary" value="2" /> Bridget Jones's Diary</li>
</ul>
This can be pretty simple if we use jQuery's sortable
plugin. We just need to tell it what element we want sortable, and pass a callback to define what happens when things are re-ordered.
$('#my-list').sortable({
update: function(event, ui) {
$('#my-list').find('input').each(function(index,input) {
$(input).val(index);
});
}
});
This is short enough that CoffeeScript won't save us a ton of code, but in my mind the list comprehension is a lot tidier:
jQuery ->
($ '#my-list').sortable
update: (event, ui) ->
($ input).val(index) for input, index in ($ '#my-list').find 'input'
There are a few things going on here worth discussing. First is the way I'm (not) using parens. Because CoffeeScript eliminates redundant punctuation, I can leave off the parens on method calls as long as it doesn't make anything ambiguous. I could have written ($ input).val index
and ($ '#my-list').find('input')
instead, but I think the idiomatic CoffeeScript style is to omit parens wherever possible. I find val(index)
to be more readable here, but that's probably because I'm not used to reading list comprehensions.
Second, my jQuery selectors look a little funky: ($ '#my-list')
instead of $('#my-list')
. Because parens are optional, $ '#my-list'
is perfectly valid. The outer parens make it clear that this is a single expression, though. That's one of the syntactical concepts that I really like about CoffeeScript -- your work units can be discrete expressions as well as functions. For me as a Rubyist, that's a very comfortable style.
We touched on this but I'm not sure it was made clear. Here's the bestest click handler ever:
jQuery ->
($ 'a[rel=external]').click -> alert "Let's go to #{@href}!"
(If you're unfamiliar with CoffeeScript's @
, it simply expands to "this.
". Since @
is mostly a convenience for accessing member variables, it's supposed to remind you of Ruby's instance var sigil.)
When our click handler is executed, it's called on the <a>
element and this.href
will refer to the link's href
attribute.
But if we use the fatrocket, we can do this:
jQuery ->
@href = 'the mall'
($ 'a[rel=external]').click => alert "Let's go to #{@href}!"
Our handler is the same, but now we're using the fatrocket to define it. This binds the function to the local scope, where we've defined @href
. (Remember: that's just shorthand for this.href
.) So now our handler will always use the local variable. Fatrockets are good for making sure this
doesn't change identities on you later.
@harisamin and @snuggsi were debating string interpolation and whether it's really a gain over basic concatenation. My take is that operator overloading can be convenient, but you have to know all the rules. Consider Ruby's case
statement, which overloads the ===
operator:
case foo
when Fixnum then ...
when /a string/ then ...
(This is for illustrative purposes only. If you have a method that doesn't even know what basic type to expect, there's a good chance you need to rethink something.) Other Ruby operators are similarly inconsistent, like *
:
'1' * 5 #=> '11111'
5 * '1' #=> TypeError: String can't be coerced into Fixnum
I tend to take conveniences like this for granted, but violating the commutative property is what makes Pythonistas think we're a Manson Family of dangerously unwired hippies.
Given how sloppy JS is with its string and number types, I like to use interpolation for strings and reserve the +
operator for numeric casts. Consistency breeds clarity, so when I see "Let's go to #{@href}!"
and var i = +n
they're like compiler hints for my brain.
We didn't even mention the splat operator or deconstructive assignment and I won't get into them now. But there's plenty more sugar in CoffeeScript (SEE WHAT I DID THERE?) to check out. I found the CoffeeScript PeepCode and Trevor Burnham's CoffeeScript book both excellent resources, but there's no shortage of free material online.
I LOVE string interpolation in Coffeescript. It really makes me happy :)