Skip to content
New issue

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

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

Already on GitHub? # to your account

syntax error #43

Closed
mark-hahn opened this issue Sep 8, 2012 · 12 comments
Closed

syntax error #43

mark-hahn opened this issue Sep 8, 2012 · 12 comments
Labels

Comments

@mark-hahn
Copy link

I'm trying redux for the first time on production code that works in cs 1.3.3. Here are the first two syntax errors before I gave up ...

Syntax error on line 1, column 1: unexpected '#'
1 : ###
^ :~^
2 : /root/CoffeeScriptRedux/bin/coffee --js < /ri/utils/coffee_watcher.cof
3 : ###
4 :

Syntax error on line 22, column 13: unexpected '='
19 : monitor = compileOne = null
20 : firstPass = yes
21 :
22 : do doOneDir = ->
^^ :~~~~~~~~~~~~~^
23 :    if not (dir = dirsPending.shift())
24 :            if firstPass
2
@michaelficarra
Copy link
Owner

These issues are both known. The first issue is that I simply haven't yet added support for block comments, since they provide no functionality, and are an extremely low priority for me right now. Just replace them with single-line comments, which are ignored. I'll get to them when other real problems have been taken care of.

The second issue is one I'm currently debating over how to solve, so I'm glad you've brought it up publicly. Really, you shouldn't be allowed to perform an assignment within a higher-precedence operation like a do expression. I've been running examples through that contain expressions like a and b or c = d, and though it is currently accepted by the current overly-permissive compiler, I think it should produce an error because it just doesn't make sense. With the correct operator precedence, that expression would be disambiguated as ((a and b) or c) = d. Doesn't look so good any more now. Likewise, the example you provided is (in my mind, correctly) being parsed as (do doOneDir) = -> ..., which is invalid. @jashkenas: what do you think about this?

@mark-hahn
Copy link
Author

you shouldn't be allowed to perform an assignment within a higher-precedence operation like a do expression.

I guess it could be do (func = ->) but then I'd have to put a paren at the end of the func definition which would be a pain in the butt. This is a very common use case. I've used it hundreds of times.

I was really hoping redux would be backwards compatible. I've got around 40,000 lines of coffescript and it would be nice if I didn't have to support 1.x and 2.x at the same time for different projects. But that is my selfishness. Do what is right for the community.

@mark-hahn
Copy link
Author

Now that I think about it, if I had to use do (func = ->) I wouldn't use do any more. It would be cleaner to do

func = ->
func()

which is less expressive than the old way. Couldn't you just make do low-precedence? Your and or example has a clear precedence but why does do have to be high-precedence?

@epidemian
Copy link

Nice discussion. I would tend towards allowing things like do foo = -> bar (equivalent to do (foo = -> bar)) or a and b = c (equivalent to a and (b = c)).

In the case of do foo = -> though, i'm curious about where are you using that, @mark-hahn. If it is for auto-invoking a function that also needs to be used later on, then i think separating the function definition and it's first usage as you suggested in the last message would be clearer. But i could also imagine that it might be for auto-invoking recursive functions that need a reference to themselves inside the definition; something like:

do drawTree = (tree = root, depth = 0) ->
  return unless tree
  drawValue tree.value, depth
  drawTree tree.left, depth + 1 
  drawTree tree.right, depth + 1

About the mixed and/or and = operators, i think that = is quite special compared to other operators in that the left-hand side should only be an identifier, and not an expression of any kind. Therefore, it shouldn't obey the same precedence rules as the other operators (which allow the LHS and RHS to be arbitrary expressions). a and b = c should only mean a and (b = c) and not (a and b) = c, as only the former has a = LHS that makes sense.

Also, i sometimes find things like a and b = c to be useful. For example, when i want to cache the result of c, which should only be computed if a is truthly, for later usage.

Finally, notice that the argument about how = is special and may not follow normal operator precedence rules can also be applied to allow assignments inside do's :)

@michaelficarra
Copy link
Owner

@mark-hahn:

I was really hoping redux would be backwards compatible

That's the goal, but the compatibility is with the CoffeeScript features as we intended them to be implemented. For instance, I didn't duplicate any of the bugs in the current compiler, so it's no backwards compatible in that way. But that was always the goal. I'm just not sure if we ever intended to allow people to throw unqualified assignment operations into just about everything.

@epidemian:

