DEV Community

Meghan (she/her)
Meghan (she/her)

Posted on

What ever happened to putting the object type on the left?

I apologize in advance for this seeming like a rant, but there's one bit of syntax that I've been seeing a lot lately, and I can't seem to figure out why.

It's the : and -> syntax for type definitions. Consider the two snippets in TypeScript and Java.

function add(a: Number, b: Number): Number { ... }
Enter fullscreen mode Exit fullscreen mode
public static int add(int a, int b) { ... }
Enter fullscreen mode Exit fullscreen mode

I've noticed that the two above behaviors are very distinct between newer and "old" languages.

Java, C, C++, Objective-C, and C# define functions and variables the Java way.

Kotlin, Swift, Rust, TypeScript, and Go all put types on the Right.

Any ideas on why this shift has occurred? Which way do you prefer?

Top comments (20)

lexlohr profile image
Alex Lohr

If I'm not mistaken, all of the languages that put types on the right seem to be able to infer types, so you can omit them in some cases; omitting them if they would stand before the variable name could easily lead to confusion, thus they opted for a syntax without any ambiguity.

lucretia profile image
Luke A. Guest

Not all.

lexlohr profile image
Alex Lohr

Please enlighten me about the exception(s)?

Thread Thread
lucretia profile image
Luke A. Guest

...types on the right seem to be able to infer types...

Ada is static and strong, it does not have type inference, most other languages are weak ;)

tterb profile image
Brett Stevenson

This was my first assumption too.
Yet, as someone who has recently begun exploring Typescript, it still throws me off every time.

bgadrian profile image
Adrian B.G. • Edited

I think it has more sense like "here is X, a type Y variable" but anywayI think it all started with Fortran (type on the left) but there were exceptions like Basic

ASTRING$ = "Hello World"

There are also "neutral" ones, in which you do not have to specify the type (including Go), Lips or everything is a word (BCPL).

To answer your question I think this stackoverflow answer got it right

All of the languages you mentioned support type inference, which means the type is an optional part of the declaration in those languages because they're smart enough to fill it in themselves when you provide an initialization expression that has an easily-determined type.

That matters because putting the optional parts of an expression farther to the right reduces parsing ambiguity, and increases consistency between the expressions that do use that part and the ones that don't.

And New vs Old is relative to our age :))

Having the type after the identifier is not a new phenomenon, it goes back to at least Pascal (designed 1968–1969, released in 1970), but actually was used in mathematical type theory, which starts ~1902. It was also used in ML (1973), CLU (1974), Hope (1970s), Modula-2 (1977–1985), Ada (1980), Miranda (1985), Caml (1985), Eiffel (1985), Oberon (1986), Modula-3 (1986–1988), and Haskell (1989).

bertilmuth profile image
Bertil Muth

Very good. Just as a side note, the type in UML goes to the right as well, I guess for similar reasons.

jeffreymfarley profile image
Jeff Farley

I came in here to add this

jballanc profile image
Joshua Ballanco

To begin with a caveat, the following is based on my own intuition and experience (and no time to do any research to back it up). That said...

This is part of a more general trend of programming environments and languages moving from accommodating the machine to accommodating the programmer. Ask yourself, in a function's declaration, what's more important: the parameter names or their types? Consider:

Fraction make_fraction(int denominator, int numerator)

How would you call that function to make the fraction ½? If you said make_fraction(1, 2), then you were probably too distracted by the type annotations to notice that I flipped the argument order from what you might have naively expected. Placing the annotations on the right makes it easier to focus on what's important:

func makeFraction(denominator:Int, numerator:Int) -> Fraction

Furthermore, if the same function is overridden to accept more different types of arguments, but keeps the same basic semantics, it would be preferable if those functions looked similar at first glance:

Fraction make_fraction(int numerator, int denominator)
Fraction make_fraction(float numerator, int denominator)
Fraction make_fraction(int numerator, float denominator)
func makeFraction(numerator:Int, denominator:Int) -> Fraction
func makeFraction(numerator:Float, denominator:Int) -> Fraction
func makeFraction(numerator:Int, denominator:Float) -> Fraction

So why, then, were type declarations ever put on the left? Well, if you look back far enough you'll find that C function definitions used to be written like so:

make_fraction(numerator, denominator)
int numerator;
int denominator;
{ /*...*/ }

Well, now, that doesn't seem so developer hostile, aside from having to repeat the names of the parameters. It does give us a clue into how type-on-left probably came about, though. Remember that C was originally nothing more than "portable assembly". Well, in assembly when you want to define a function there are a few things you need to do, including setting aside registers and/or stack space for the arguments that will be passed to the function. The most important bit of information for this space allocation is not the names of the parameters, but rather the type.

So, if you're writing a compiler for a language (and especially if you're on a very memory constrained system such that you can't just go placing things in memory willy-nilly for later retrieval), you'd want the first thing your compiler encounters in the language to be the most important. In this case, that means putting the type first so your compiler can do its layout calculations.

As for why more modern, sophisticated compilers (e.g. C++ and Java) kept this declaration form, inertia is a helluva drug, and doubly so in programming language design.

evanoman profile image
Evan Oman

I am not sure if this is a trend or not or why it is happening, but I do think I prefer the return type on the right.

