Elixir in the Type System Quadrant
Programming languages are created with different kinds of type systems. When “Thinking Elixir”, it is helpful to know where Elixir sits in the Type System Quadrant and where that is relative to the language you are coming from. Beyond coming from OO, you may also be coming from a different area of the Type System Quadrant. It is helpful to understand the differences so you can better appreciate how Elixir may be different than what you’re familiar with.
Type System Quadrant
Let’s look at the Type System Quadrant with some languages placed in their respective areas. We’ll explain what it means next.

The horizontal axis deals with when type checking happens in the language. From left to right, it goes from static to dynamic.
- Static: type checking at compile time
- Dynamic: type checking at run time

The vertical axis is for type safety. From top to bottom, it goes from a weak type system to a strong type system. This describes how aggressive a language is with enforcing its type system.
- Weak: allows implicit conversions that lose precision
- Strong: requires explicit conversions where precision would be lost

If you would like to lookup other languages, you can find them loosely categorized here.
Which area of the quadrant is best?
Which is best? There is no “absolute good” here. They behave differently and this results in pros and cons when using a language for a specific purpose.
It is worth being aware of what you are accustomed to and where you are coming from when coming to Elixir.
Why is C considered weak?
C is a static language. The type checking happens at compile time. However, a major feature of the language is pointers. At run time, the program can be told to “interpret this blob of memory as a float”, but it could have been allocated and assigned ASCII character data. No run time checking is performed. The binary memory chunks will now be interpreted as a float. This behavior has caused no end of security vulnerabilities. This characteristic (and consequences) gave rise to the Rust language. The goal being to create a type safe C-like language.
A look at JavaScript
JavaScript is a weak dynamic language. This is an easy one to play with as you can open a browser console and try this out.
JavaScript is very permissive in converting types. It implicitly converts types, not always how you would expect either!
"1" + 2
//=> "12"
2 + "2"
//=> "22"
{} + []
//=> 0
[] + {}
//=> "[object Object]"
How does object + array = 0
? Then flipping it around, array + object = object
? Wacky.
The JavaScript runtime implicitly converts those types for you.
Elixir is strong dynamic
Elixir has a strong type system. Try these operations out in IEx. Elixir will not implicitly convert one type to another type where precision is lost.
"1" + 2
#=> ** (ArithmeticError) bad argument in arithmetic expression: "1" + 2
#=> :erlang.+("1", 2)
["a"] ++ ["b"]
#=> ["a", "b"]
{"b", 1} ++ ["a"]
#=> ** (ArgumentError) argument error
#=> :erlang.++({"b", 1}, ["a"])
{"b", 1} + ["a"]
#=> ** (ArithmeticError) bad argument in arithmetic expression: {"b", 1} + ["a"]
#=> :erlang.+({"b", 1}, ["a"])
Elixir has dynamic type checking. It happens at run time. This is evident in pattern matching.
{:ok, %{name: name}} = {:ok, %{name: "Howard", age: 25}}
name
#=> "Howard"
At run time, the BEAM checks the types. Here’s a hypothetical walk-through of the type checks:
- Is the left side of the match operator a 2 element tuple? Yes.
- Is the first element the
:ok
atom? Yes. - Is the second element a map? Yes.
- Does the map have an atom
:name
key? Yes. - Then bind the value from the right to the
name
variable on the left.
Notice that it goes beyond just checking the types. It is also comparing specific values as well. As these checks all happen at run time, it is dynamic.
Pattern matching is a feature I absolutely love in Elixir. I don’t believe you can have the expressive pattern matching you find in Elixir in a static language.
If you are coming from a static language and the lack of compile time type checking is uncomfortable, I understand. Try to keep in mind that this more dynamic type checking enables features you could not otherwise have. Try to keep an open mind.
I don’t believe you can have the expressive pattern matching you find in Elixir in a static language. The type checks must happen at run time.
Expanding Elixir towards static
Within the Type System Quadrant, each language can’t be easily reduced to a single point in a grid. A feature of the language or ecosystem might define a point. All together, a language is more of a point cloud. Additionally, the features you choose to use (or not use) in your project push it in a more static or dynamic direction.
For instance, Elixir has optional language features and ecosystem tools that extend its area towards the static side. These include:
- Structs have compile time checks applied to keys
- Behaviour implementations are checked at compile time
- Dialyzer is a “static code” analysis tool

Closing
Elixir is a strong dynamic language. Types are checked at run time (dynamic) and the enforcement of type conversions is strong.
If you are coming to Elixir from a static language, it may feel uncomfortable. You may miss the strict compile time type checking. Just keep in mind that some of the greatest strengths and features of Elixir are possible because of these type system choices.
Also, the use of specific language features and ecosystem tools can push your Elixir project further in the direction of static if that’s what you want.
6 Comments
Comments are closed on this static version of the site.
Comments are closed
This is a static version of the site. Comments are not available.