Readit News logoReadit News
andolanra · 6 years ago
There are several technical advantages to the types-to-the-right style. For one, it's often easier to write a parser for: having an explicit let or val keyword makes it immediately obvious to a parser without lookahead that the statement in question is a declaration, and not an expression. This is less of a problem in languages like Java, where the grammar of types is simpler, but in C and C++, if you begin a line with

    foo * bar
then it's not yet clear to the parser (and won't be until after more tokens are observed) whether this is a variable declaration for bar or a statement multiplying foo and bar. This isn't a problem for name-then-type syntaxes.

On a related note, it's also often advantageous for functions to indicate their return type last, as well, especially when the return value of the thing might be a function of an earlier argument. There are plenty of examples of this in functional or dependently-typed languages, but even C++ (which historically has listed the return type of a function first) has added an alternate (slightly clunky) syntax for function types where you can specify the return type after the arguments for this reason:

    template<typename Container, typename Index>
    auto
    foo(Container& c, Index i)
      -> decltype(c[i])
    { ... }

WalterBright · 6 years ago
The parsing problems have long been solved. The `foo * bar;` issue is solved in D by the observation that the * operator returns a value, and no use of the value is made here, so it must be a declaration. No symbol table lookup is necessary. (D explicitly does not rely on a symbol table to parse.)

It is possible that the * operator is overloaded and the overload has side effects that are relied upon here, but D discourages overloading arithmetic operators for non-arithmetic uses, and considers the side-effect only case as particularly wretched and so has no problem not supporting it.

Lookahead issues are also trivial to solve, so shouldn't be a factor in modern language design.

jstimpfle · 6 years ago
That's why C is usually parsed with a symbol table, which is the way it was meant to be parsed. When parsing "foo * bar;", then it should be clear after parsing "foo" whether the statement is a variable declaration or an expression syntax. (It's a variable declaration if "foo" is registered as a type in the symbol table).

The advantage of this style is that types align nicely to the left and no additional keywords (like let) are required. The disadvantage is that parsing becomes highly contextual. We can't start with parsing somewhere in the middle of a file.

millstone · 6 years ago
Mostly it just seems silly to use * for both pointers and arithmetic. We're not hurting for sigils even in ASCII.
thedufer · 6 years ago
That seems insufficient - what if foo is both a type and a variable?
adrianmonk · 6 years ago
> having an explicit let or val keyword

Surely you can have such a keyword in either case. Both of these are certainly possible:

    var int x = 1

    var x int = 1
I don't see that as an advantage of types to the right so much as an advantage of choosing to define the grammar to include a terminal symbol that makes parsing easier.

It is true that newer languages seem to be more likely have a keyword like 'var', but that has less to do with types left or right and more to do with type inference. If types are optional, then there needs to be something that remains which identifies it as a variable declaration.

pjmlp · 6 years ago
That basic example falls down with more complex examples, unless only basic typenames are allowed in variable declarations.
colanderman · 6 years ago
> it's not yet clear to the parser

Not at all. C/C++ grammar is explicitly contextual. `foo * bar` is a declaration if-and-only-if `foo` was previously declared as a type. Otherwise it is an expression. The subsequent tokens have zero bearing on this.

Nor is this a good argument for types-to-the-right. If C named pointer types like `⋆foo` instead of `foo⋆` [stars used to avoid formatting glitches], then putting the type to the right as `bar * foo` would be equally ambiguous as your example.

