Created
April 18, 2015 16:21
-
-
Save mprymek/1ebb4c28247879f70463 to your computer and use it in GitHub Desktop.
Example of a specialization pattern in Elixir - using a protocol this time
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# | |
# Another example of specialization in Elixir. This time using | |
# a protocol. | |
# | |
# I think it's a cleaner implementation. | |
# | |
defprotocol MyProto do | |
def base_data(state) | |
def fun1(state) | |
def fun2(state) | |
end | |
# general base module | |
defmodule ImplBase.State do | |
defstruct base_data: nil | |
end | |
defmodule ImplBase do | |
alias ImplBase.State | |
@doc "Initialize ImplBase data" | |
def init do | |
%State{base_data: :some_base_data} | |
end | |
defmacro __using__([]) do | |
quote do | |
def base_data(state), do: state.base_data | |
def fun1(state) do | |
IO.puts "fun1 - ImplBase implementation. data=#{inspect MyProto.base_data(state)}" | |
state | |
end | |
def fun2(state) do | |
IO.puts "fun2 - ImplBase implementation. data=#{inspect MyProto.base_data(state)}" | |
state | |
end | |
defoverridable [base_data: 1, fun1: 1, fun2: 1] | |
end | |
end | |
end | |
# specific module no 1 | |
# - implementation without using any processes | |
defmodule Impl1.State do | |
defstruct base_data: nil, impl1_data: nil | |
end | |
defmodule Impl1 do | |
use ImplBase | |
alias Impl1.State | |
@doc "Initialize Impl1 data" | |
def init do | |
%State{base_data: ImplBase.init, impl1_data: :some_impl1_data} | |
end | |
end | |
defimpl MyProto, for: Impl1.State do | |
alias Impl1.State | |
use ImplBase | |
def fun1(s=%State{}) do | |
IO.puts "fun1 - Impl1. data=#{inspect s}" | |
s | |
end | |
end | |
# specific module no 2 | |
# - implementation using Agent to store the data | |
# | |
# NOTE: we still have the "two structures" problem here - we must use one data structure | |
# as a state wich allows specific MyProto implementation and another | |
# datastructure to be fed into Agent. | |
# datastructured passed to the user | |
defmodule Impl2.State1 do | |
defstruct agent: nil | |
end | |
# internal datastructure kept in Agent | |
defmodule Impl2.State2 do | |
defstruct base_data: nil, impl2_data: nil | |
end | |
defmodule Impl2 do | |
alias Impl2.State1 | |
alias Impl2.State2 | |
use ImplBase | |
@doc "Initialize Impl2 data" | |
def init do | |
{:ok,a} = Agent.start_link fn -> %State2{base_data: ImplBase.init, impl2_data: :some_impl2_data} end | |
%State1{agent: a} | |
end | |
end | |
defimpl MyProto, for: Impl2.State1 do | |
alias Impl2.State1 | |
alias Impl2.State2 | |
use ImplBase | |
def base_data(%State1{agent: a}), do: Agent.get(a, fn %State2{base_data: bd} -> bd end) | |
def fun1(s=%State1{agent: a}) do | |
data = Agent.get a, fn x -> x end | |
IO.puts "fun1 - Impl2. data=#{inspect data}" | |
s | |
end | |
end | |
# | |
# main | |
# | |
# NOTE: we have a big advantage here: we call MyProto.fun1 and not Impl1.fun1 as in the previous example | |
Impl1.init | |
|> MyProto.fun1 | |
|> MyProto.fun2 | |
Impl2.init | |
|> MyProto.fun1 | |
|> MyProto.fun2 |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment