Skip to content

Instantly share code, notes, and snippets.

@jimmycuadra
Created September 14, 2011 04:39
Show Gist options
  • Save jimmycuadra/1215863 to your computer and use it in GitHub Desktop.
Save jimmycuadra/1215863 to your computer and use it in GitHub Desktop.
Syntax question for CoffeeScript
# Given a function that accepts a function followed by a non-function argument...
foo = (callback, name) ->
console.log "Calling callback #{name}..."
callback()
# you end up with a line starting with
# a comma which just feels wrong...
foo ->
console.log "Callback called!"
, "bar"
# or an awkard set of parens which generates
# unnecessary parens in the compiled source...
foo (->
console.log "Callback called!"
), "bar"
# or a syntax error.
foo ->
console.log("Callback called!"), "bar"
# Wat do? What's the preferred way to write this?
@TrevorBurnham
Copy link

The preferred way is to make callback the last argument. Of course, you don't always have access to the original function, but this is the standard convention in most APIs (Node.js, jQuery...), and you can always create a wrapper, e.g.

delay = (ms, func) -> setTimeout func, ms

Then your example can be written simply as

foo "bar", -> console.log "Callback called!"

If the callback is longer, write it as a separate function:

callback = (data) ->
  console.log "Callback called!"
  cache.push data
  console.log "Cache now has #{cache.length} items"

foo "bar", callback

@jimmycuadra
Copy link
Author

@TrevorBurnham

Agreed, but this is not always the case, especially if the callback is a required argument and the subsequent non-function parameters are optional. Real world examples of this are the ajax convenience functions in jQuery. In JS you'd write:

$.post("/items", $('form').serialize(), function (response) {
  console.log(response);
}, "json");

It seems inconvenient at best to store every inline function in an intermediate variable just to avoid having to write ugly syntax in CoffeeScript.

@jimmycuadra
Copy link
Author

More examples of this can be seen in Pow's source code. There are many places where this comes up and they have opted to use lines starting with commas. That seems really difficult to read to me.

@jashkenas
Copy link

Even though you don't always want to wrap the function in a helper that changes the argument order ... you can always use a nicely named local variable:

logCallback = ->
  console.log "callback called"

foo logCallback, "bar"

@jimmycuadra
Copy link
Author

@jashkenas

Would you be open to the possibility of a new syntax to better support this case? I'm not sure what it might be yet, but it does strike me as heavy handed to use an intermediate variable for a function that would otherwise be inlined, just to avoid having a line with a leading comma.

@jashkenas
Copy link

I doubt it. It's nothing more than a style point, and it'll be hard to find a syntax that makes arbitrary numbers of inline functions combined in any kind of expression into something more readable than properly factored out and named small functions.

That said, if you come up with something you think is groovy -- please open a ticket for it.

Copy link

ghost commented Sep 17, 2011

Is there a drawback to the extra parens in the generated code? This looks rather nice to me:

q.when createRouteDone,
    ((value) ->
        console.log value),
    (reason) ->
        console.log reason

@jimmycuadra
Copy link
Author

@irickt

That's great! It turns out the extra parens aren't even needed in that case. This works too:

q.when createRouteDone,
  (value) ->
    console.log value,
  (reason) ->
    console.log reason,
  "non-function parameter"

Without the first argument, it seems you need parens around the whole thing, but it still looks very good to me:

q.when(
  (value) ->
    console.log value,
  (reason) ->
    console.log reason,
  "non-function parameter"
)

/cc @jashkenas, @reshun

@jrus
Copy link

jrus commented Dec 22, 2011

@jimmycuadra, in your last version, the commas also aren’t necessary:

q.when(
    (value) ->
        console.log value
    (reason) ->
        console.log reason
    "non-function parameter"
)

@jrus
Copy link

jrus commented Dec 22, 2011

I have to admit though that I do find it a bit inconsistent/odd that when arguments are an object (key/value), indentation alone is enough to indicate calling:

somefunc
    foo: '1'
    bar: '2'

but when they’re just positional arguments (whether functions or not), there’s a requirement to either add parentheses, or put one argument and a comma on the same line as the call:

somefunc(
    'yay'
    (spam) -> eggs
)

or:

somefunc 'yay',
    (spam) -> 'eggs'

I’d personally prefer if this was also interpreted as a call instead of a syntax error:

somefunc
    'yay'
    (spam) -> 'eggs'

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