Polymorphism: Elixir vs. Java
Tags: elixir, java, oop
Published: 2019-09-08 16:35:12

---
title: Polymorphism: Elixir vs. Java
published: true
description: An introduction to polymorphism in Elixir using protocols, and how they relate to class hierarchies in Java.
tags: elixir,java,oop
---

## Introduction

This post seeks to enhance the understanding of Elixir protocols by comparing them to class hierarchies in Java. Though the two languages are much different, they share a common, powerful feature: Polymorphism.

When you first start learning Elixir, the first thing people say you have to do is wrap your head around Functional Programming (FP). This might mean kicking some old habits that have formed from using traditionally object-oriented languages such as Java and Python, and thinking slightly differently when you're writing code and designing software.

Though every Elixir dev will tell you how great FP is and why they love it, a lesser-known feature to beginner Elixir devs is its support for polymorphism through Protocols. 

Polymorphism is a concept that is at the core of object-oriented programming, making it a feature that seems contrary to the core of the Elixir language. We'll see why it's useful, and compare them to familiar, foundational features in Java.

### Who is this article for?

This article introduces the use of protocols in Elixir to achieve polymorphic functionality. If you've never heard of Elixir before, be sure to check out [the Elixir website](https://elixir-lang.org/) for more details on this exciting young language.

If you're at all familiar with Object Oriented Programming (OOP) in Java, this post will help to draw similarities between Java class hierarchies and Elixir protocols.

## What is polymorphism?

Polymorphism is when our program dispatches to functions or methods based on the given data's type, or class.

For example, in Java we have the ability to define an interface, which only contains method _definitions_ that outline some functionality. We then provide the _implementations_ for an interface's method definitions when we define a class which `implements` that interface.

If a method's signature accepts an instance of an interface and calls a method on that instance, then at runtime, Java decides which method _definition_ to dispatch to based on the underlying _implementation_ class.

This allows us to decouple our code by not tying our implementation of one module, to the implementation of another module.

## Java class hierarchies

Let's use a simple example here. Americans measure their height in inches, whereas the rest of the world uses centimetres. I'm sure some parts of the world use other units of measurement, but for the sake of simplicity we will stick with that assumption.

Let's define an interface for measuring Americans, or someone from the rest of the world, in either centimetres or inches:

```java
public interface MeasuredPerson {
  double measureInInches();
  double measureInCentimetres();
}
```

Now we can implement this interface in a couple of classes which represent people from America and the rest of the world:

```java
public class American implements MeasuredPerson {
  private String name;
  private double heightInInches;

  public American(String name, double heightInInches) {
    this.name = name;
    this.heightInInches = heightInInches;
  }

  public double measureInInches() { return heightInInches; }
  public double measureInCentimetres() { return heightInInches * 2.54; }
}

public class RestOfTheWorld implements MeasuredPerson {
  private String name;
  private double heightInCentimetres;

  public American(String name, double heightInCentimetres) {
    this.name = name;
    this.heightInCentimetres = heightInCentimetres;
  }

  public double measureInInches() { return heightInCentimetres * 0.39; }
  public double measureInCentimetres() { return heightInCentimetres; }
}
```

Now that we have a couple of classes we can use, we can write a method that accepts a `MeasuredPerson` and prints out their height in inches.

```java
public class Main {
  public static void main(string[] args) {
    American american = new American("John", 72);
    RestOfTheWorld restOfTheWorld = new RestOfTheWorld("Jean", 178);

    printInches(american);
    printInches(restOfTheWorld);
  }

  private void printInches(MeasuredPerson measuredPerson) {
    System.out.println("The person's height is: " + measuredPerson.measureInInches());
  }
}
```

This code will print out the height in inches of both the American and the person from the rest of the world. The method `printInches` dispatches the call to `measureInInches` at runtime based on the underlying type of the given object!

## Elixir protocols

Protocols in Elixir allow for us to define similar relationships in our programs. First, we define a protocol, which is a set of function _definitions_ that outline some functionality. We then provide the _implementations_ for a protocol's function definitions when we define an implementation module for an Elixir type.

Elixir types include `List`, `Map`, and `Keyword`, but they can also include custom structs that we define. The following is an example of two Person structs, similar to our Java classes which we defined above:

```elixir
defmodule Person.American do
  defstruct [:name, :height_in_inches]
end

defmodule Person.RestOfTheWorld do
  defstruct [:name, :height_in_centimetres]
end
```

