Elixir difference between Behaviour and Protocol
Tags: elixir, programming, codequality, beginners
Published: 2021-05-16 02:19:29


While building and analyzing code in Elixir, you have probably come across the keywords `behavior`, `impl`, `defprotocol` and `defimpl`, while searching you may have found a short description that can confuse you about when to use them and their differences, this post brings some examples of when to use them and how to implement them in a real project.

## Behaviour

Behaviour is a way to implement a user interface for a module to share its api in a public way, this module must have functions that need to be used in the ones that implement this behaviour.

Let's get to the examples: 

Suppose we need to define an interface for our parsers, for this we will create a module:

```elixir
defmodule Parser do
	@callback parse_ids(map) :: map
  
	@spec to_downcase(map) :: map
	def to_downcase(attrs) do
		attrs
		|> Map.new(fn {i, v} -> {i, String.downcase(v)} end)
	end
end
```

In this case we create a Parser module with an `@callback` and a function that takes a map and transforms its values into downcase.

`@callback` is the function that should be implemented by all modules that use the Parser behavior.

Let's look at an example of its use.

```elixir
defmodule Person do
	@behaviour Parser
	
	@impl Parser
	def parse_ids(attrs) do
		attrs
		|> Map.put_new("name", get_in(attrs, ["user", "name"]))
		|> Map.put_new("email", get_in(attrs, ["user", "email"]))
		|> Parser.to_downcase()
	end
end
```

Here we use the `Parser` behaviour to define the `parse_ids` function, otherwise the compiler will return a warning.

Although not required, the `@impl` ensures that we are implementing the correct callbacks.

Behaviours are a great way to ensure behavior when we need to export a module's public api, as they have types defined in their construction.

## Protocols

In a very direct way, protocols are a way to implement polymorphism in Elixir, we use them when we need a module to have a different behavior depending on the type of the value.

Here are some examples:

```elixir
defprotocol Document do
	@spec id(any) :: any
	def id(item)

	@spec encode(any) :: map
	def encode(item)
end
```
Here we define our protocol, so everyone who uses it can create the function with a return that is within the specifications and is for a unique type.

Let's use the protocol.
```elixir
defmodule Person do
	embedded_schema do
		field :name, :string
		field :social_security_number, :string
	end

	defimpl Document, for: __MODULE__ do
		def id(struct), do: struct.social_security_number	  

		def encode(struct) do
			struct
			|> Person.to_map()
		end
	end

	def to_map(struct) do
		%{
		name: struct.name,
		social_security_number: struct.social_security_number
		}
	end
end
```
Here we define the use of the Document protocol with defimpl, see that we are defining the type, through `for: __MODULE__`, that way when Document.encode is called, it already knows it will return a map, the definition of which map it will return is in the module specification, very separate responsibilities.


## But what is the difference between Behaviour and Protocol?

A good example is given by the creator of the language, José Valim, in this post: [https://groups.google.com/forum/#!msg/elixir-lang-talk/S0NlOoc4ThM/J2aD2hKrtuoJ](https://groups.google.com/forum/#!msg/elixir-lang-talk/S0NlOoc4ThM/J2aD2hKrtuoJ)

"""A protocol is indeed a behaviour + dispatching logic.

However I think you are missing the point of behaviours. Behaviours are extremely useful. For example, a GenServer defines a behaviour. A behaviour is a way to say: give me a module as argument and I will invoke the following callbacks on it, which these argument and so on. A more complex example for behaviours besides a GenServer are the Ecto adapters.

However, this does not work if you have a data structure and you want to dispatch based on the data structure. Hence protocols."""

I appreciate everyone who has read through here, if you guys have anything to add, please leave a comment.