I think the readability argument is pretty subjective. Personally I find declarations in for instance Rust a lot easier to quickly scan than C++. For one thing the names of functions are all at the same offset, which to me is actually the most important thing. I can see at a glance whether it's a function or variable being declared. I've never found the fact that the return type is at the end to be a problem - why would it be?
fn first_function(...) -> i32 { ... }
fn second_function(...) -> Vec<i32> { ... }
const X: i32 = 0;
vs
int first_function(...) { ... }
vector<int> second_function(...) { ... }
int X = 0;
If I'd had to guess, the syntax might be inspired by math notation such as x ∈ ℝ
C type/variable declarations are an expression that transforms the type/variable into a primitive type, which is then annotated on the left of the expression. Why not on the right?
IMHO the existence of "cdecl" is evidence that it might not have been the right choice.
It probably helped that I just parse to an AST that’s generated so experiments like this are trivial.
Also, some people say that it makes for easier (less ambiguous?) parsing.
But the -> part is a bit weird. It "shouldn't" be
def factorial(x: int) -> int:
but it "should" be def factorial(x: int): int =
Maybe it's that way because of Python?The arrow syntax meshes better with the idea that there's a difference between the types of values, and the meaning of a function signature. "foo: t" declares a name "foo" as having a type "t", but functions are abstractions — what is the "type" of factorial()? It's not "int".
The type of factorial() could be written:
(int) -> int
or just: int -> int
If you look at the ML family as well as Haskell, that's exactly how they express it.They do that for other reasons, too; those languages follow lambda calculus, which only has single-argument functions, so all functions are simply mappings between a single value to another value.
Why do you care about the return type before the name?
The structure 'term: definition' is pretty common in many natural languages and has a lot of advantages: all terms are aligned, you can sort them, you can grep the term you're looking for, etc.
I even think it should be extended to functions, too. That 'def' ruins the harmony. Instead of
def factorial(int) -> int
it should be factorial: fn(int) -> int
You trade a keyword ('def') for another ('fn') but open the way to lambdas (just the part after the colon).I agree that the -> serves no purpose, it could be made optional. Alternatively one could require the -> and dismiss the 'fn' keyword, like this:
factorial: (int) -> int
It's slightly harder for the parser who has to wait until the -> to understand what it's reading, but it's doable.About putting the return type after the parameters, that absolutely reflects how I reason about functions: I will want to first examine its input, and then its output. Even the man pages for common functions and commands explain the parameters first, and leave the return value last.
1) Easy to parse (less ambiguous) 2) We like Python