Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

More clarification about implicit object literals #575

Closed
timmfin opened this issue Aug 5, 2010 · 10 comments
Closed

More clarification about implicit object literals #575

timmfin opened this issue Aug 5, 2010 · 10 comments

Comments

@timmfin
Copy link

timmfin commented Aug 5, 2010

When moving code over to .9, I was surprised that you couldn't use implicit object literals for function arguments:

# From the documentation
kids =
    brother:
        name: "Max"
        age:  11
    sister:
        name: "Ida"
        age:  9

Sweet, that means I can create classes with objects this way, right?

# Fails
TEST = new ClassThatTakesAnObject
    callback: (arg) ->
        puts arg

# Fails
TEST = new ClassThatTakesAnObject
    optional: 'one'

# Works
TEST = new ClassThatTakesAnObject(
    callback: (arg) ->
        puts arg
)

# Works
TEST = new ClassThatTakesAnObject {
    callback: (arg) ->
        puts arg
}

Guess not. How about function calls instead of constructors?

# Fails
TEST = funcThatTakesAnObject
    optional: 'one'

# Fails
TEST = funcThatTakesAnObject
    callback: (arg) ->
        puts arg

# Fails
funcThatTakesAnObject
    callback: (arg) ->
        puts arg

# Works
funcThatTakesAnObject {
    callback: (arg) ->
        puts arg            
}

Bummer. Is this a bug? Or difficult to pull off in the grammer?

If it is difficult to implement, then I think this should be mentioned in the doc because it seems natural to take the kids example and add a function call to it.

@mrblonde
Copy link

mrblonde commented Aug 6, 2010

Big big +1 on this. Given how often large parameter sets are used in JS API design, I'd really like to see some kind of easy multiline method call with implicit object literal. Here's a concrete example I pulled out of my project that I think will occur many times for anyone using CoffeeScript

# current
$.ajax {
  url: editor.attr("action")
  type: "POST"
  dataType: "json"
  data: editor.serialize()
  complete: ->
    progress.html("done")
}


# wish
$.ajax
  url: editor.attr("action")
  type: "POST"
  dataType: "json"
  data: editor.serialize()
  complete: ->
    progress.html("done")

@TrevorBurnham
Copy link
Collaborator

+1, but let's generalize this a bit. The example you give is a special case of the following:

a
  b

This is currently a syntax error, but it should be interpreted as

a(b);

You can get around this using a backslash escape after each line (except the last), but since the syntax is unambiguous, backslash escapes shouldn't be necessary. Here's a more complete test case (though without any object literals):

a
  b
    c
  d = ->
    e
  f

should be interpreted as a(b(c), (d = -> e), f). (Note that the parentheses around (d = -> e) shouldn't be necessary; the comma after e should suffice to indicate the end of thefunction.)

@StanAngeloff
Copy link
Contributor

I reckon this is going to be quite the challenge to allow mainly because this is valid:

a(  # code and indents below unchanged
  b
    c
  d = ->
    e
  f)

and it produces quite the different output to what is expected.

@TrevorBurnham
Copy link
Collaborator

@StanAngeloff: So, the one difference between the output from the snippet you gave and the output I expect is that b and c are both considered to be arguments to a. That is, the indentation in front of c is ignored. You could get around this with parentheses

a(
  b(
    c)
  d = ->
    e
  f)

but what is this, Lisp? :)

Particularly now that we allow significant indentation for YAML-style object literals, I think it's very important for CoffeeScript to allow significant indentation for function arguments. Do you agree?

@StanAngeloff
Copy link
Contributor

I have tried the above myself on a few occasions (as well with impl object literals) so I kind of expected it to work as per this ticket so I am in no disagreement. However I recall Jeremy allowing more flexible indentation inside functions as a result of several issues posted on the tracker so I am unsure how easy it is going to be to allow what we are all expecting.

@jashkenas
Copy link
Owner

Here's the minimal test case that demonstrates what's problematic about this idea:

if one
  two()

Right now, that parses into:

if (one) {
  two();
}

With this ticket, it would also parse to:

if (one(two()) {};

If you want to call against an implicit object, you need to mark that object as callable, either by using parentheses for the call, or braces for the object ... Or does anyone have a way to fix this ambiguous parse? Remember that there can be an arbitrarily complex expression as the condition to the "if".

@rofrankel
Copy link

I guess this would be backwards incompatible in a pretty painful way (at least as bad as symbology) but what if you could add a then to specify the former parse, and make the latter the default?

I don't think that's a good solution, but honestly I don't know if there is one...I think the combination of semantic whitespace, paren-free function calls, and brace-free object literals is a recipe for confusing behavior.

@TrevorBurnham
Copy link
Collaborator

@jashkenas: Your example certainly gave me pause. But on reflection, I think it's acceptable—even ideal—for

if one
  two()

to retain its current meaning of if one then two(), but to make

one
  two()

mean one(two()). If you think of the indentation after an if as being equivalent to the then keyword, then this is perfectly consistent behavior.

Will it flummox newbies? Yes, but not as much as the current behavior when passing function arguments that span multiple lines, which I can attest has tripped me up repeatedly.

@jashkenas
Copy link
Owner

Alright folks, here's the commit for this:

http://github.com/jashkenas/coffee-script/commit/7a0d95c61243f8bd90750e2f411edf814666d5df

It doesn't go into the weeds and allow syntax like this:

one
  two
    three

... which I think would set a poor precedent, but does allow implicit objects within a block to be treated as arguments, as if you had added the { on the previous line:

jQuery.ajax
  url: editor.url
  type: 'POST'
  complete: -> progress.text 'done'

Compiles into this JavaScript:

jQuery.ajax({
  url: editor.url,
  type: 'POST',
  complete: function() {
    return progress.text('done');
  }
});

I'm digging it. Thanks for opening the ticket.

@dbrans
Copy link

dbrans commented Dec 16, 2010

This still leaves
if one
two: 0
as ambiguous... of course until you compile it and see how CS handles it.
In addition, there is a significant "asymmetry" in the language:

Having to write
foo a,
b
c
Instead of:
foo
a
b
c
The latter looks nicer, is more consistent, and can be used to create easy-to-read DSLs for nested structures (for producing html, for example)

This issue was closed.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

7 participants