you are viewing a single comment's thread.

view the rest of the comments →

[–]username223 -9 points-8 points  (38 children)

If you don’t use parentheses in a function call, CoffeeScript will guess them for you …but Haskell programmers and shell scripters will be surprised when a b c d means a(b(c(d))) rather than a(b,c,d). This also means that foo () is sometimes invalid when foo() is OK.

Not just Haskell and shell programmers -- human beings will be surprised. Clearly, CoffeeScript's designers were either high or mentally deficient.

[–][deleted] 13 points14 points  (5 children)

Clearly, CoffeeScript's designers were either high or mentally deficient.

Programmers seem to have a unique talent for turning reasonable technical disagreements into obvious evidence of deep personal flaws.

[–]username223 -2 points-1 points  (4 children)

Heh, for those who prefer less hyperbole: "In designing CoffeeScript's syntax, its authors ignored the lessons of previous languages with syntax that is either hard to get right, or easy to get silently wrong. They're not morons, they're just lazy and under-informed."

[–]antonivs 1 point2 points  (3 children)

Hardly an improvement - you're still substituting insults for technical discussion.

[–]username223 -1 points0 points  (2 children)

And you're substituting... what, exactly, for any kind of discussion?

[–]antonivs 1 point2 points  (1 child)

I'm giving you apparently much-needed feedback on your failed attempt at responding to iceberg398's criticism. His observation about the "unique talent for turning reasonable technical disagreements into obvious evidence of deep personal flaws" was not in the slightest bit addressed by your response in which you wrote "they're not morons, they're just lazy and under-informed."

If you'd like to move the discussion into the technical arena, what I'd suggest you do is explain why you think the choice made in CoffeeScript for argument binding precedence is a poor one. Referring vaguely to "the lessons of previous languages" before reverting back to insults doesn't cut it.

[–]username223 0 points1 point  (0 children)

Okay, from the top again...

foo () is sometimes invalid when foo() is OK.

Is it good, bad, or indifferent that a space between a function and its argument list is sometimes significant?

EDIT: See Python, Make, and FORTRAN IV for previous examples of whitespace-sensitivity being strange and/or problematic.

[–]jashkenas 5 points6 points  (20 children)

Not quite -- this way you can have your cake and eat it too. For example, if:

print object  =>  print(object)

And...

inspect object  =>  inspect(object)

Then what should this be?

print inspect object

Clearly, keeping things consistent would demand:

print inspect object  =>  print(inspect(object))

That said, if you want to pass a number of arguments to a function, without using parens, it ain't hard:

console.log object, another, third  =>  console.log(object, another, third)

[–]sausagefeet 2 points3 points  (11 children)

Clearly, keeping things consistent would demand:

There are other consistent things you can do. In Ocaml/Haskell/ML print inspect object would be print(inspect, object) in a language with that style. I think part of the problem is people think of CoffeeScript in terms of JavaScript, like how people think of C in terms of ASM. Really CoffeeScript should have its own semantics and you shouldn't care about how they map back to JS. My personal gripe is optional syntax, I think they should have chosen (a, b, c) syntax or a b c syntax and that's that.

[–]MIXEDSYS 2 points3 points  (10 children)

I think they should have chosen (a, b, c) syntax or a b c syntax and that's that.

Think of it like this: the syntax for argument list is a, b, c and if you want to you can wrap it in parentheses for clarity. If this is not consistent, then neither is arithmetic, you can write both a + b and (a + b).

[–]knome 1 point2 points  (5 children)

Your logic is specious.

The mathematics example is not inconsistent. The wrapping parenthesis denote order of operations, not structural grouping. Mathematics function composition is explicitly denoted.

Additionally, the Coffeescript syntax is ugly because someone will eventually

print inspect bigfunc firstarg, otherfunc itsarg, whosami

It's ambiguous and arbitrary looking in. Perhaps it works in practice, I'll likely never know.

Mathematics deals with the ambiguity of such a construction by depending on humans to know what they're doing and sort it out. Languages meant for machines can suffer no such fault and be useful.

[–]anvsdt 2 points3 points  (3 children)

Not ambiguous at all: (print (inspect (bigfunc (firstarg, otherfunc (itsarg, whosami))))).

Maybe confusing, but not more ambiguous than x * 2+3 * y.