Guvante · 6 years ago
I don't know of any language that has type on right without a non-ambiguous marker like `let`. That is what avoid ambiguity.
naniwaduni · 6 years ago
plausible misread: "it's not at all clear"
paulddraper · 6 years ago
And on case of inference, It also makes the grammar simpler if the thing that is optional follows the thing that is required.
lisper · 6 years ago
Type-to-the-right also makes it a lot easier to find the initial definition of a variable or a function.
WalterBright · 6 years ago
I recently updated my editor (https://github.com/DigitalMars/med) to highlight in yellow all search matches on the screen. It works so damn well I am disgusted with myself for not thinking of this decades ago.

It neatly finds the first use, etc. It also does a word search so `it` does not highlight when searching for `i`.

stefan_ · 6 years ago
Okay, but I have no problem with the parsers of any C or C++ compiler. I don't care to make it easier for the machine.
mehrdadn · 6 years ago
Easier for the machine means easier for people to write tooling for the language. Imagine if you could ast.parse() C++ code the same way you could for Python code.
saagarjha · 6 years ago
Oh, but you do, because the parser will invariably leak into the way you write code. If your code takes twelve times longer to compile [1], or you need to add a space because the parser thinks that ">>" at the end of a nested template is ambiguous [2], or any number of "hacks" we have internalized as idioms because they make the parser happy, it's not really a problem with your machine anymore.

[1]: https://stackoverflow.com/questions/29707622/swift-compiler-...

[2]: https://en.wikipedia.org/wiki/C%2B%2B11#Right_angle_bracket

diroussel · 6 years ago
In type to the right Language’s it’s also easier to grep for assignment or function declaration. And for me, easier to read.

Dead Comment

int_19h · 6 years ago
I don't think it has much to do with type inference. It's more that type systems became more complicated, and so did type names. And with a long composite type name, the name of the variable gets pushed too far out and obscured. It worked great in Algol, and still works pretty well in C (although that is partly because it splits the declarator to keep array and function syntax to the right of the variable name), but in C++ with templates it's already hard to read.

There are also a variety of issues with parsing it that way, most of which go away entirely if the name is first.

waterhouse · 6 years ago
Rob Pike wrote a rationale for Go's type syntax here: https://blog.golang.org/gos-declaration-syntax He seems to agree with you.

"One merit of this left-to-right style is how well it works as the types become more complex." ... "Overall, though, we believe Go's type syntax is easier to understand than C's, especially when things get complicated." ... "Go's declarations read left to right. It's been pointed out that C's read in a spiral! See The "Clockwise/Spiral Rule" by David Anderson."

saagarjha · 6 years ago
I am still of the opinion that not putting some sort of visual break between a variable name and the type is somewhat unpleasant. Almost every other language uses a colon to make these easier to visually parse.
theoh · 6 years ago
Previously: https://news.ycombinator.com/item?id=12775735

Apparently the Spiral Rule is not entirely correct.

jstimpfle · 6 years ago
> especially when things get complicated

I think that's a strange rationale and it doesn't fit the philosophy promoted by golang otherwise, which is that we should optimize for the 80% or 90% cases.

0815test · 6 years ago
Well, the parsing issues go away mostly because the actual pattern is "keyword on the left, type on the right". `let a: My_Type` is a whole lot easier to parse than `My_Type a`, and this goes even further when instead of My_Type you have something more complex, due to the C-family syntax trick of having "declaration mimic use".
int_19h · 6 years ago
`a: My_Type` or even `a My_Type` (assuming that juxtaposition is not used for something else) is easier to parse, since you only need one token of lookahead here to know that it's a declaration, and that the rest is a type constructor - and this is true regardless of how complicated that type constructor is.

For an extreme example on the other end of the spectrum, in C++, for something like this:

    a<>::b<c>d;
It's impossible to even say whether it's an expression statement or a variable declaration, because it depends on what exactly b is. In fact, it might be impossible to determine even if you have the definition of b in pure C++. Consider:

   template<size_t N = sizeof(void*)> struct a;

   template<> struct a<4> {
       enum { b };
   };

   template<> struct a<8> {
       template<int> struct b {};
   };

   enum { c, d };

   int main() {
       a<>::b<c>d;
   }
Now whether it's a declaration or an expression depends on pointer size used by your compiler.

Needless to say, this is all very fun for tools that have to make sense of code, like IDEs. And I think that's another vector for a different syntax - as "smart" tooling (code completion etc) became more common, PL designers have to accommodate that, as well. C++ helped by first becoming popular, and then teaching several painful lessons in that department.

mjw1007 · 6 years ago
Looks to me like the world had mostly settled on types-on-the-right already in the 1970s, except C was an anomaly and languages which imitated its syntax in other ways often imitated that too.
int_19h · 6 years ago
Aside from the Pascal family, who else had types-on-the-right back then?
mjw1007 · 6 years ago
PL/I, ML, Ada, ...
krona · 6 years ago
Python type hints (since 3.5) also follow the same pattern. see: https://www.python.org/dev/peps/pep-0483/
andrepd · 6 years ago
Same syntax as Ocaml/ML family.
majewsky · 6 years ago
Types are moving to the right because having them on the left makes the grammar undecidable. Languages like C or C++ can only be parsed when semantic information is passed back into the parser.

For example, consider the following C++ statement:

  a b(c);
This is a declaration of `b`. If `c` is a variable, this declares `b` of type `a` and calls its constructor with the argument `c`. If `c` is a type, it declares `b` to be a function that takes a `c` and returns an `a`.

mehrdadn · 6 years ago
> Types are moving to the right because having them on the left makes the grammar undecidable.

This is just an artifact of how they designed the syntax in some languages like C++ or C#. You can easily put types on the left in a way that makes this false.

UIZealot · 6 years ago
If you're designing a new programming language anyway, why not go with:

    var int a
    function int f()
Clear and easy to parse.

WalterBright · 6 years ago
`a b(c);` is always a declaration of function `b` in D. To construct `b` of type `a`, it is written instead:

    a b = a(c);     // one way
    auto b = a(c);  // preferred way

SamReidHughes · 6 years ago
That's only if you have weird function type syntax like C does.
colmvp · 6 years ago
Is this an example of the 'Most Vexing Parse' problem?
Aearnus · 6 years ago
I disagree with the fact that types are moving over to the right side for readability's sake or anything like that. Modern theorem proving languages explicitly specify that type declarations are simply equivalent to set inclusion, which makes the type specification operator (usually :) equivalent to set inclusion (∈). This influence was what rubbed off onto modern MLs, and by extension, inspired many of the ML inspired/type safe languages that have come out recently.
seanwilson · 6 years ago
Type annotations on the right reads way more naturally to me e.g. "customerNameToIdMap is hash map of strings to UUIDs" over "there's a hash map of strings to UUIDs called customerNameToIdMap".
Nimitz14 · 6 years ago
I disagree. Compare

    int num_entries = ..
    float div = ..
    float result = num_entries // div
vs

    num_entries: int = ..
    div: float = ..
    result: float = num_entries // div

And that's an extremely simple example. I tried out Nim but dropped it because while writing some code with list of lists of different types etc it became completely impossible to parse it quickly.

seanwilson · 6 years ago
I don't see much difference here. It's probably not a big deal when the type annotation is short but if it's something longer reading the short variable name first helps give some context first. When scanning code I think I read the variable name first as well.

Type inference should be able to deal with the simple cases anyway.

lgas · 6 years ago
Your examples are a good argument for types on the right, in my opinion... for the same reason the person you are replying to pointed out.
dom96 · 6 years ago
What does types being on the right have anything to do with parsing types representing lists of lists?
faissaloo · 6 years ago
Yeah I experienced this with Crystal and really disliked it.
dragonwriter · 6 years ago
Types on the right reads better on those examples (read ":" as "is a(n)" and "=" as "which is").
jey · 6 years ago
I thought this is from ML and related functional languages? (i.e. this is just the way they've always done it)
int_19h · 6 years ago
It's from Pascal; at least I can't think of any earlier language that had it. It's very noticeable when you compare it to ALGOL W, which Wirth did just before Pascal.
erk__ · 6 years ago
ML came out around the same time, though I don't know why they chose to have the type on the right there.