Type-Annotated Functions

The Shiny Black-boxes of Programming

Simon Janes
The Accepted Forest
4 min readAug 3, 2017

--

Light in the Darkness

Using Types and Human Augmentation in Software Development

Human augmentation? Absolutely! Our minds can get tired or overwhelmed by complexity unless we have systems to guide us away from dangers. In programming, a strong type system is used to prevent the accidental use of unrelated values. One place this shines is at the edge of functions where values are input and output. When your “augmentation” (here being the Elm compiler or any other compiler) warns you of a problem, you should pay attention.

Elm is a delightfully small language that distills programming behaviors into a warehouse of little black boxes that only misbehave when you allow them to. But how do these little black boxes know when they would or would not misbehave?

Often when you start prototyping a function’s behavior you first name the “thing you want.” You may not know how it will do it but you think that you know what it would be called. At this point you have a black box with a label and nothing else.

Let’s contrive two functions that output some “personal” information. Your name and your birthplace. In Elm, these have to be written in a certain way so the compiler can distinguish between different parts of your program. So we will transform “your name” to what Elm would want to see: yourName. “Your birthplace” of course becomes yourBirthplace. Some functions don’t need inputs so they just do whatever they need to do. When you call/invoke/use a function, it makes an output.

A good function is predictable or “pure” without side-effects. If you turned on your blender for example to make your morning power-shake, you would probably not imagine it should also add more micellar casein to your Amazon shopping cart and click’s “SHIP” because it guessed that you needed more, all you really wanted was your shake… blended. The output is the shake in that case.

A Dance of Values and Types —

Values are of course the stuff of computing, when someone asks “What is your name?” You give them a “value” of "Robert" or "Josephine" — and everyone is happy, because it looks like a “name” or a “string”. Now there is a bit of a distinction here, when you give a “name” you are not giving a “number,” because most people will think that is wrong to say your name is 384394558. In Elm, we can inform the compiler — which is ever watchful — that a name should look like a String with a “type alias.” Let’s make a couple of type aliases, one for names and another for “birthplaces.” The following two lines are declarations of “type aliases:”

type alias Name = String
type alias Birthplace = String

Now, to use these we should write a couple of functions to return values, one for yourName, and another yourBirthplace. With these functions we will want to inform the compiler — which can on its own “infer” the types of things most of the time — what we really intend to do with the functions:

yourName : Name
yourName = "Robert"
yourBirthplace : Birthplace
yourBirthplace = "Mirkwood Forest"

In Elm, the first lines before each function with the “:” are called “type annotations” — which are optional in the language, but a good practice once you have sketched out your program. These are the guard-rails to prevent the misuse of functions — and in some organizations, are required as documentation.

With these two functions, we can happily get yourName and yourBirthplace whenever we want. But, there is a silent danger lurking within these aliases: we aren’t getting the maximum type protection from the Elm language that we could possibly get. What if we were confused and wrote:

yourBirthplace : Birthplace
yourBirthplace = yourName

What would the Elm compiler do?

It would let it fly without warning and crash into the forest just outside of the airport incinerating dozens of woodland creatures and making a few options traders extremely happy at their (mis)fortune. For the two type aliases Name and Birthplace both resolve to the same “type” of String.

Let’s add some more strength to the types by adding type constructors, starting over we make full types instead of type aliases:

type Name = Name String
type Birthplace = Birthplace String

Our functions now have to be updated to construct and return the types, because they are no longer “aliases” of the type String.

yourName : Name
yourName = Name "Robert"
yourBirthplace : Birthplace
yourBirthplace = Birthplace "Mirkwood Forest"

Now what would happen if you tried to corrupt the meaning of yourBirthplace with the result of a Name type?

yourBirthplace : Birthplace
yourBirthplace = yourName

Elm will give you a very helpful error message:

-- TYPE MISMATCH -----------------------------------------------  The definition of `yourBirthplace` does not match its type annotation.  > yourBirthplace : Birthplace
> yourBirthplace = yourName
The type annotation for `yourBirthplace` says it is a: Birthplace But the definition (shown above) is a: Name

Crisis averted, the woodland-creature barbecue never happens, as this plane will not be allowed access to the tarmac and the options trader’s day progresses as ordinary and normal. The black boxes know who is who, and where is where.

--

--