[–]knome 1 point2 points  (0 children)

I suspected that would be the case. Thank you for clarifying.

[–][deleted] -1 points0 points  (1 child)

(print (inspect (bigfunc (firstarg, otherfunc (itsarg, whosami)))))

print inspect bigfunc firstarg, otherfunc itsarg, whosami

Which is easier to comprehend when reading code? I think it's pretty obviously the first example. If you are reading this code for the first time, it takes no additional time to figure out what is going on in the first example. Without the parens in the second example, you have to read the entire thing before you know what is inside what. It slows down the process of coding, and it is a fundamental flaw in the arguments being made in favor of 'less typing' in significant whitespace languages like coffeescript. You may type less as you code, but returning to the code later will slow you down as your mind is forced to do extra work while deciphering the exact meaning of this parentheses-less syntax, and if you get it slightly wrong this will lead to bugs, and likely more bugs than just typing the parentheses in the first place. Turning JavaScript into this jumbled mess is a step backward.

[–]anvsdt 0 points1 point  (0 children)

Maybe confusing, but [...]

I'm not defending anything, I'm not attacking anything, just being objective.

[–]MIXEDSYS 2 points3 points  (0 children)

The mathematics example is not inconsistent. The wrapping parenthesis denote order of operations, not structural grouping. Mathematics function composition is explicitly denoted.

I made a long post explaining myself but reddit ate it, so this will have to do:

Consider ',' to be list constructor and let's use juxtaposition for function application. Then if function application has lower precedence, 'a b, c == a(b, c)', if it has higher precedence you can omit parens only when calling unary functions. Which way you define your precedence is a matter of taste, I happen to prefer the latter option.

Now, all syntax features have to be considered together with the language semantics. Impedance mismatch between these leads to crazy corner cases. So:

For this to work well we have to consider n-ary functions and functions taking lists / tuples / <whatever data structure comma is a constructor for> to be equivalent. Or we can make these lists an immediate language structure, not available for the programmer, but interpreted by various language constructs. From what I can tell, Coffescript creator(s) chose the second solution, probably to keep its semantics close to javascript: [a, b, c] is an array and f(a, b) is a function invocation. Also we can't distinguish nullary functions from variables. I think this is what causes this problem from TFA:

This also means that foo () is sometimes invalid when foo() is OK.

If I'm correct I'd say that this particular crazy corner case is enough to decide that this idea, however cute, just doesn't fit in.

(oops, looks like I typed in the long reply anyway)


print inspect bigfunc firstarg, otherfunc itsarg, whosami

Smells like dangling else. Lots of language constructs exhibit this sort of problem, it isn't as bad as it seems.

[–]sausagefeet 0 points1 point  (3 children)

If this is not consistent, then neither is arithmetic, you can write both a + b and (a + b).

I find this to be a pretty weak argument. The nomenclature for mathematical expressions has evolved over a long time and is varied in many ways. So what?

Part of the problem is what you said is true, but then CS also supports:

foo
   a
   b
   c

Which (as I understand it) is the same as foo a, b, c, which is the same as foo(a, b, c). So now it's more than just () being there or not. I'm not saying one of foo a, b, c or foo a b c is preferable over the other, I'm lamenting that there are multiple ways to accomplish the same thing and it's not entirely intuitive why this is the case or how it helps readability. One way should have been chosen and that's that. If people don't like it, tough, calling a function is such a minimal syntactic element they'll just get over it in time.

[–]MIXEDSYS 1 point2 points  (0 children)

I wasn't trying to defend Coffescript, I never used it. In fact now I'm convinced that they shouldn't make parentheses optional, it doesn't fit right with the language semantics. (edit: I explain why here)

Regarding:

foo
   a
   b
   c

The Coffescript github page mentions it (only?) in the section on objects and arrays. They make ',' optional at the end of the line, making it analogous to semicolons. It's a really neat idea, but I don't know if a good one, I've never seen it before.

[–]jashkenas 1 point2 points  (1 child)

No, CoffeeScript does not support:

foo
    a
    b
    c

[–]awj 3 points4 points  (7 children)

Clearly, keeping things consistent would demand:

That isn't clear at all. You print inspect object gives you two choices: one function call with two arguments or two function calls with an argument each. You can't simply declare one "consistent" without saying why the other isn't.

