JKWA

JKWA

Author of Advanced Functional Programming with Elixir

Polymorphism in Elixir

I didn’t have room for this in my book, Advanced Functional Programming with Elixir , but I still thought it was worth sharing on my blog.

Most Liked

JKWA

JKWA

Author of Advanced Functional Programming with Elixir

I admit, I’m having a bit of trouble giving helpful advice on this one.

Your goal, as I understand it, is to reduce Ecto boilerplate, and you’re also, in a sense, extending Ecto’s macro system. Within your project, I don’t see a problem, except that it introduces a bit of indirection that you’ll need to make sure your colleagues understand.

From the perspective of protocols, what hung me up is that I was looking for the polymorphism problem you were solving, but in the end, I think you’re using the protocol more as a contract.

Is this generalizable? I see some tight coupling to your domain, so my answer would be probably not, but I might be misunderstanding.

Is there another way to solve it? Sure. But you’re approaching it from a Ruby context, and I don’t have enough Ruby experience to say whether there’s a better alternative through that lens.

christhekeele

christhekeele

I would say protocols are for datatype polymorphism, often to get similar behaviour from different input data. Behaviours are for functional polymorphism, often to get different implementation details from similar contracts. Both can solve similar problems but have their own idiomatic wood grains to cut with or against.

Both involve functions accepting dynamic input that changes how the program executes: just as a function that accepts dynamic data may invoke a protocol to get different results, a function that accepts a dynamic module may invoke a behaviour to get different results.

Generally, unless we are doing supervision stuff/adapter work, our applications want to act similarly on different data more than we want them to act differently on similar interfaces, which is more of a library concern. Additionally, the type system and dialyzer work well enough with dynamic data types but dynamic modules is currently more of a black box for editor experiences. So I’d say the average application has more reason to be concerned with protocols than behaviours in practice, despite both mechanisms being open for extension in some sense to application developers.

But I would disagree with the claim that behaviours are not a mechanism for polymorphism; just a mechanism with different and less-common aims. I think my first paragraph there is a half-remembered direct quote from José, I’ll see if I can find the source. Some keynote maybe?

JKWA

JKWA

Author of Advanced Functional Programming with Elixir

We’re probably just interpreting the term “polymorphism” a bit differently. I’m using the Strachey definition, where the runtime selects behavior based on the type of the input. Under that (admittedly narrow) lens, behaviours don’t quite fit, since the caller selects the module explicitly. But in the end, it’s probably just semantics.

adamu

adamu

I didn’t have room for this in my book

This topic is explored in more depth in my book

These two statements contradict each other, perhaps the footer on the blog is generic and doesn’t apply in this case?

I think behaviours also deserve a mention along with protocols. My experience is that it’s quite rare to write a protocol, compared to a behaviour.

For example, Eq could also be implemented using a behaviour:

defmodule Eq do
  @callback eq?(a :: struct, b :: map) :: boolean

  def eq?(%type{} = a, b) do
    type.eq?(a, b)
  end
end

defmodule Cat do
  defstruct [:name, :microchip]

  @behaviour Eq

  def eq?(%Cat{microchip: a}, %{microchip: b}), do: a == b
end
iex(1)> Eq.eq?(%Cat{microchip: "foo"}, %{microchip: "foo"})
true
iex(2)> Eq.eq?(%Cat{microchip: "foo"}, %{microchip: "bar"})
false
JKWA

JKWA

Author of Advanced Functional Programming with Elixir

Yes, we’re both solving the same problem: defining a default comparison for a type.

Here’s how I might implement the homogeneous sort:


def sort(list, ord \\ Funx.Ord) when is_list(list) do

Enum.sort(list, Ord.Utils.comparator(ord))

end

This lets the caller sort a list using the domain’s default ordering or pass in a different Ord when needed. It supports cases where the same type needs multiple comparison strategies. It also allows Ord composition, making it easy to build more complex ordering logic.

Where Next?

Popular in Blog Posts Top

elbrujohalcon
We all know about the most renowned Erlang/Elixir centers worldwide, like Sweden, Brazil, California, and London. But the community, even...
New
brainlid
Phoenix 1.7.0 brings a lot of new things when we run mix phx.gen my_app. These new and cool ways of doing things aren’t automatically bro...
New
rrrene
In this series, we take a look at the different ways to organize, structure and execute a good “flow” in our Elixir programs. The latest...
New
AstonJ
Update: How to use the blogs section You can post in one of the Official Blog Posts threads (like this one), or, via Devtalk and a new t...
New
wmnnd
Here’s the story how one of the world’s first production deployments of LiveView came to be - and how trying to improve it almost caused ...
New
AstonJ
Just finished doing a clean install of macOS (which I highly recommend btw!) and have updated my macOS Ruby & Elixir/Erlang dev env s...
New
ConnorRigby
Just finished a post for Embedded Elixir and I thought it should be cross-posted here: I’ll update this thread as posts get published. ...
New
gaggle
This post explores different ways to do test automation in Elixir, focusing on how to handle dependency injection — covering patterns, li...
New
lawik
One of the Erlang ecosystem’s spiciest nerd snipes are hot code updates. Because it can do it. In ways that almost no other runtime can.
New
mssantosdev
Our take on how to build a frontend style guide with Phoenix Components, Atomic Design and plain CSS, with focus on reusability and code ...
New

Other popular topics Top

yurko
Here are few pieces of (common) Linux knowledge that we use for reasonably small one server apps. We use Ubuntu but this should work for ...
New
vonH
In asking this question I am more interested about the expressiveness of the language itself and less concerned about the availability of...
New
lessless
I believe there are people here who are dealing with CSV files import on the daily basis, and since Excel is a really popular tool there ...
New
bsollish-terakeet
Credo is smart enough to check for (something like) this: assert length(the_list) == 0 with this response: Checking if an enum is empt...
New
fireproofsocks
I’m working on defining a simple Ecto schema for a table (in PostGres), but I don’t see where I can define a column as NOT NULL. Conside...
New
lastday4you
I wanted to check elixir version in phoenix because i found that my elixir is 1.5 but when i use Enum.chunk_by it said the function is un...
New
myronmarston
The Elixir Typespec docs show the following syntax for keyword lists in typespecs: # ... | [key: type] # keyword lis...
New
vonH
When I run the Plug and I recompile I wind up having to use Ctrl C to quit iex and start again. Witht the help of rlwrap I can use the cu...
New
Qqwy
Original source of discussion: This topic on the Pragmatic Programmers' Functional Web Development with Elixir, OTP, and Phoenix forum. ...
New
lanycrost
Hi everyone! I need implement if…else if…else condition from my elixir code, and anymore of this control flow structures not work proper...
New

We're in Beta

About us Mission Statement