Hacker News new | ask | show | jobs
by megapatch 3207 days ago
Yes, please! This applies to many other lists as well, not only restricted to JSON.

Besides, imagine a world of Java, Javascript, C-like anything, where you would be DISALLOWED to have a semicolon after the last statement in a block. Crazy thought, right?

7 comments

In Erlang, commas and semicolons are separators not terminators. So you are in fact disallowed to have a semicolon at the end of a sequence of clauses, it's either nothing (for case) or a period (for functions)
It sounds dumb, but this was actually one of the things that turned me off Erlang despite my interest.
It's definitely weird, but it allows for neat stuff e.g. in Emacs' erlang-mode when you type a semicolon it automatically creates a new clause (function or case) for you.
In Rust, omitting the last semicolon in a block indicates an implicit return of the final expression of the block.
That's terrible, to have semantics depend on such a tiny syntax change that's so easy to miss.
It's actually super nifty and IMO a great feature. However I think one of the reasons it works so well is that Rust is statically and very strongly typed, in a more dynamic language it would be hard to keep track of what's going on.

An other reason it works is that almost everything is an expression in Rust, including things like `if` statements. So for instance you can write:

    let message =
        if auth_ok() {
            "success"
        } else if tries < 3 {
            "try again"
        } else {
            "failure"
        };
If you add semicolumns after the strings the `if` will always evaluate to nil here.

Note that this won't work if one branch returns string an and other returns an integer for instance since the type of "message" must be known at compile time.

It also works well for getter functions and lambdas, for instance:

    fn is_empty(&self) -> bool {
        self.len == 0
    }
or:

    list.sort_by(|a, b| a.key < b.key);
This way you focus on what's important.

I can see where you're coming from though, it's easy to dismiss this feature as a bad idea on paper, especially if you've been traumatized with Javascript's insane handling of semicolumns. In practice however it's a rather useful sugar and so far it's been harmless in my experience. It basically makes the language more lispy.

I read your comment more as an endorsement of implicit returns. Make no mistake, I love those. I've been using them most of my life, first in OCaml and then in Scala.

I just think that using the semicolon to make the function return unit instead is problematic. It would be better to have the type system guide that decision, like in Scala. It would be even better to have an explicit `ignore` function or something (like in OCaml) to signal to the compiler that you really want to ignore the value and you only care about uthe side effect. I mean, why would you ever write just e.g. `a < b;`?

> I just think that using the semicolon to make the function return unit instead is problematic.

Agree. The idea to attach additional semantics to ; is completely nuts, especially as ; is mandatory in Rust (usually, there are odd corner cases where it is not allowed).

Just get rid of mandatory ; completely and let the type system handle the rest.

; is used to separate statements, how do you propose to get rid of that?

I guess you could use significant newlines like python but I never really liked that. I think Rust's compromise is pretty decent.

In particular it's the first time I hear people complaining about it, so far most users (myself included) seem to be praising it. Javascript's handling of semicolon is nuts, I wouldn't say Rust's is.

IANAPLRESEARCHER, but to my understanding, it actually unifies the semantics. The return value is always the result of the last expression, and its type may either be something non-trivial, or `()` which is what `expr;` returns.

This makes it easier to write and use generic code for which you don't know the return type (could be `String`, could be `()`), without insisting on special cases for "returns a thing" and "doesn't".

Example: I hold a thing of type `T` and want to let you call a function on it. I can write this generically as

    pub trait WithMut<T> {
      fn with_mut<R, F: Fn(&mut T)->R>(&mut self, func: F) -> R;
    }
and now with one implementation I can deal both with functions that return meaningful values, and "computations" that do some work but return nothing (other than `()`).
No it isn’t. In practice it’s extremely obvious when it is happening. And if you mess up and add a semi colon accidentally then the type checker will catch it.
I wish there was times that HN supported Quadratic Voting[0] so that I could more (but costly) up votes to a really sane and pertinent comment.

[0] http://ericposner.com/quadratic-voting/

Have you tried writing Rust? I have and I think this feature is great.

I upvoted you for your link, but actually the argument that it would be good because people who care more will make a better decision than those who don't does not completely convince me. Someone could care a lot about something but still have the wrong idea about it.

Thanks for the link, I started reading the paper. I love this:

Groups frequently make collective decisions through majority rule. Legislators pass bills by majority; shareholders make most corporate decisions by (share-weighted) majority rule, as do directors; clubs, university faculties, and civic associations typically use majority rule as well. The reason that they do so is not entirely clear. (Emphasis mine).

>The reason that they do so is not entirely clear.

Not clear? It's almost too clear as to be tautological.

They do because they (a) think all members should have equal say, (b) most members in the group want X to be done.

Unless we're talking about submitting to force or listening to expertise, why would many people wanting to do something, let fewer people tell them what to do instead?

Heck, if it comes to fighting for what's to be done (the most effective but primitive form of getting a decision), the majority could beat up the minority and have its way anyway.

Since we're going off-topic about QV here: am I correct in my understanding that you can _buy_ votes there? Because the obvious problem (because I'm probably missing something) with that would be that money is not as costly for some as it is for others? In other words, it'd lead to tyranny of the wealthy?
(from a cursory read) You can buy votes, but they become increasingly costly. Also, the payments are redistributed out among the population, so if you spend 1B to buy 31.6K votes, that 1B gets handed to the population at large who, if larger in number, can more efficiently re-spend it voting you down.

What I imagine QV does is find the point at which participants who don't care so much would rather pocket the funds and walk away, rather than spend it on voting.

If the ballot measure is "Eat Vinnl", you might want to spend a lot to vote it down, but it might not take much to convince the others not to spend it back at you (and you all eat berries instead). If the ballot measure is "Vinnl eats everyone else", you won't be able to spend enough that everyone else can't just spend it back at you, more efficiently.

Edit: one important difference here is that unless the GP is actually going to pony up real money, something of value to those they inconvenience with their (imo silly) opinion, it doesn't make much sense.

So it's a method of exchanging influence for money for poor people, and money for influence for rich people? Still doesn't sound that great...

Anyway, I see it's been submitted separately :) https://news.ycombinator.com/item?id=15206291

Interesting idea, but what if I stead of buying them, we could spend our accumulated points to emphasize ideas.
I've never heard about quadratic voting before. This is very interesting!
In practice it's not so bad, because your program won't compile if you've missed returning something in any branch, or if you've returned the wrong thing.
I was thinking the same before actually using rust. Turns out it surprisingly awesome given how rust treats statements and expressions.

it just makes obvious that "result of something" and "control flow back to caller" are very different things.

Don't sweat it, your compiler will point it out and it's fairly ingrained to functional programmers that the last line of function is its return value anyway
Oh definitely, implicit returns are awesome. I just commented on the fact that a semicolon supresses them.
How do you feel about the ! operator?
I won't even get started. I see no reason why a modern language would use it in preference to "not" (like Python).
By your logic, you should also hate the "+" and "-" operators – they're just as easy to confuse as ";" and "", completely change the semantics, and the type checker won't even catch your mistake!
Welcome to Pascal.
How do you mean? In Pascal the semicolon after the last statement of a block is optional. So, for example, this:

  function square(const x : Integer) : Integer;
  begin
    result := x * x;
  end;
Is equivalent to:

  function square(const x : Integer) : Integer;
  begin
    result := x * x
  end;
If you mean in if-then-else constructions, then yes the semicolon must be at the end of the statement:

  if (condition) then
    dosomething
  else
    dosomethingelse;
In C you couldn't follow cases and labels with just a semicolon, it was an invalid statement. That's because a statement couldn't be just a semicolon. But yes, I agree.
Commas are unnecessary. There is zero benefit to having them. The code is actually easier to parse if you just remove them from the rules list entirely. No commas might look weird for a day or so, but you quickly get used to it.

(I'm ignoring the comma operator, which is a special case and not relevant to the main point.)

    foo(x, -y)
is not the same as

    foo(x -y)
(from your last sentence, I assume you were talking about JS in general, not just JSON)
Mm, fair point. Infix math rears its ugly head again. I've been living in Lisp a bit too long.

Never mind.

Actually, let's double down: Instead of writing 1+2, you should really be writing +(1 2). Don't you see how much easier that would make things? A single, uniform syntax everywhere! + is just a function that gets called like anything else! Your foo(x -y) example would become foo(x -(y)).

I'm mostly joking.

Haskell makes a reasonable job of combining infix operators with no commas between function arguments[1]. You sometimes end up with a few extra parentheses and $s instead, but things generally seem to work out.

Does use commas for lists and tuples, though. The latter kind-of make sense, it's the commas that identify the expression as a tuple. Not sure what the rationale for commas in lists is, though.

[1] Slightly complicated by currying (arguably, it's several successive function applications rather than one multi-arg application) but the end result is the same...

> Not sure what the rationale for commas in lists is, though.

Well, you need some separator, and spaces won't do since [x y] has x applied to y.

Good point.

Although could, in principle, make space-between-list-items higher precedence than function application. E.g.:

    [x (func y) z]
It's not infix math that's the problem, it's the ambiguity of "-". It can take two arguments in one context, or one in another (or just be a representation of a negative number without implying any function call at all.) If we just used a different symbol for subtraction, it would clear up a number of other parsing problems languages run into. Or you could just require negations to be done inside parentheses.
Or just require whitespace for binary - and no whitespace for unary.

  res = -x ==> negative x

  res = c - x ==> c minus x

  res = - x ==> syntax error
Not really suggesting this, but if we only allow the unary operator - to always be stuck next to the number, and the binary operator - to always be surrounded by whitespace, it is. So:

  foo(x -y) => foo(x, -y)

  foo(x - y) => foo(x - y)
I like OCaml's terse syntax. You can write something like that as just "foo x (-y)" (the brackets are only needed because of the use of minus but can usually be omitted).
They exist because of string concatenation. In some languages any consecutive strings are implicitly concatenated so that you can have a very long string split across many lines.
Some JavaScript style standards require no semicolons at the end of lines: https://standardjs.com/rules.html#semicolons
Yes, but he means no semicolon only on the last statement of a function, I believe.
I made the switch earlier this year and I don't think I would ever willingly go back. It just looks so much more parsable. Semicolons at the end of my statements just look ugly and savage now.
Javascript: automatically inserts semicolons for you at the end of a line, but doesn't allow commas at the end of a list.
JavaScript does allow commas at the end of a list! :)

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Refe...

Not if you want your code to work in certain versions of Internet Explorer :(
Nobody (1) cares for those versions of IE anymore.

(1) where nobody = limit(somebody) -> 0