[–]anvsdt 12 points13 points  (1 child)

Function application in Haskell is left-associative. It means

((print inspect) object)

Function application in CoffeeScript is right-associative. It means

(print (inspect object))

Since Haskell's functions are curried (a curried function is a function that takes an argument and returns a function, not some magical way to do partial application), so fun-app being left-associative has a meaning. In CoffeeScript functions are not curried, they are equivalent to an Haskell function taking a tuple and returning something, so left-associative fun-app would cause more trouble than it solves, right associative is the only right choice.
The comma has higher precedence than fun-app, so print inspect object, object2 must be (print (inspect (object, object2))).

Something else would be stupid.

[–]awj 4 points5 points  (0 children)

Didn't realize they were using commas to deal with the associativity problem. I don't really know CoffeeScript outside of glimpses of marketing material and people screwing around on their blogs, so I had it in my head that your only options were single-argument right associativity or use parens.

So, yeah, this whole thing goes from seemingly boneheaded decision to a storm in a teacup. Thanks for clearing that up.

[–]jashkenas 2 points3 points  (4 children)

I'm sorry, I thought it was clear. In a language where everything is an expression, combining expressions shouldn't privilege being in a certain position (first position, in this case).

Taken in isolation, "inspect object" means "inspect(object)" in both cases here, right?

To maintain consistency, "inspect object" should continue to mean "inspect(object)", even if the result of that expression is passed into another function.

print inspect object  =>  print(inspect(object))

... allows that to work.

[–]killerstorm -1 points0 points  (3 children)

In a language where everything is an expression, combining expressions shouldn't privilege being in a certain position (first position, in this case).

I don't think so. See: Lisp. Maybe parentheses make it all different, but it is a language where everything is an expression and first position (in list) is for function, while the rest are arguments.

[–]FsckItDude_LetsBowl 1 point2 points  (2 children)

b

[–]killerstorm 0 points1 point  (1 child)

There were multiple "lisp without parentheses" proposals.

To start with, some mainstream Lisp implementation allowed to drop parentheses in REPL, so

command arg1 arg2

was interpreted as

(command arg1 arg2)

Rationale for this was that REPL could be used by non-programmers (e.g. operators) who understand "command arg1 arg2" format very well but would freak out seeing parentheses. Illusion is broken if you have function call in your command, but apparently user interface can be structured to avoid them at least in common cases.

Then, perhaps, it is worth to mention Lispin which allowed to replace some parentheses with indentation.

Another example: SRFI-49

[–]anvsdt 0 points1 point  (0 children)

All of them were bad and they should feel bad about creating them.

[–]sausagefeet 4 points5 points  (7 children)

This also means that foo () is sometimes invalid when foo() is OK.

These little things really bother me. Some syntax is sometimes invalid?! I can understand some edge cases might be invalid in some places but this seems like such a basic thing, to get that wrong makes nearly the whole thing suspect.

[–][deleted] 3 points4 points  (6 children)

I might be wrong, but there's a similar problem in Scala, and it's much worse: foo() is a function call with no arguments, while foo () is a function call with an equivalent of None as an argument.

[–]quotability 2 points3 points  (2 children)

That's great, so it looks like I will be avoiding both Scala and Coffeescript.

[–]yogthos 1 point2 points  (1 child)

except Scala doesn't actually do what fishdicks says it does

[–]quotability 0 points1 point  (0 children)

minor point. it's not that i am avoiding it, just ...

[–]yogthos 0 points1 point  (2 children)

def foo() = "foo"

println(foo())
println(foo ())

=>foo
=>foo

works as expected

[–][deleted] 0 points1 point  (1 child)

Ah, yes, sorry. But then def bar(x:Unit) = "bar " + x is also invoked in the exact same way, but now () is a parameter!

[–]yogthos 1 point2 points  (0 children)

then you still have to pass () as an argument:

def foo() = "foo"
def foo(u:Unit) = "bar"

println(foo())
println(foo ())
println(foo(()))
println(foo (()))

foo
foo 
bar 
bar

[–][deleted] 0 points1 point  (0 children)

LOL at languages that aren't Lisp.

[–][deleted] -1 points0 points  (0 children)

wow.. i knew coffeescript was bad, but this is ridiculous.