C and C-style languages like C++, Java, and C# tend to have method types written like this:

Other typed languages and programming papers use a notation more like this:

I found it took a bit of getting used to, but I now much prefer to read and write this style. I think it is worth becoming familiar with, as it is used in quite a few languages1 and in all the programming papers I’ve seen. So here’s a quick guide on how to read this style of type annotation.

## Structure

From the methodName example above, we can see the structure has changed from “return type - name - arguments” to “name - arguments - return type”. So the main change is moving the return type from the beginning to the end.

A : separates the name from the type signature. : can be read as “has type”. Haskell unfortunately uses :: for this, instead of the : character which seems to be used pretty much everywhere else.

A -> arrow separates function input from function output. So a -> b reads as “I take values of type a and produce values of type b”.

Arguments are shown as a tuple of one or more types. In some languages (like ML, OCaml, and F#) tuple types are shown denoted by types separated by * characters, so the signature would look like methodName : argType0 * argType1 -> returnType.

## Generics

There are a few different ways of representing generic parameters. Let’s take a function that, given a single element of some type, returns a singleton list of that type.

In Haskell, any type starting with a lowercase character is a type variable rather than a concrete type. In F# type parameters begin with a quote character '. Not requiring an additional step to list generic parameters is handy.

## Higher order functions

Where this notation starts to show some advantages is with higher order functions. For example, say we want a generic map function:

These functions take a function that translates Ts to As, and a list of Ts, to produce a list of As. The parentheses around the (t -> a) in the Haskell-style signature show that this is a single argument (that happens to itself be another function). This is a bit cleaner than the equivalent Func<T, A> in the C# version, particularly when the explicit type parameter declarations are taken into account. The difference becomes more noticeable as we increase the number of functions and type parameters:

## Curried functions

In the map example above a “more exact, less idiomatic translation” was shown:

map1 takes a function (t -> a) and returns a function List t -> List a. It would also be correct to write it as map1 :: (t -> a) -> (List t -> List a). In constrast, map2 takes a single argument that happens to be a tuple of ((t -> a), List t). If we are supplying both arguments at once there is not much difference, but the map1 version also lets us supply just the (t -> a) argument to create a new function.

Being able to supply less than the full contingent of arguments to a function, and get back a function that takes the remainder of the arguments, is called partial application.

The map1 form of signature, where a function is built out of functions that each take a single argument, is called a curried function (map2 is “uncurried”). We get partial application, the ability to provide one argument at a time, for free with curried functions.

Curried function signatures in C# get unpleasant fairly quickly:

## Unit values

Some methods take no input and return a value (either a constant, or due to some side-effect). The “no input” value is normally represented by empty parenthesis (), and is called “unit” (because there is only a single legal value of this type, ()).

Similarly for things that take an argument but produce no direct output value (i.e. performs a side-effect)2. Again, this is represented by unit:

This starts to look a bit funny when methods take other calls with no input and no direct output:

It does give some immediate clues as to where side-effects are in a type signature thought.

## Types inside implementations

We’ve looked at different forms of type signatures, but this style also tends to work its way into method definitions, again using the form name : type.

Haskell tends to split the type signature from definition. F# specifies the arguments as argName : argType, and then gives the type of the resulting value (in this case List<'T>. Generic type parameters are indicated with a ' prefix. Swift uses a similar style, but an arrow is used for the return type. Swift needs explicit declaration of generic type parameters.

In both the Haskell and F# cases the type information can actually be omitted – the type system will infer the correct type signature.

## Conclusion

This has been a quick tour through the things that first tripped me up when reading type signatures from non-C-style languages.

The main habit I needed to break was expecting to see a type then a name. Instead, names are first, then their type shown. So method types change like this:

Similarly arguments go from ArgType name to name : ArgType.

Hope this helps!

1. Such as Haskell, F#, Swift, Scala, OCaml, ML, Idris, Elm, PureScript, and TypeScript.

2. Note that void in C-style languages is different to the terms “unit” and “Void” in non-C-style languages. In C-style languages void means “has no return value”, where a return type of () means “returns the value ()”. In contrast, the type Void is one with no legal instance. We can never return a value of type Void, so my understanding is a function a -> Void can never return.