Hacker News new | ask | show | jobs
by unoti 3899 days ago

  > It’s reasonable to assume "foo bar and hello world" will compile to either:
  > foo(bar) && hello(world)
  > foo(bar && hello(world))
Feel free to sprinkle in additional operators to increase the clarity and readability of your code. Coffeescript is extreme in what it allows. But even C code can be made more clear by using more operators than the compiler actually demands.

I'm not really trying to come to the defense of Coffeescript here, but I think it's worth noting that adding more operators for clarity is a good thing. I even go as far as adding additional variables for clarity, because the names can show what you're thinking as you do the computations involved in a complex expression.

Although Coffeescript is worse than most, any language will allow you to write expressions that are hard for humans to parse.

6 comments

> [I use] additional variables for clarity, because the names can show what you're thinking as you do the computations involved in a complex expression

For me this was one of the greatest lessons of Clean Code by Bob Martin, in my opinion a wonderful book which completely changed and dramatically improved the way I write code. By improvement I mean simply the ease with which I can understand and modify my own code months after writing it/ seeing it last.

There's a slight contradiction here with another great lesson of that book though which is short code (by lines) > longer code simply because it's easier to consume in a single glance. There's certainly a delicate balance at play here.

It's a great thing to break down a complex process into steps, naming those steps as you go, and storing things in variables achieves that. However sometimes a better tool, and again another frequent suggestion in Clean Code (really, all credit to Bob Martin for all these ideas) is to instead break the steps into clearly-named functions, if this is possible. Chaining or nesting a series of function calls, depending on the language, can be more readable still than the variables approach and at the same time more concise.

Another great tool for the belt and one of the reasons I have come to always prefer programming in a functional style vs a more stateful, OO style, where I can (even within pretty OO languages such as Java - though the lambdas in 8 have greatly improved the ease with which this can be done).

> another great lesson of that book though which is short code (by lines) > longer code simply because it's easier to consume in a single glance.

I hate this lesson. And it's not because short code is a bad thing...when you find code that's well structured and broken down such that no method/function needs to be long, it's a joy to read. The problem comes when people learn the rule without learning the why behind it. And that leads to one logical function/method that's huge and has now been broken down into many nested methods/functions simply to satisfy the "none are long" requirement. Now, as a reader, I've still got to keep the entire context of the method/function in my head, but I now have to jump all over the code to find out what the single, large method actually does. I'd rather have a single, 200-line code block to look at than this sort of jumble.

What developers need to learn is that short methods/functions are a consequence of good techniques, not a technique in and of themselves and that long methods/functions are not bad in and of themselves, but they indicate that it's very likely that the developer hasn't put the proper thought into separating concerns and ensuring that each unit of his/her code has a single responsibility.

Sorry to get on my soapbox, but I've seen this "lesson" make ugly code even uglier when programmers don't understand the why behind the lesson.

If you stick to purely functional pieces (=no mutable state), than following the letter of the lesson will yield the spirit---because the context becomes so much simpler.
"Chaining or nesting a series of function calls, depending on the language, can be more readable still than the variables approach and at the same time more concise".

Couldnt agree more, variables are just clutter you have to keep mental track of, and working with series of transformations is much easier (for me at least) to process. I would be interested to know if its like this for all developers, perhaps not, since I know some that are also opposed to recursion.

but for me this:

    sum([]) = 0.
    sum([H|T]) = H + sum(T).
is much easier to understand than:

    function sum(ary) {
      var i = ary.length,
       res;
      while(--i) {
        res += ary[i];
      }
      return res;
    }

    function sum(array) {
      return array.reduce((a, b) => a + b);
    }
With a loop:

    function sum(array) {
      let acc = 0;
      for(let val of array) {
        acc += val;
      }
      return acc;
    }
What's the value of sum for an empty array in your example?
The reduce one needs an initial value of 0 for that to work. Then both versions will return 0 if the array is empty.

    array.reduce((a, b) => a + b, 0);

    sum = (values) ->
      values.reduce (result, x) -> result + x
In most languages the parsing rules are pretty sane even for humans. The question you're quoting is the perfect example of a rule that's unreasonable, no matter the answer, because there's no way in hell a normal person can remember that without first being burned and I'm pretty sure that's hard for the compiler to parse as well.

> Although Coffeescript is worse than most, any language will allow you to write expressions that are hard for humans to parse

But regardless of the truthiness of this statement, it has no value. It's like saying that no matter what you eat, in large quantities it can cause humans harm. And yet there's a clear distinction between ingesting mercury or lettuce.

The message is: write clear code. Writing ambiguous code is a sin committed by the developer, not the language (although it is easier to commit that sin in CoffeeScript).
agreed, add a good linter and problem solved. Ruby has been this flexible since its inception and you don't see people complaining about ambiguity because they learn how the language works. When there's ambiguity, be explicit.
When there's ambiguity, be explicit.

Many (too many) programers seem to take a viewpoint of "only type the minimum numbers of characters necessary to get something to work." Then they make their programming goal to be the least characters possible as long as the compiler accepts their input.

complaining about ambiguity because they learn how the language works.

Some programmers also enjoy learning all the ambiguous edge cases then making sure their code fits them exactly in the way they intend, but not necessarily the way others will read the code.

That's why we end up with awful things like "if (abc) def; else hij;" or worse "if (abc) { def; } else hij;", etc. Plus, if your language isn't line-sensitive, using weak syntax but pretty alignment is just a recipe for future failures a few steps removed from when you originally introduced the future inconsistencies.

Ugh, I used to write coffeescript and I was that way for a while. I left off parens, brackets, commas, etc. wherever possible. I thought it was great at the time, but when I came back to the code after writing in other languages it looked awful, and I wasn't always sure how to read it. Being a little more verbose will save you a lot of time in the future.
Ruby had to make changes for almost this exact syntax case after it bit people by the thousands, they added:

     foo () (irb) :2: warning: don't put space before argument parentheses .
Julia, as of v0.4, has deprecated this syntax [1]. I am not sure where it originally stems from, C? It makes sense from a compiler stand-point since the argument call semantics would most likely be inferred after tokenisation.

[1]: https://github.com/JuliaLang/julia/commit/28e7bd4536d06a9e13...

As a person who uses Python to a great degree because of its explicitness, I complain about this in Ruby all the time.
> any language will allow you to write expressions that are hard for humans to parse

The problem isn't "hard to parse", but rather "easy to parse --- in two or more ways".

definitely! and, be consistent.

an example that is clear and consistent, albeit contrived, would be to wrap arguments when there are more than one:

foo bar

foo (bin, bar)

or if that's too "iffy" for you, you just always wrap with parens!

In CoffeeScript I always do (foo bin, bar) when I need the clarity. Found it in a major style guide and I loved it.

E.g. (foo bar) and (hello world)

Only two steps away from embracing the dark side:

  (and (foo bar) (hello world))
Or from the other dark side (Haskell):

    foo bar && hello world
and for more than one argument:

    foo bin bar && hello world
Livescript has sane defaults: `foo bar and hello world` -> `foo(bar) && hello(world)`