When I write code in Scala, the method definitions look like:

def add(a: Int, b: Int): Int = a + b

So when you read from left to right you get the intuition that I am taking these parameters and turning them into the return type.

That being said I don't think the placement matters too much to me as long as I am not switching between the two styles too frequently.

mortoray profile image
edA‑qa mort‑ora‑y

It has to do with divorcing the type from the variable or symbol to which it is attached. This is primarily on the return type, and not the argument types.

Consider the C-like example you gave:

int add(int a, int b) { ... }

The problem with this syntax is we can't describe the type of this function without a name. You can't remove the add bit and still have it make sense. In C function pointer syntax is weird because of this:

typedef int (*add_type)(int,int)

This defines add_type as the desired type, but we still don't have an actual way to say what that type without the name.

The left-return type declaration limits us from speaking of function types. This problem has been amplified by lamba functions: those without names.

The right-return syntax allows you to talke about function types without needing a name. Consider, using Leaf syntax

defn add = (a : int, b : int) -> ( : int ) { ... }

The type of add is (a : int, b : int) -> ( : int ). It doesn't require any new syntax, and can be specified without a name.

The other problem with left-aligned types is due to a lack of separator for a type. It causes problems for parsing and user-defined types and variables.

If you have public protected int add there is a convention that the right-most value is the symbol name.

But what about arguments? add( int x, int y ) What if you want the type without the named arguments: (int, int). With extended type information though it becomes difficult to determine what is a type symbol and what is the argument symbol:

(large int, int const, int large)

The right-aligned types differ in introducing an explicit type separator, the : in your TypeScript example. I use the same character in Leaf. This explicit type separator greatly simplifies parsing and eliminates all sorts of ambiguities, such as those in C++, when dealing with type names.

This change in syntax is due to an inrease in the use of the functional paradigm. We need a clean and consistent way to talk about types separate from their variables.

val_baca profile image
Valentin Baca

As others have noted, I've seen it as an indicator of languages with type inference. So you can omit the type if you don't need it.

The syntax itself is taken from Pascal, so not "new" in the strict sense but "new" in the "not seen in popular languages until recently".

Here's how I read each one in my head and I find the right-hand more like natural English:

String hello = ...;
// "a string named hello with value..."

public String greet(int times) {...}
// "A public method that returns String, is named greet and takes an int parameter named times"
var hello : String
// "the variable hello is a String"

func greet(times: Integer) -> String {...}
// "a function named greet that takes times, an Integer, and returns a String"
// Though it can sometimes be slightly more difficult hard to find the return type in a complex function definition, especially in lambdas (functions that return functions ....)
maruru profile image
Marco Alka

I think, that's just a language quirk and has been there all the way back. Just think about Pascal:

program hello;
  word: String;
  word := 'Hello World!';
sammyisa profile image
Sammy Israwi

I used to think that it was normal and preferred to have types definitions on the left of the variable or function name, I think it may be because of the languages that I learned to program with at first (C, C++, Java).

But as I started to work with TypeScript it became easier to read type definitions on the right. For variables, I read them as "X is of type Y", for functions "Function A returns type B" but that may have more to do with conditioning after TypeScript.

As of the reason, when I first saw type definition on the right was in TypeScript. Back then, I thought it may be a limitation of the language since it was a wrapper around JavaScript, but I have nothing but a gut feeling to back that up.

ben profile image
Ben Halpern

This makes sense

_midofo_ profile image
Mick Fong

I reckon it is just CS degree types creating new lex parsers to try and distinguish themselves and their new language. There is absolutely no other reason why you would change a traditional approach to programming. In essence there is no benefits of doing this apart from bucking from the norm. Doesn't matter if it is strongly typed or not (poor argument).

thedeemon_lj profile image
Dmitry Popov

Right! Putting the type on the right is traditional (as mentioned in another comment, it was done so in Pascal (designed 1968–1969, released in 1970), ML (1973), CLU (1974), Modula-2 (1977–1985), Ada (1980), Miranda (1985), Caml (1985), Eiffel (1985)...) There is no good reason for putting types on the left, except for CS degree types creating new lex parsers. ;)

lucretia profile image
Luke A. Guest

Java, C, C++, Objective-C, and C# define functions and variables the Java way.

No, Java, C++, Objective-C/C++ and C# do it the C way as they're all derived from C. Algol "influenced" C.

Having them on the right is just another way. Doesn't really matter.

michaelhonan profile image

Delphi has always used the "sMyString: String" right-side syntax. But it's an interesting point you bring up.

I feel that it's also more readable because we usually read from left to right, therefore, I get to see the variable name first instead of a data type, which is way more helpful IMHO.

migu profile image
Michael Gutbier • Edited

There's a blog article about Go's declaration syntax that explains the reasoning behind it:

The article gives a good example what a mess C code can become if pointers are used. In C, the declaration for the example function looks like this, parameter names omitted. The function returns a function pointer and also expects a function pointer as the first parameter.

int (*(*fp)(int (*)(int, int), int))(int, int)

In Go, the same is much easier to read:

f func(func(int,int) int, int) func(int, int) int

That being said, Go also makes exceptions when needed. Read the article for more information.