Also, i sometimes find things like a and b = c to be useful. For example, when i want to cache the result of c, which should only be computed if a is truthly, for later usage.

Remember: this wouldn't be disallowed, you would just need to raise the precedence of the assignment by putting it in parentheses.

@mark-hahn
Copy link
Author

it might be for auto-invoking recursive functions that need a reference to themselves inside the definition;

Exactly. Here is the code that gave the error, minus a bunch of snipping. Note the nesting and how messy it would be if = were forced to low precedence ...

do doOneDir = ->
    if not (dir = dirsPending.shift()) 
        dirsPending = ['/ri', '/rip']
        doOneDir()
        return

    fs.readdir dir, (err, files) ->
        do doOneFile = ->
            if not (file = files.shift()) then doOneDir(); return

            filePath = path.join dir, file
            process filePath, doOneFile

@michaelficarra Please don't take this away from me. I really like @epidemian's idea of changing the precedence of = when the left side isn't legal.

Coffescript is very pragmatic. Making it less useful for the sake of syntactic "purity" would be a big loss to the language.

@epidemian
Copy link

@mark-hahn Cool. Yeah, though i've never used them, i think recursive functions are a valid use of do func = ->. Thanks for the examples 👍

Remember: this wouldn't be disallowed, you would just need to raise the precedence of the assignment by putting it in parentheses.

Yeah, but is it necessary? Does a and b = c have some kind of ambiguity?

In my previous comment i said that the only requirement was that the LHS of = needed to be an identifier. That was incorrect. The LHS needs to be an lvalue, which should be something like lvalue ::= identifier | lvalue "." identifier | lvalue "[" expression "]" if i'm not forgetting some other(s) valid lvalue(s).

My point is that = is not like the other operators that accept an arbitrary expression in both sides, so i don't see why a case like a and b = c would need to be disambiguated with parentheses around the assignment.

I don't know if it's a good example but, in Ruby a && b = c is a valid expression. In Python though, as assignments are statements and not expressions, that's not valid syntax (nor using a simple assignment in an if condition).

Anyway, that's just my opinion, i'm also interested in knowing what @jashkenas thinks.

@jashkenas
Copy link

Hrm. I don't know about the "correct operator precedence"-ness of it all, but I think it's highly desirable to have:

defaults and defaults = options
options or options = defaults

... work like this:

defaults and (defaults = options)
options or (options = defaults)

... even though they're just longhand versions of and= and or=. They don't involve any special confusion when trying to be read. That said, they're not valid in vanilla JavaScript, so I can see the case for "correcting" them. Would it be too difficult to add a special case? Or is there any undesirable behavior that including the special case would allow?

@rlidwka
Copy link

rlidwka commented Sep 24, 2012

I use auto-invoking functions with do smth = -> a lot, so I'd appreciate if it'll be supported here.

@KamilaBorowska
Copy link

Just wondering, isn't

do (reuse = ->
  scope = 'I have new scope'
)

and:

(reuse = ->
  scope = 'I have new scope'
)()

equivalent to:

reuse = ->
  scope = 'I have new scope'
reuse()

@michaelficarra
Copy link
Owner

@glitchmr: yes

@heartsentwined
Copy link

The use case of @jashkenas is different from that of @epidemian. For "default value assignment" that @jashkenas mentioned, I would favor the shorthand &&=, for arguably better readability.

However, @epidemian mentioned a valid use case, in that one intends to work with various different variables. For example:

if foo && bar = funcThatReturnsSomething()
  # do something with bar

This is, almost certainly, an upgrade from

bar = funcThatReturnsSomething()
if foo && bar
  # do something with bar

I think that, if coffeescript aims to be a wrapper script that provides convenience and irons out inconveniences of javascript, then I think this being a useful shorthand should already be a valid argument.

Symmetry (hence predictability) would also be a concern. On one hand, we have the already implemented do foo = -> syntax, itself being a justified as a shorthand for recursive function use cases as I understand.

On the other hand, if (foo = 'foo') && (bar = 'bar') would now become, kind of, asymmetric, in that the second pair of parens are now optional, but not the first. Although I would still argue that in this case, keeping both is significantly more readable than if (foo = 'foo') && bar = 'bar', even if the programmer is allowed the choice.

# for free to join this conversation on GitHub. Already have an account? # to comment
Labels
Projects
None yet
Development

No branches or pull requests

7 participants