Qqwy

Qqwy

TypeCheck Core Team

Why is Access a behaviour instead of a Protocol? When to use a Protocol instead of a Behaviour?

I am having some trouble understanding the difference between Behaviours and Protocols. This mostly is caused because the built-in Access functionality in Elixir is a Behaviour instead of a Protocol.

The odd thing is, that it seems that everything that a protocol does can be made using a Behaviour. Can someone shine light on what is going on?

Protocol:

  1. Someone defines a protocol using defprotocol, which has a name and one or multiple functions that need to be implemented (with the given arity). The first argument passed to these function implementations will always be the ‘thing’ that the Protocol is implemented on.
  2. In another module, someone defines an implementation for this protocol using defimpl.

Dispatching from the function call to the protocol implementations is automatic.

It is not possible to call the implementations directly on the final module, as they are secretly defined in another place that you cannot access yourself. Therefore, it also does not make sense to document the implemented functions.

Behaviours

  1. Someone defines a behaviour by just making a module and having multiple @callback statements in there, that take a spec; It seems that behaviours not only try to validate the arity of the callback implementations, but also e.g. the formats of the input/output data.
  2. To implement a behaviour, someone has to add use ModuleWithTheCallbackStatements to some module, and then simply define the callbacks of that behaviour in that module as functions.

Dispatching happens manually inside the module defining the behaviour (using e.g. for structs the module name inside the struct field); The callbacks may (using defoverridable) or may not be defined as functions in the module defining the Behaviour. This means that it is a lot easier to treat certain function inputs as ‘special’.

The implemented functions of a behaviour are simply defined and fully accessible and callable as normal functions. As such, they ought to be documented.


It seems to me that Behaviours are more flexible than Protocols. When is it a good idea to implement a Protocol rather than a Behaviour? Are there differences I’ve missed?

Most Liked

bbense

bbense

The @callback attribute doesn’t do as much as you might think, at best it just generates some compile time errors. You can actually use behaviours just fine w/o ever using the module that defines them.

A behaviour is just a promise that your module implements a function with given inputs.

A Protocol is a means for defining functions that potentially work for many different data types.

The way I think about it is that behaviours are for when you want to have a function for a single
set of args that potentially does different things. Protocols are for when you want to have a function that does the same thing for many different kinds of args.

You are correct that if you do enough hard work, you can probably implement a Protocol as a set of behaviours, but using Protocol does all the heavy lifting for you. This does not come for free though.

From my own work with Elixir I have some examples:

  • Write a function to find the documentation for a function in a Module in either Erlang or Elixir.

I wrote this as a behaviour. The arguments were exactly the same, but the underlying implementation was quite different. The idea was to have a list of Modules that implemented this behaviour and to call the function in each of the modules to find the documentation.

  • Create transformations of data structures without apriori knowledge of the data structure.

This is where Protocols shine because you can call the function defined in the Protocol recursively. Inspect is the example code to look at to really understand the power of Protocols.

Qqwy

Qqwy

TypeCheck Core Team

I asked @josevalim about it in the IRC group today directly. His reply:

(10:19:50 PM) josevalim: you only consolidate inside a project and after compilation
(10:20:03 PM) josevalim: this means all the access during compilation and in scripts still won’t be fast enough, which is a no-go

which is a very clear answer. Thank you very much, José! :heart_eyes:

michalmuskala

michalmuskala

Access was a protocol initially, but I think it was changed to a behaviour because of performance issues around protocol dispatch. It was simply too slow for something used as frequently as access.

Qqwy

Qqwy

TypeCheck Core Team

Thank you for your replies, @michalmuskala and @bbense. :slight_smile:

One other difference (maybe the important difference) that came to mind now, is that a Protocol doesn’t care where it is defined.

  • Person A could create a Protocol,
  • Person B could create a data structure doing something.
  • Finally, Person C could define an implementation of Protocol A for Struct B.

With behaviours, you’re limited to defining the Behaviour inside the module that defines the structure.
Maybe this is exactly why Access is defined as a Behaviour, by the way, to prevent people from adding it to external data structures at a later time.

@michalmuskala: Now that Elixir always consolidates protocols during standard compilation, these performance issues have gone away, right?

OvermindDL1

OvermindDL1

I am especially curious. I would love to have Access implemented to call methods on Erlang style tuple modules:

iex> defmodule MyModule do
iex>   def a_method(i, tup), do: {i, tup}
iex>   def fetch(tup, key), do: {key, tup}
iex> end

iex> thing = {MyModule, :some, :more, :data}

iex> # This next line works right now because the underlaying Erlang system does it
iex> thing.a_method(42)
{42, {MyModule, :some, :more, :data}}

iex> # This next line I would love to have work like this (for my Array module for :array compatability without needing a struct), but right now Access throws a fit
iex> thing[42]
{42, {MyModule, :some, :more, :data}}

iex> # Although, much as I would love the above for consistency with Erlang, I would actually 'expect' this, but it does not happen either, Access throws a fit regardless
iex> thing[2]
:more

Where Next?

Popular in Questions Top

itssasanka
Hi all, Trying to get some more clarity over utc_datetime and naive_datetime for Ecto: https://hexdocs.pm/ecto/Ecto.Schema.html#module-...
New
_russellb
I want to try my hand at web scraping. What tools/libraries do I need to use. I’m hoping to turn this into something professional so don’...
New
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
rms.mrcs
Hi, I need to transform a list of numbers into a map where the keys are the indexes and the values are the original values of the list....
New
alice
Hey, Just curious what are the main benefits of Elixir compared to Clojure? When is Elixir more useful than Clojure and vice versa? Th...
New
johnnyicon
Hi all, I've just started learning Elixir and Phoenix Framework, so please pardon my n00bness at this stage. I'm trying to use Postg...
New
baxterw3b
Hi guys, i’m new in the Elixir world, and i have to say, that i love it! i’m having some problem to understand anonymous functions with ...
New
chrisalley
ExUnit now has describe blocks which is a welcome addition coming from RSpec. In the docs, it states that nested hierarchies of describe ...
New
jay1
Why is it that the mnesia database isn’t the most preferred database for use in Elixir/Phoenix?
New

Other popular topics Top

JDanielMartinez
Hi! May someone helps me, please! I have two apps into an umbrella project: the first one is Database, which manages queries, and the se...
New
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
Tee
can someone please explain to me how Enum.reduce works with maps
New
_russellb
I want to try my hand at web scraping. What tools/libraries do I need to use. I’m hoping to turn this into something professional so don’...
New
nsuchy
Hi. I’ve noticed that Windows Powershell has it’s own IEX command and you cannot access Elixir’s IEX due to the conflict. This isn’t a cr...
New
aadeshere1
I have a another noob question about loop. Since elixir is immutable, while loop is not directly possible. total = 10 while total != 0 ...
New
fayddelight
I tried installing elixir 1.11.2 erlang 23.3.4 via asdf in my zsh shell. Enabled the versions locally and globally. When I list them ...
New
msaraiva
Surface is an experimental library built on top of Phoenix LiveView and its new LiveComponent API that aims to provide a more declarative...
564 42633 214
New
AstonJ
by Lance Halvorsen Elixir and Phoenix are generating tremendous excitement as an unbeatable platform for building modern web application...
460 27162 124
New
magnetic
Hey :wave:t3: Elixir community, I’ve been learning Elixir, and working on some side projects. My editor of choice is VSCode, and althoug...
New

We're in Beta

About us Mission Statement