Americans measure their height in inches, and the rest of the world measures their height in centimetres. What if we want to measure a person, without worrying about whether they're from America, or somewhere else in the world?

### The `defprotocol` keyword

Similar to how we defined an interface in Java, we can define a protocol for measuring the height of a person. The protocol expects a unit of measurement which the user wants to measure a given person with.

```elixir
defprotocol Person.Height do
  @doc """
  Accepts the person to measure as the first argument, and accepts either
  `:inches` or `:centimetres` as the second argument to indicate 
  which unit of measurement to return.
  """
  def measure(person, unit_of_measurement)
end
```

Notice that we don't provide a `do` block for our `measure/2` function. The next section will show how we define an implementation for our fresh protocol.

### The `defimpl` keyword

The following modules use the `defimpl` keyword to provide an implementation for our custom person structs:

```elixir
defimpl Person.Height, for: Person.American do
  def measure(%Person.American{height_in_inches: height_in_inches}, :inches) do
    height_in_inches
  end

  def measure(%Person.American{height_in_inches: height_in_inches}, :centimetres) do
    height_in_inches * 2.54
  end
end

defimpl Person.Height, for: Person.RestOfTheWorld do
  def measure(%Person.RestOfTheWorld{height_in_centimetres: height_in_centimetres}, :inches) do
    height_in_centimetres * 0.39
  end

  def measure(%Person.RestOfTheWorld{height_in_centimetres: height_in_centimetres}, :centimetres) do
    height_in_centimetres
  end
end
```

`defimpl` takes a protocol module (defined using `defprotocol`), and the module mapping to the type that we want to provide an implementation for. The first argument to our `measure/2` functions defined in our implementation modules is therefore a struct of that type.

There is a key difference from our Java implementation: instead of implementing the behavior inside the `Person.American` and `Person.RestOfTheWorld` modules, we implement the behavior in a separate module. Since structs themselves can't have behavior (i.e. you can't call `american.measure(:centimetres)`), the struct must be passed to the function instead; see below for how we do this.

### Tying it all together

Now that we've defined a couple of implementation modules for our `Person.American` and `Person.RestOfTheWorld` structs, we can call the `Person.Height.measure/2` function to measure the given person in our preferred unit of measurement!

```elixir
%Person.American{name: "John", height_in_inches: 72}
|> Person.Height.measure(:centimetres)
|> IO.inspect()
# 182.88

%Person.RestOfTheWorld{name: "Jean", height_in_centimetres: 178}
|> Person.Height.measure(:inches)
|> IO.inspect()
# 70.0787
```

## The power of protocols

We've used a simple example here to show how we can create our own protocols. But what's the big deal?

Using protocols is powerful because we can define a single interface with well-defined behavior _once_, and then implement that behavior elsewhere based on our custom struct.

A big advantage which Elixir protocols have over Java class hierarchies is that we can supply implementations of our own protocols for structs from external libraries. In Java, you'd have to edit the source code of other libraries to implement your custom interface. But in Elixir, all we have to do is provide an implementation for a type (struct), regardless of what library/framework provides that type!

### A familiar example

In Elixir, we use the `Enum` module for traversing lists and maps. Anything we pass as the first argument to the functions in the `Enum` module must have an implementation for the `Enumerable` protocol! To read more about this [check out the `Enumerable` docs](https://hexdocs.pm/elixir/Enumerable.html).

## Resources

I'd like to provide some additional resources for learning about Elixir Protocols, so you can continue to explore this OOP concept in a traditionally functional language.

- [Elixir protocols](https://elixir-lang.org/getting-started/protocols.html): the introduction to Elixir protocols on the official Elixir lang website.
- [Elixir `Protocol` docs](https://hexdocs.pm/elixir/Protocol.html): if you're looking for a deeper dive into how protocols work, this is a good place to start. The Elixir docs are written so well that usually I don't look elsewhere.
- [_Programming Elixir >= 1.6_](https://www.amazon.com/Programming-Elixir-1-6-Functional-Concurrent-ebook/dp/B07DP8Y2GJ/ref=sr_1_1?crid=2E3ZEQ9KFZE4F&keywords=elixir+language&qid=1567959174&s=books&sprefix=elixir+lang%2Caps%2C128&sr=1-1): this book is one of the best tools in my arsenal. I couldn't recommend a better book for beginners and experts alike. I use it as reference all of the time.

I wrote this post for those of us that are familiar with the power of OOP, and to showcase an awesome feature in Elixir that feels right at home for anyone familiar with OOP in Java. Thanks for reading!