Skip to content

Instantly share code, notes, and snippets.

@jashkenas
Created February 10, 2012 16:29
Show Gist options
  • Star 3 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save jashkenas/1790643 to your computer and use it in GitHub Desktop.
Save jashkenas/1790643 to your computer and use it in GitHub Desktop.
# Apart from the basic niceties of being able to leave off
# parens in clear calls...
print 10
alert "Hello #{name}"
# ... the reason why they're important to have in CoffeeScript
# is because it allows our "block" syntax (single function body
# passed as the final argument to a call) to play nicely with
# significant whitespace:
fs.readFile "config/options.json", (err, contents) ->
... work with file here.
# Writing the above without the ability to leave off the
# parens which would otherwise have to wrap the function body
# would defeat a large part of the purpose of significant
# whitespace in the first place.
@avdi
Copy link

avdi commented Feb 10, 2012

The example which drove me batty was:

$('some_selector).click (e) -> 

Which instantly signaled to my programmer brain: there is something special about the function $(), since it is called with parens and click() is not. And I didn't know what was special about it, and that is always troubling.

It turns out that there is nothing special about it; except where it appears.This really bothers me, because it's signaling a difference where there is no difference. Both are functions taking arguments; the fact that one happens to take an anonymous function argument should be immaterial to how it is called.

@avdi
Copy link

avdi commented Feb 10, 2012

I wound up putting explicit parens around the click((e) -> ....) to avoid this false differentiation, which suspect will annoy conventional CoffeeScripters.

If the point is to avoid the trailing delimiter on blocks, I would much prefer a syntax based on HEREDOCs. Something like this:

$('some_selector).click( (e) ->>)
  ...anon function contents go here...

This is comparable (but more concise) to a Perl or Ruby HEREDOC:

foo(...).bar(<<END)
   blah blah text goes here
END

Since CS has significant whitespace it can eschew the explicit delimiter. In effect this:

$('some_selector).click( (e) ->>)
  ...anon function contents go here...

Would say to me "the following indented block gets inserted -->here<--", without mis-cueing me that there is something special about that function call.

@jashkenas
Copy link
Author

It is immaterial, in the sense that you can choose to write calls-with-anon-functions-as-arguments with parentheses or without in every case... Note that this is not true about Ruby.

object.call { |e|
  ...
}

object.call &proc

object.call(&proc)

# If you try to be consistent:

irb(main):002:0> [1,2,3].each({|i| print i})
SyntaxError: compile error

Your suggestion is interesting:

$('some_selector).click( (e) ->>)
  ... contents ...

... but unfortunately from a readability perspective, it doesn't hold water because you're very clearly closing the parentheses, and thus the call, before the contents of the call exist. Mis-nesting parentheses with special syntax to indicate a placeholder for where their arguments will be inserted is a direction I don't think we should take.

@avdi
Copy link

avdi commented Feb 10, 2012

I will also make the general observation that when you want to let programmers use fewer parens (a noble goal), it seems easier to accomplish using Smalltalk-style method calling than using C++/Javas/Ruby/etc-style dot-calling. Dot-calling and removing parens are syntactical principles somewhat at odds with each other.

@avdi
Copy link

avdi commented Feb 10, 2012

In your Ruby example you appear to be conflating anonymous function syntax (which in Ruby 1.9 is ->(args){...}) with block arguments. CoffeeScript doesn't have block arguments so they shouldn't enter the conversation. The Ruby equivalent to your example is:

[1,2,3].each(->(i){print i})

Which is perfectly valid, although not how #each is defined to be called.

My question is: how else can I make the calls in my first example visually consistent? Is there some way I can also leave out the parens around the $('some_selector') part?

@jashkenas
Copy link
Author

In my Ruby example, I'm pointing out that Ruby has to have a special case for the syntax we're talking about here, one which doesn't behave consistently with passing a function expression as an argument... We can talk about the same Ruby inconsistencies with other block forms:

puts(if true
  5
else 
  10
end)

# => 5

puts if true
  5
else 
  10
end

# => syntax error, unexpected kELSE, expecting $end

Whereas in CoffeeScript:

console.log( if true
  5
else
  10
)

# => 5

console.log if true
  5
else
  10

# => 5

... but in any case, to answer your question -- if you value visual consistency for that call, I'd recommend either of these two options:

callback = (e) ->
  ... code ...

$('some_selector).click(callback)

# Or:

$('some_selector).click( (e) ->
  ... code ...
)

... both of which work fine.

@avdi
Copy link

avdi commented Feb 10, 2012

If you don't like the HEREDOC-inspired syntax (understandable; that form of HEREDOC wigs a lot of people out), how about this. An alternate method-call syntax with very low binding priority. I'll use .. for the sake of example:

$ 'some_selector' .. click (e) ->
  ...click handler...

@avdi
Copy link

avdi commented Feb 10, 2012

The latter of your two examples is what I used, but I share your distaste for the trailing paren in a language that tries to avoid them. The former is obvious but not really helpful, since I'm looking for syntactical fixes, not code restructurings. What do you think of a low-precedence method calling syntax for chaining without parens?

@jashkenas
Copy link
Author

Ah, interesting. An explicit "close" operator that ends implicit calls, without having to start them... That would be great material for a Github ticket, if you feel like writing it up and starting discussion. I also don't think it's something we've ever talked about before (a true rarity for CoffeeScript syntax proposals).

@avdi
Copy link

avdi commented Feb 10, 2012

@jrus
Copy link

jrus commented Feb 12, 2012

Another way to avoid the inconsistency is of course:

($ 'some selector').click (e) ->
    ...

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment