Hacker News new | comments | show | ask | jobs | submit login
Math Envy and CoffeeScript's Foibles (johnbender.us)
69 points by johnbender on Nov 29, 2012 | hide | past | web | favorite | 30 comments

Very cool post -- looking forward to part 2.

For what it's worth, the syntax foible up for discussion here is intentional. Because CoffeeScript allows you to pass an argument on the right hand side into a function on the left hand side without having to use parentheses, there are several cases where we need to disambiguate:

    f / a /i
Repeated division, or a function being passed a regex with space in it? CoffeeScript treats it as:

    f / a / i
The example in the post is:

    func (arg1, arg2) ->
We disambiguate by saying that if the parentheses are attached to the identifier on the left, then they're part of the function call, otherwise they're part of the argument list as usual. It seems fiddly, but is useful for writing code like this:

    load(url).then (response) ->
      handle response
... as well as code like this:

    reduce(list, {}) ->
      for arg in arguments

This is an important point, and I'm glad I can expand a bit on it here.

Considered as purely a syntactic issue there's not much for discussion. Fix the parser. But! Conceding that method/function application is useful along side the invocation operator `()` (which it certainly seems to be) makes this an interesting semantic issue. That is can two terms mean different things but look "similar"

I have no interest in telling anyone how to build a programming language or that one approach is subjectively "wrong", but knowing that certain term pairs in a language could be ambiguous with respect to each other seems like valuable information. Provided to the creator at the right time it might help with language design.

Eh? This is where I don't follow. What exactly are you recommending should be changed when you say "fix the parser"? Choose the other disambiguation, or simply use an entirely different function literal in the first place?

For what it's worth, this particular ambiguity was discussed as part of the original language design. It was decided that having parentheses for both argument definitions and argument application was useful parallelism, and that function literals shouldn't have to have a "def", "func", "function", or other prefix. From those two ideas:

    # Definition
    (input) -> output

    # Application
... the rest of it falls out naturally.

s/fix the parser/change the parser/, meaning choose not to support both types of invocation.

Both Ruby and Perl have the same challenge, and both solve it by treating the expression differently depending on whether f is a variable or a function/method. As it turns out, you can't actually parse Perl 5 [1], but Ruby can be statically parsed [2], and resolves this ambiguity based on context.

I don't actually know anything about CoffeeScript's parser, and I imagine that it complicates the grammar significantly, but would the same approach not be possible there?

(Personally, I'm quite happy to type parentheses in exchange for clear syntax, and it's these very aspects of CoffeeScript that put me off it: I really find it tricky to follow what's going on when function definitions, function calls, and named parameters are mingled. At least regular JavaScript is syntactically simple, even if I do have to type function more often and worry about this.)

[1] http://www.perlmonks.org/?node_id=663393 [2] http://po-ru.com/diary/ruby-parsing-ambiguities/

[1] is wrong. Perl is defined over a finite memory computer, not a Turing machine that by definition has infinite memory. The Turing halting theorem does not apply.

If you ran perl on a machine with variable size pointers and hotpluggable ports, and added more RAM as needed, you would have enough RAM for any halting program.

I actually prefer this feature to JavaScript. Coffee forces the developer to use parentheses in a meaningful way. Many developers are using outer parentheses in Coffee, similar to Haskell, and I think that provides more clarity.

Realistically in CoffeeScript these versions of the article's examples are far more common, and much less ambiguous:

    doSomething = -> true

    doSomething -> false   or:    doSomething (-> false), otherarg

    doSomething()(-> false)
I feel that extreme flexibility of JS spacing leads to much more ambiguity. Syntax highlighting is in every modern IDE and I never find myself confused. A programmer can write ambiguous code in any language.

I think the amount of controversy over CoffeeScript's syntax is a indicator of its level of innovation.

one common issue i found is in the use of parantheses , around some expressions .

f (c)->2*x , 5 is ambiguous when you read it ( not sure it even compiles ).

or for generator expressions

f(x) if x<0 for x in E

is really ambiguous when you read it.

Maybe CS should be "less smart" and more strict regarding syntax. Because now the task of writing non ambiguous code lies in the hand of the developer. I really think the language should not evolve in a "super language for ninja" like COCO ( which is just too frightening and insane), just keep exposing the functional side of javascript in a better way.

This post left me feeling pretty bad about Coffeescript. Those semantics weren't well thought out at all.

In defense of CoffeeScript, I'm not convinced that semantic issues like this are at all obvious during the creation of a programming language. Honestly it'd be dumb luck landing on this one in particular since you can imagine that the grammar definition for lambdas and invocation/application aren't sitting right next to each other. That's what got me thinking about how you could explore the term space automatically.

Flaws like this arise from trying to be too clever with syntax. Function application with parentheses, literal regular expressions without clear delimiters, etc. Syntax should be describable in a handful of universal rules. Lisp is the extreme in this regard, the rule is simply: parentheses nest properly. But you can build surprisingly rich syntax with a simple set of rules so long as you lay out those rules first and conform the syntax to them as you go.

Io language is another great example of a language with a minimal set of universal rules yet incredibly rich metaprogramming.


This particular issue was discussed during the design of the function definition and application grammar. (see above comment)

Right, but it's possible that the trade offs inherent in supporting both forms of method/function invocation and how they interact with function literals may not have been obvious to a different person.

Moreover in a different language it seems like it would be exceptionally hard to know how everything will interact, which is where a tool that can help you explore the term space automatically has value.

I dunno, I think the regex example that Ashkenas posted is a pretty good argument that the syntax decision wasn't a mistake, even if the actual decision is up for debate. Syntactic sugar has inevitable tradeoffs and edge cases if the goal is to minimize the alteration of existing symbols and grammar.

I'll keep on using JavaScript.

I hope you came to this conclusion by actually trying out the language.

I have written thousands of lines of coffeescript and I have never faced the mentioned problems.

Besides, you can always use the pragmatic approach and use parenthesis in case of ambiguity.

I work in teams scattered across the globe with different experience levels, so we all tend to avoid fashion languages.

On top of that, experience shows that transpiling to JavaScript increases the debugging overhead as now besides debugging our own code, we need to debug what the Transpiler has generated.

Source-maps won't cut it, because they are not available in all browsers we usually are required to support.

Check out Dart. It has less problems than JavaScript, not more.

Let me know when it is supported natively in all browsers.

The teams I work with won't use any language that translates code into JavaScript.

I assumed your comment would be related to the article, my bad.

Commenting a little late, but I just got around to reading this. One question, if anyone is still reading:

How is looking at distance between derivation trees any more useful than just looking at distance between whatever ASTs the coffeescript parser makes? In other words, why involve operational semantics if you can just talk about the trees produced by a BNF grammar for your language?

dear god, please do not ever do any of that

Why a long essay on semantics to argue a point about coffeescripts choice of whitespace syntax?

Totally fair question, which I'm reading as, "why did you take the time to write this?"

1. I've been reading through Types and Programming Languages in fits and starts over the last year or so. Operational Semantics is one of the topics covered in the book and I wanted to test my knowledge of it. I did the same thing with Category Theory and jQuery

2. I get a kick out of "teaching" (that's an overestimation of what I'm doing) things like Operational Semantics using examples that developers can relate to out of their own experience. This is also part vanity. I like people to read/enjoy/approve of my ideas, but that requires that they be able to understand them.

3. Writing is a difficult but rewarding exercise. It makes you better at thinking through and conveying your ideas in all mediums.

Hopefully that answers your question.

Also, probably more importantly this isn't just about CoffeeScript. It's finding a way to let language designers know that something might confusing during the design process (assuming that's possible).

This is probably just going to be buried, but I think someone needs to tell you, Mr. Bender, that you are failing to see the forest through the trees.

In this work and in other projects of yours that I see on github, I see an incredible amount of effort wasted on semantics, formal theory, notation and attempted minor refinements to syntax.

Maybe your obvious intelligence would serve us all better if you focused more on creating new programming languages or software development tools rather than refining existing systems that are quite functional.

Have you ever created a tool that has anything approaching the utility of CoffeeScript? Because I think that unfortunately in your blog post you've proven that your study of mathematics was actually wasted.

Math is just obfuscated code that doesn't compile or run.

John Bender created Vagrant[1] with me back in 2010. I don't consider it even close to the caliber of CoffeeScript, but I'd say that is a pretty solid contribution to the world.

I should also note that basically the only part of Vagrant that hasn't changed in the past 3 years are the parts John came up with based on some pretty impressive mathematical underpinnings.

Research almost always appears useless on the surface, but it is what makes all of what we do on these clicky-clacky machines possible today.

[1]: http://vagrantup.com

How is vagrant not close to the caliber of coffee script? Vagrant solves an actual problem.

Guidelines | FAQ | Support | API | Security | Lists | Bookmarklet | Legal | Apply to YC | Contact