Implementing a Protocol

The defimpl macro lets you give Elixir the implementation of a protocol for one or more types. The code that follows is the implementation of the Inspect protocol for PIDs and references.

 defimpl​ Inspect, ​for:​ PID ​do
 def​ inspect(pid, _opts) ​do
 "​​#​​PID"​ <> IO.iodata_to_binary(pid_to_list(pid))
 end
 end
 
 defimpl​ Inspect, ​for:​ Reference ​do
 def​ inspect(ref, _opts) ​do
 '#Ref'​ ++ rest = ​:erlang​.ref_to_list(ref)
 "​​#​​Reference"​ <> IO.iodata_to_binary(rest)
 end
 end

Finally, the Kernel module implements inspect, which calls Inspect.inspect with its parameter. This means that when you call inspect(self), it becomes a call to Inspect.inspect(self). And because self is a PID, this in turn resolves to something like "#PID<0.25.0>".

Behind the scenes, defimpl puts the implementation for each protocol-and-type combination into a separate module. The protocol for Inspect for the PID type is in the module Inspect.PID. And because you can recompile modules, you can change the implementation of functions accessed via protocols.

 iex>​ inspect self
 "#PID<0.25.0>"
 iex>​ ​defimpl​ Inspect, ​for:​ PID ​do
 ...>​ ​def​ inspect(pid, _) ​do
 ...>​ ​"​​#​​Process: "​ <> IO.iodata_to_binary(​:erlang​.pid_to_list(pid)) <> ​"​​!!"
 ...>​ ​end
 ...>​ ​end
 iex:3: redefining module Inspect.PID
 {:module, Inspect.PID, <<70,79....
 iex>​ inspect self
 "#Process: <0.25.0>!!"