Hacker News new | ask | show | jobs
by runarberg 1650 days ago
C/Java style of annotating the return type of a function first, and then annotating the argument types before the name feels really old school at this point. Python, TypeScript, Go, Rust, etc. all opted for annotating after the name.

Since this is so prevalent in newer languages, despite a pretty strong tradition in the other direction, I wonder if there is a pretty good reason for this which language design experts are keenly aware of when they design new languages.

2 comments

> C/Java style of annotating the return type of a function first, and then annotating the argument types before the name feels really old school at this point. Python, TypeScript, Go, Rust, etc. all opted for annotating after the name.

>

> Since this is so prevalent in newer languages, despite a pretty strong tradition in the other direction, I wonder if there is a pretty good reason for this which language design experts are keenly aware of when they design new languages.

My $0.02:

Maybe consistency between named functions and anonymous functions?

If you put the return type of a function before the name then it reads ambiguously when the name is left out (as in anonymous functions):

// Named function

int funcName (params) { ... }

// No-name function

int (params) { ... }

Which leads to the language needing alternative syntax or extra keywords when declaring anonymous functions:

// Something like this maybe?

int lambda (params) { ... }

If you put the return type after the function information but before the body then it's always consistent:

// Named function

funcName (params) : int { ... }

// No-name function

(params) : int { ... }

And, of course, to retain consistency you then make sure that all variables are declared the same way (type following variable name):

// Var declaration

myvar : int;

The disambiguation comes into its own when creating functions inline:

// Prefixed return-type looks odd

callFooWithFunc (int (argslist) { ... });

// Prefixed return-type requires extra keywords to not look odd

callFooWithFunc (int lambda (argslist) { ... });

// Suffixed return-type looks normal

callFooWithFunc ((argslist) : int { ... });

I think it's much easier to parse unambiguously, especially when you have operators on values that also appear in types (e.g. `*` and &`).