52

Behaviours define callbacks & protocols define methods without signatures. Modules implementing a protocol should give definition for all those methods. Same for modules using a behaviour. What is the semantic difference?

One difference I can think of is, a protocol can be implemented for a single type only once where as we can implement a behaviour for a module multiple times based on our requirements. I am clear with when to use what. Is there any other difference other than this?

Aravindh S
  • 1,185
  • 11
  • 19
  • 5
    Also, most importantly, behaviours start new processes where the callbacks are run while the protocol functions are run in the same process. This is the same comment as below, but it is often missed. Behaviours hide all the concurrency and message passing but they are still there and the client calls and callback calls are run in different processes. – rvirding Oct 09 '14 at 00:16
  • 4
    @rvirding I didn't know it and I don't remember seeing that in any book or tutorial. Is there any part of documentation or any other article about how behaviors start new processes implicitly? – Krzysztof Wende Apr 15 '16 at 10:01
  • 1
    @KrzysztofWende It is implicitly stated in http://erlang.org/doc/design_principles/des_princ.html . Basically the term `behaviour` is deeply connected to OTP applications and therefore to supervision trees which are all about maintaining processes. In Erlang, a behaviour abstracts away common patterns (client-server, state machine etc) so that you don't have to call `spawn*` and pattern match messages manually (which is error prone). Fred Hebert's online tutorial helped me a lot to understand this: http://learnyousomeerlang.com/what-is-otp#the-common-process-abstracted – toraritte Jun 16 '17 at 19:33
  • Related: [Why is Access a behaviour instead of a Protocol? When to use a Protocol instead of a Behaviour?](https://elixirforum.com/t/why-is-access-a-behaviour-instead-of-a-protocol-when-to-use-a-protocol-instead-of-a-behaviour/1020/6) – toraritte May 03 '22 at 09:35

3 Answers3

60

Protocol is type/data based polymorphism. When I call Enum.each(foo, ...), the concrete enumeration is determined from the type of foo.

Behaviour is a typeless plug-in mechanism. When I call GenServer.start(MyModule), I explicitly pass MyModule as a plug-in, and the generic code from GenServer will call into this module when needed.

sasajuric
  • 5,949
  • 1
  • 22
  • 18
  • 7
    Also, most importantly, behaviours start new processes where the callbacks are run while the protocol functions are run in the same process. – rvirding Oct 08 '14 at 13:39
  • 17
    @rvirding You're wrong. Behaviours just describe what functions must be present in your module. They don't do anything with processes. –  May 24 '15 at 23:15
  • 1
    @rightfold ok, then elixir has a different meaning of behaviour than in erlang, which could be confusing as they also use erlang behaviours. You could say that an erlang behaviour describes the interface functions but there is much much more as the base for it all are the properties of the processes to which the functions interface. That there are processes are what erlang behaviours are all about. – rvirding May 26 '15 at 11:12
  • 2
    @rvirding Behaviours in Erlang also just describe what functions must be present in your module. gen_event is an example of a behaviour which doesn't do anything with spawning processes. –  May 26 '15 at 11:15
  • @rightfold Sorry, a gen_event behaviour assumes that you have an event manager process which controls the gen_event behaviours. So while it doesn't start a process there are still processes at the base of it. All the other behaviours explicitly are based on processes, even the application behaviour usually, but not always, starts and manages processes, in this case a supervision tree. – rvirding May 26 '15 at 11:20
32

Answered by José Valim on the same topic ( from google thread, 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.

selvan
  • 1,298
  • 14
  • 9
  • To make things more complicated: dynamic dispatch is also possible with behaviors: https://elixir-lang.org/getting-started/typespecs-and-behaviours.html#dynamic-dispatch – denis.peplin Mar 13 '23 at 09:47
1

Behaviours are about modules, Protocols are about data.

Behaviour

Is a list of functions that a module has. It’s useful when you’re doing something and need to say “now I need you to do your bit”.

An example of a behaviour is Plug . When running middlewear on a webserver, it says “OK, now you do your bit to handle this request” for each plug that’s been set up.

You implement behaviours by defining the required functions in a module, and use them by passing that module name into something that expects to be able to call the functions in the behaviour.

Protocols

Is way of doing something with a type of data. It’s useful when you have some data and need to say “I want to be able to do X with this”.

An example of a protocol is String.Chars. It says “this is the way to convert data to a string”. This is used by to_string.

You implement protocols by declaring the implementation for a specific datatype, and use them by passing data into a function that expects to be able to use the protocol for that data.


Here’s an example of a behaviour:

defmodule AnimalBehaviour do
  @callback make_sound() :: atom()

  def hello(callback_module), do: callback_module.make_sound()
end

defmodule Dog do
  @behaviour AnimalBehaviour

  @impl AnimalBehaviour
  def make_sound, do: :woof
end

And a protocol:

defprotocol AnimalProtocol do
  def hello(data)
end

defmodule Cat do
  defstruct []

  defimpl AnimalProtocol do
    def hello(%Cat{}), do: :meow
  end
end

Usage:

iex(1)> AnimalBehaviour.hello(Dog)
:woof
iex(2)> AnimalProtocol.hello(%Cat{})
:meow
Adam Millerchip
  • 20,844
  • 5
  • 51
  • 74
  • This is the clearest explanation I've yet seen of the difference between them. Simple helpful examples, side-by-side. Thank you! – Bukov Aug 24 '23 at 10:02