Created
April 18, 2015 15:10
-
-
Save mprymek/7d05487ffa923a71a295 to your computer and use it in GitHub Desktop.
Example of a specialization pattern in Elixir
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
# Just an example of specialization in Elixir. | |
# | |
# I'm not sure if this pattern is a good practice in Elixir or | |
# it's too much "OOP-like" and it would be more Elixirific to | |
# use protocols. But using protocols to do the same thing would | |
# probably lead to more boilerplate code (explicitly define all | |
# funX in every protocol implementation). | |
# | |
# general base module | |
defmodule ImplBase.State do | |
defstruct base_data: nil | |
end | |
defmodule ImplBase do | |
alias ImplBase.State | |
defmacro __using__([]) do | |
quote do | |
def fun1(state) do | |
# | |
# NOTE: this is problematic - we DEMAND that all implementations' state data | |
# allow Acces.get(data,:base_data) | |
# | |
# Or we could use some custom protocol to access base_data here but it's | |
# almost the same situation... | |
# | |
IO.puts "fun1 - ImplBase implementation. data=#{inspect state.base_data}" | |
state | |
end | |
def fun2(state) do | |
IO.puts "fun2 - ImplBase implementation. data=#{inspect state.base_data}" | |
state | |
end | |
defoverridable [fun1: 1, fun2: 1] | |
end | |
end | |
@doc "Initialize ImplBase data" | |
def init do | |
%State{base_data: :some_base_data} | |
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 | |
alias Impl1.State | |
use ImplBase | |
@doc "Initialize Impl1 data" | |
def init do | |
%State{base_data: ImplBase.init, impl1_data: :some_impl1_data} | |
end | |
def fun1(s=%State{}) do | |
IO.puts "fun1 - Impl1. data=#{inspect s}" | |
s | |
end | |
end | |
# specific module no 2 | |
# - implementation using Agent to store data | |
# | |
# NOTE: we have little problem here - we must use one data structure | |
# as state wich allows Access.get(s,:base_data) and another | |
# datastructure to be fed into Agent... | |
defmodule Impl2.State do | |
defstruct impl2_data: nil | |
end | |
defmodule Impl2 do | |
alias Impl2.State | |
use ImplBase | |
@doc "Initialize Impl2 data" | |
def init do | |
{:ok,a} = Agent.start_link fn -> %State{impl2_data: :some_impl2_data} end | |
%{base_data: ImplBase.init, agent: a} | |
end | |
def fun1(s=%{agent: a}) do | |
data = Agent.get a, fn x -> x end | |
IO.puts "fun1 - Impl2. data=#{inspect data}" | |
s | |
end | |
end | |
# | |
# main | |
# | |
Impl1.init | |
|> Impl1.fun1 | |
|> Impl1.fun2 | |
Impl2.init | |
|> Impl2.fun1 | |
|> Impl2.fun2 |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment