Protocol

polymorphism in Elixir

protocol in Elixir as interface in Java or class in Haskell - method's declarations only

dispatching on a protocol is available to any data type as long as it implements the protocol

implementation - definitions of the protocol methods for concrete Type

defprotocol Blank
do

  def blank?(x)

end
  

so now you can define implementations:

defimpl Blank , for: Integer
do

  def blank?(_) , do: false

end


defimpl Blank , for: List
do

  def blank?([]) , do: true
  def blank?(_)  , do: false

end


defimpl Blank , for: Map
do

  def blank?(map) , do: map_size(map) == 0

end


defimpl Blank , for: Atom
do

  def blank?(false) , do: true
  def blank?(nil)   , do: true
  def blank?(_)     , do: false

end

with the protocol defined and implementations in hand:

iex> Blank.blank?(0)
false
iex> Blank.blank?([])
true
iex> Blank.blank?([1, 2, 3])
false

passing a data type that does not implement the protocol raises an error

iex> Blank.blank?("hello")
** (Protocol.UndefinedError) protocol Blank not implemented for "hello"

iex> defmodule User do 
...>   defstruct name: "john", age: 27
...> end

iex> Blank.blank?(%User{})
** (Protocol.UndefinedError) protocol Blank not implemented for %User{age: 27, name: "john"}

iex> defimpl Blank, for: User do
...>   def blank?(_), do: false
...> end
{:module, Blank.User,
 <<70, 79, 82, 49, 0, 0, 6, 96, 66, 69, 65, 77, 69, 120, 68, 99, 0, 0, 0, 207,
   131, 104, 2, 100, 0, 14, 101, 108, 105, 120, 105, 114, 95, 100, 111, 99, 115,
   95, 118, 49, 108, 0, 0, 0, 4, 104, 2, ...>>, {:__impl__, 1}}

iex> Blank.blank?(%User{})      
false

functions

assert_impl!(protocol, base) -> :ok | no_return
   checks if the given base module is loaded and is an implementation of the given protocol
   returns :ok if so, otherwise raises ArgumentError

assert_protocol!(module_protocol) -> :ok | no_return
   checks if the given module is loaded and is protocol
   returns :ok if so, otherwise raises ArgumentError

consolidate(module, [module]) ->  ::
     {:ok, binary}
   | {:error, :not_a_protocol}
   | {:error, :no_beam_info}
  receives a protocol and a list of implementations and consolidates the given protocol
 
consolidated?(module) :: boolean
   this function does not load the protocol nor loads the new bytecode for the compiled module
   however each implementation must be available and it will be loaded
  

example

        iex> p = :code.lib_dir(:elixir, :ebin)
        iex> m = Protocol.extract_impls(Enumerable, [p])
         [Function, GenEvent.Stream, List, HashSet, Range,
          File.Stream, Stream, IO.Stream, HashDict, Map, MapSet]
        iex> List in m
        true
        iex> s = Protocol.extract_protocols([p])
        [Inspect, String.Chars, Enumerable, Collectable, List.Chars]
        iex> Enumerable in s
        true