Skip to content

Instantly share code, notes, and snippets.

@polvalente
Created November 27, 2020 03:51
Show Gist options
  • Save polvalente/b8524232f8ba2f3910b12c53a9c82456 to your computer and use it in GitHub Desktop.
Save polvalente/b8524232f8ba2f3910b12c53a9c82456 to your computer and use it in GitHub Desktop.
Effect handling with strategy + observer patterns
defmodule Effect do
defstruct subscribers: [], payload: nil
end
defmodule EffectHandler do
@callback handle(effect :: Effect.t()) :: :ok | {:error, reason :: term()}
defmacro __using__(effectful_function: [{effectful_function, 0}]) do
quote do
def main do
EffectHandler.handle(unquote(effectful_function)())
end
end
end
def handle(%Effect{subscribers: subscribers} = effect) do
# Strategy-pattern implementation (without a cumulative context for simplicity)
result =
Enum.reduce_while(subscribers, :ok, fn subscriber, _ ->
case subscriber.handle(effect) do
:ok ->
{:cont, :ok}
{:error, _} = err ->
{:halt, err}
end
end)
case result do
:ok ->
{:ok, effect.payload}
error ->
error
end
end
end
defmodule MyLogger do
@behaviour EffectHandler
require Logger
def handle(%Effect{payload: payload}) do
Logger.info("Received payload: #{inspect(payload)}")
end
end
defmodule HandlerWithError do
@behaviour EffectHandler
def handle(%Effect{}) do
{:error, :could_not_handle}
end
end
defmodule MyIO do
@behaviour EffectHandler
def handle(%Effect{payload: payload}) do
IO.inspect(payload)
:ok
end
end
defmodule EffectfulCalculator do
def add(x, y) do
z = x + y
effectful_log(z)
end
def effectful_log(z) do
%Effect{subscribers: [MyLogger, HandlerWithError, MyIO], payload: z}
end
end
defmodule Main do
use EffectHandler, effectful_function: [_my_fun: 0]
def _my_fun do
EffectfulCalculator.add(1, 2)
end
end
@polvalente
Copy link
Author

iex(1)> Main.main
{:error, :could_not_handle}
iex(2)> 
00:51:00.771 erl_level=info application=teste domain=elixir file=lib/effect_dispatching.ex function=handle/1 line=45 mfa=MyLogger.handle/1 module=MyLogger pid=<0.223.0> [info]  Received payload: 3
 
nil

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment