Types Are Moving to the Right
medium.com
medium.com
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])
{ ... }It neatly finds the first use, etc. It also does a word search so `it` does not highlight when searching for `i`.
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.
(Additionally, in C++, where `struct/enum/class foo` can be shortened to `foo`, the variable interpretation is always preferred.)
Personally I like how it is in Pascal - caret sign for pointer types, and postfix unary operator caret for dereference, like^.this which doesn't need an additional syntax ( like->this is just (* like).this in C). But now, caret is usually also the XOR operator.
The deeper problem is that Pascal cannot use caret as XOR binary operator since postfix operators cannot easily be distinguished from binary operators with only 1 token lookahead - it would require more lookahead or type resolution to determine that it's not a binary operator. While in C the deref operator is a prefix which can be distinguished from binary operators without lookahead.
http://blog.reverberate.org/2013/08/parsing-c-is-literally-u...
But you can very easily get the AST. Obviously it may happen that you have to wait until the hell is frozen to get a result, but generally it doesn’t happen in code that you see day to day..
So, for anyone else feeling similarly unsatisfied, here's another example that might be more satisfying in that respect:
y = f(g<T, U>(x));
In conjunction with the fact that templates are already Turing-complete, we can see that detecting whether this is a call to a templated g or two comparisons is a Turing-complete question.Also notice a similar parsing issue arises in C# as well, but I don't think generics there can be used to perform Turing-complete computation... though not sure.
[1]: https://stackoverflow.com/questions/29707622/swift-compiler-...
[2]: https://en.wikipedia.org/wiki/C%2B%2B11#Right_angle_bracket
[2] hasn't been an issue since 2011.
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.
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.
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.
You could say there are two parsing challenges: knowing that you are inside a declaration (as opposed to an expression, statement, etc.), and parsing the parts of the declaration once you know you are in it.
If you have a (required) keyword like "var", then the first challenge is solved in any case. (And that is the challenge that was actually mentioned in the above comment.)
But yeah, the second challenge exists, and since variable names are exactly one token long (in any language I know of), it's easier for them to come first. Sometimes. It depends on the language, though: if the language has a separator terminal symbol ("var int : x"), then I don't see how the order matters. Some languages do and some don't.
There are also a variety of issues with parsing it that way, most of which go away entirely if the name is first.
"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."
Apparently the Spiral Rule is not entirely correct.
int (*arr)[2]
that means "if you dereference arr, the expression represents an array of 2 elements".
So, for example, if you want a pointer to a function that returns a pointer to an array of two elements, how do you denote it? You'd go step-by-step based on how you would use it: int a[2]
int (*f)[2]
int (*(*fp)())[2]
Once you understand this, the typing syntax should make sense, and you see that any description like spirals or whatever misses the big picture.The grammar seems to prevent recursive typedefs, but I've never seen it spelled out that you can't have them. This has caused me trouble on occasion.
I can't even seem to solve this with gcc's non-standard __typeof__ extension. I was halfway expecting to crash or hang the compiler.
Why? This seems completely baseless.
> How can the language committee leave this flaw unfixed?
Same way many languages like C++, C#, Java, VB etc. left this "flaw" unfixed.
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.
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.
And somehow I thought ML was from early 80s - thanks for correction!
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`.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.
a b = a(c); // one way
auto b = a(c); // preferred way var int a
function int f()
Clear and easy to parse. 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.Type inference should be able to deal with the simple cases anyway.
let x ∈ ℝ
I would still consider the link above editing text... but I posted it anyway for what it is worth.
The idea seems to be to confine the text to its AST in a form like fashion.
That is essentially the way it is done in Scala (2004), F# (2005), *ActionScript 3 (2006)*,
Go (2009), Rust (2010), Kotlin (2011), TypeScript (2012), and Swift (2014) programming languages.also Pascal is mentioned at rank 204
based on that yes ActionScript do make the cut in term of popularity, or you can also remove TypeScript and Pascal from the article
also hypocrisy much?
in one of your other comments
You are welcome. D did not actually made it to the list
based on the language-popularity-selection criteria I’ve used,
but I thought it is prominent enough to be included and
overrode my criteria for it.
so that part "prominent enough" is completely subjective right?int i,j,k;
I think that's clearer with type first.
Seems fine to me either way
let i,j,k : int;
is pretty clear. Although I'm not convinced that declaring multiple variables like this should be allowed at all. Imo it's clearer to declare them seperately when they're used.For example:
for (let i in 0..n) {
for (let j in 0..i) {
// do stuff
}
}I suspect it's also why the colon is often used when the order is reversed, even when it's not strictly necessary to disambiguate parsing - you want something that reads "i is integer", and colon already kinda sorta has that meaning.
function foo(string bar): string {
return “y u do this”;
}[1]: https://en.m.wikipedia.org/wiki/Hindley–Milner_type_system
Though my co-workers obsessed with writing ‘good’ code refuse to use them. They also refuse to write comments because their code is so good ‘it documents itself’.
(I've been programming for 20 years, and mostly in statically typed languages.)
Edit: In a big complicated system, knowing the type like ‘user’ or ‘account’ doesn’t tell you much. There’s always more information and context. A small type definition is only the tip of the iceberg.
I've recently moved from writing javascript in vim, to writing typescript in vim, to writing typescript using VSCode. Being able to hover over a variable and see a variable's type makes it easier to read complex code. This is useful irrespective of whether or not you're using type inference, since you don't need to hunt down the variable's declaration to figure out its type. And if you're reading back the compiler's type information through your IDE while programming, you may as well take advantage of type inference.
This is why new languages are trending towards inferred types. Much easier to write and read (you get your padding taken care of for free as well when you use var)
Also ‘what’ is happening is the actual flow and logic of the code. ‘Why’ it is happening is in your comments.
How so? Padding is inserted when the layout (order of fields) is bad. There are no language that reorder structures, are there?
> Also ‘what’ is happening is the actual flow and logic of the code. ‘Why’ it is happening is in your comments.
That's certainly one way to put it. But I was relating to the "show me your tables..." quote's kind of feeling. So, to be more clear, maybe more to the "what is possible" vs "what have the authors currently thought of / implemented".
Admittedly, I don't use any sort of IDE that does fancy mouseover type checking and only occasionally poke at C++ but there's one lib I wrote[0] where I had to remove a bunch of autos so I could figure out exactly what was going on (and, ironically, make the code "self documenting").
--edit--
[0] OK, translated from java to C++ so I also had to figure out the logic behind it to do any sorts of modifications.
For local variables, it's probably not necessary, unless the type is especially complicated.
Though, I wouldn't call putting type annotations on all variables as a bad thing.
As an aside, Intellij has this great future with Scala where it displays the inferred type of all functions and variables automatically.
Certainly, VB wasn’t way ahead of its time.
That said it's probably a "both" situation: ML and Pascal are both clear founders of type descriptions and their both deciding on relatively the same type syntax within just a few years of each other likely a convergent evolution indicator.
· That what's best for natural language is also the best for programming languages. This is still a debated topic, but my personal feeling is that the two are different enough in both mechanism and purpose that what's good for one isn't necessarily good for the other. (For an illustrative example, look at how sigils worked in Perl 5, as they were explicitly designed to work like English demonstrative words like "that" or "these", and many programmers when first exposured to Perl feel like they were 'illogical'. They weren't, but in this case, mirroring a natural-language convention tended to obscure, rather than clarify, what was going on in the language!)
· That phrase ordering in English is necessarily 'natural'. Lots of naturally-occurring spoken languages feature word order that differs tremendously from English!
· That the English-language phrase you're describing as 'unnatural' is in fact unnatural: it's actually a common convention, especially when you're introducing a new fact to a conversation! "Tiger Woods, a famous golfer, adopted a cat."
Not really. And the Tiger Woods example is a whole phrase with a verb and an noun (describing what he did to whom), so not representative of a type declaration (which is just just describing what thing something of a specific name is).
So, it's more like:
Tiger Woods: a golfer.
vs: A golfer: Tiger Woods.
Which of course is an argument for types on the right -- the first reads much better. Tiger Woods: a golfer.
Jon Rahm: a golfer.
Bryson DeChambeau: a golfer.
vs A golfer: Tiger Woods.
A golfer: Jon Rahm.
A golfer: Bryson DeChambeau.
This makes it easier to group up information when glancing at the code.It leaves so many questions that will have to be solved through "best practices" and everyone ends up doing it slightly differently.
"Famous but controversial golfer Tiger Woods adopted a cat" might be awkward, but it's hard for me to buy an argument that it's either unnatural or even particularly hard to understand.