Struggling

Benefits of a Statically typed Language

March 15, 2018

In an attempt to improve my knowledge of functional programming, I decided to jump into the highly praised, Haskell Programming: from first principles book.

Coming from a mainly Elixir/Ruby background, both of which are dynamically typed, Haskell’s static type system was quite a different way of coding for me.

Why would you want a statically typed language?

To not beat around the bush, this quote from the above mentioned books sums it up perfectly:

A type system defines the associations between different parts of a program and checks that all the parts fit together in a logically consistent, provably correct way. (Haskell Programming: From first principles - Chapter 5)

Think about a large scale enterprise project you’ve worked on using a dynamically typed language. Even with compiled languages, keeping track of the types of data as it flows through your application becomes near impossible and from time to time you are going to run into these runtime errors:

** (ArgumentError) argument error
    :erlang.length("Not a list")

Now 9 times out of ten, the above would have tests wrapped around it that would prevent it from being shipped to users - however mistakes are made and sometimes, an edge case or two will slip through the cracks.

Having a compile time, statically type checked language helps you prevent these errors from being shipped to prod. They aren’t soley to be relied on and obiviously tests are still going to be needed but they are able to provide you with a level of confidence that dynamically typed languages cannot.

Another benefit of compile time type checking is that it allows your compiler to create optimized builds as they are able to improve the execution of your code by analyzing the types and making certain decisions based on these types.

In contrast, dynamic languages have to infer the types at runtime meaning that the compiler can have no way of optimizing the builds at compile time.

It’s not all upsides though. Writing and maintaining types does have a lot of initial cost, which will result in a slower intial development time, compared to a dynamically typed language. However as stated in the book:

This upfront cost comes with a later payoff: code that is safer and, down the line, easier to maintain. Working with a good type system can eliminate those tests that only check that you’re passing the right sort of data around, and since tests are more code that you have to write (correctly) and maintain, it will eventually save you time and effort. (Haskell Programming: From first principles - Chapter 5)

This is a clear cut example of how a statically typed language can improve your development process and make you are more efficient and effective developer.

For example, in Ruby let’s say you have this function:

def upcase_first(array)
  return "" unless array.kind_of?(Array) && array[0].kind_of?(String)
  array[0].upcase
end

and the following tests (truncated)

#... tests which test the correct results and logic

it "will return an empty string if passed a non-array" do
  expect(upcase_first({dog: "barks"}).to eq("")
end

it "will return an empty string if the first element is not a string" do
  expect(upcase_first([0, 1, 2, 3]).to eq("")
end

Here you can see we are having to write explict code to a check that the array argument passed in is infact an array and that the kind of the third element is a string. In order to ensure our logic is correct, we are then going to need to write a corresponding test.

Where as in haskell, this is all we need:

import Data.Text as T

upperFirst :: [Text] -> Text
upperFirst [] = T.empty
upperFirst (x:_) = T.toUpper x

He we are defining a function upperFirst which takes a list of Type Text (from Data.Text) and returns a peice of data of type Text.

We then have two function definitions that are invoked via pattern matching. If you are unfamiliar with pattern matching, think of it like case/switch statements on steroids (pattern matching in Elixir is especially powerful and pretty much makes up for the lack of Types in terms of readability and guarentees).

Pattern matching reads from top -> bottom so in the above case:

  1. .upperFirst [] = T.empty is saying that if passed an empty list [] then return the empty version of the datatype .Text -> ""
  2. .upperFirst (x:_) = T.toUpper x is saying that given a list of one or more elements, extract the first element x and call .T.toUpper on it. The _ syntax pretty much says ignore the rest of the elements in the list - we don’t care about them

From the above, it’s easy to see that we don’t need to test the fact that it takes in a list or the fact that the first element needs to be a Text type - this is all enforced by the compilier.

E.g. If I tried to do this:

import Data.Text as T

upperFirst :: [Text] -> Text
upperFirst [] = T.empty
upperFirst (x:_) = T.toUpper x

main = upperFirst([0, 1, 2])

I would get this compile time error message:

-- • No instance for (Num Text) arising from the literal ‘0’
--  • In the expression: 0
--     In the first argument of ‘upperFirst’, namely ‘([0, 1, 2])’
--     In the expression: upperFirst ([0, 1, 2])

Which whilst a little hard to understand is saying “Wrong type passed to this function pal”

Another great benefit of statically typed languages that I feel really speeds up the development process is IDE/editor support. Having a statically typed language makes it incredbily easy for an IDE or editor to find definitions, provide relevant documentation and even refactor your code.

To see an example of this, have a look at this demonstration of the neovim plugin I use intero-neovim.

Lastly, the self-documenting nature of statically typed language I feel further speeds up the development process and onboarding time of new employees, especially if your data types (match your domain objects)[https://fsharpforfunandprofit.com/ddd/]

By simply looking at the function type signatures, you can very easily guage what the input of a function is going to be along with it’s output.

For example, in our above upperFirst you can tell that it’s going to take a List of Text elements and return a single Text element. Whereas with the ruby implementation you would need to analyze the actual implementation of the method to determine what it’s input and output - even then you might not be able to tell.

Whilst I haven’t covered all the benefits/negatives of Statically typed languages, I’ve hope I’ve given you some insight into some of the benefits they bring to the table in terms of developer producitivity.


Harrison Lucas

👋 Hi I'm Harrison Lucas, a software engineer from Australia, currently living and Working in the UK. Currently into Elixir and functional programming.
Twitter | LinkedIn