Skip to content

Instantly share code, notes, and snippets.

@stefanluptak
Last active September 5, 2019 13:25
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save stefanluptak/f5588b1b04836f2464071ddc7731932d to your computer and use it in GitHub Desktop.
Save stefanluptak/f5588b1b04836f2464071ddc7731932d to your computer and use it in GitHub Desktop.
Behaviour vs. Protocol in Elixir
defmodule Counter do
@type t :: %__MODULE__{count: integer()}
defstruct count: 0
def transform(counter, %transformation_module{} = transformation) do
transformation_module.transform(counter, transformation)
end
end
defmodule Counter.Transformation do
@callback transform(Counter.t(), term()) :: Counter.t()
end
defmodule Increment do
defstruct [:by]
@behaviour Counter.Transformation
@impl Counter.Transformation
def transform(%Counter{} = counter, %__MODULE__{} = increment) do
%{counter | count: counter.count + increment.by}
end
end
defmodule Multiply do
@enforce_keys [:by]
defstruct [:by]
@behaviour Counter.Transformation
@impl Counter.Transformation
def transform(%Counter{} = counter, %__MODULE__{} = multiply) do
%{counter | count: counter.count * multiply.by}
end
end
# usage
counter = %Counter{count: 3}
increment = %Increment{by: 4}
Counter.transform(counter, increment) == %Counter{count: 7}
defmodule Increment do
defstruct [:by]
end
defmodule Multiply do
defstruct [:by]
end
defmodule Counter do
@type t :: %__MODULE__{count: integer()}
defstruct count: 0
def transform(%__MODULE__{} = counter, %Increment{} = increment) do
%{counter | count: counter.count + increment.by}
end
def transform(%__MODULE__{} = counter, %Multiply{} = multiply) do
%{counter | count: counter.count * multiply.by}
end
end
# usage
counter = %Counter{count: 3}
increment = %Increment{by: 4}
Counter.transform(counter, increment) == %Counter{count: 7} # true
defmodule Counter do
@type t :: %__MODULE__{count: integer()}
defstruct count: 0
def transform(%__MODULE__{} = counter, %transformation_module{} = transformation) do
protocol_module = Module.concat(Counter.Transformation, transformation_module)
protocol_module.transform(counter, transformation)
end
end
defprotocol Counter.Transformation do
def transform(counter, transformation)
end
defmodule Increment do
defstruct [:by]
defimpl Counter.Transformation do
def transform(%Counter{} = counter, %@for{} = increment) do
%{counter | count: counter.count + increment.by}
end
end
end
defmodule Multiply do
defstruct [:by]
defimpl Counter.Transformation do
def transform(%Counter{} = counter, %@for{} = multiply) do
%{counter | count: counter.count * multiply.by}
end
end
end
# usage
counter = %Counter{count: 3}
increment = %Increment{by: 4}
Counter.transform(counter, increment) == %Counter{count: 7}
defmodule Counter do
@type t :: %__MODULE__{count: integer()}
defstruct count: 0
end
defprotocol Counter.Transformation do
def apply(transformation, counter)
end
defmodule Increment do
defstruct [:by]
defimpl Counter.Transformation do
def apply(increment, %Counter{} = counter) do
%{counter | count: counter.count + increment.by}
end
end
end
defmodule Multiply do
defstruct [:by]
defimpl Counter.Transformation do
def apply(multiply, %Counter{} = counter) do
%{counter | count: counter.count * multiply.by}
end
end
end
# usage
counter = %Counter{count: 3}
increment = %Increment{by: 4}
Counter.Transformation.apply(increment, counter) == %Counter{count: 7}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment