Skip to content

Instantly share code, notes, and snippets.

@mprymek
Created April 18, 2015 15:10
Show Gist options
  • Save mprymek/7d05487ffa923a71a295 to your computer and use it in GitHub Desktop.
Save mprymek/7d05487ffa923a71a295 to your computer and use it in GitHub Desktop.
Example of a specialization pattern in Elixir
# 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