Last active
December 15, 2015 17:57
-
-
Save nirev/b722fbbb9fdb91cd5aa8 to your computer and use it in GitHub Desktop.
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
defmodule MonadError do | |
defmacro __using__(_) do | |
quote do | |
import MonadError, only: [bind: 1, fail: 1, fail: 0, with: 3, either: 3, sandbox: 1] | |
require MonadError | |
end | |
end | |
defmacro sandbox(body) do | |
quote do | |
try do | |
unquote(Keyword.get(body, :do)) | |
after; end | |
end | |
end | |
defmacro with(ctx, var, body) do | |
var = Macro.var(var, nil) | |
quote do | |
case unquote(ctx) do | |
{:cont, value} -> | |
unquote(var) = value | |
unquote(Keyword.get body, :do) | |
{:fail, reason} -> {:fail, reason} | |
_ -> {:fail, "runtime_error: {:cont, x} | {:fail, x} was expected"} | |
end | |
end | |
end | |
def from_ok2({:ok, v}), do: bind(v) | |
def from_ok2({:error, v}), do: fail(v) | |
def from_ok2(_), do: fail(:from_ok2) | |
def from_ok1(:ok), do: bind(nil) | |
def from_ok1(_), do: fail(:from_ok1) | |
def from_bool(true), do: bind(true) | |
def from_bool(_), do: fail(:from_bool) | |
def not_nil(nil), do: fail(:not_nil) | |
def not_nil(x), do: bind(x) | |
def fail, do: fail(nil) | |
def fail(x), do: {:fail, x} | |
def bind(x), do: {:cont, x} | |
def check({:cont, done}), do: {:cont, done} | |
def check({:fail, fail}), do: {:fail, fail} | |
def check(_), do: {:fail, "runtime_error: {:cont, x} | {:fail, x} was expected"} | |
def join(x) do | |
case x do | |
{:cont, {:cont, done}} -> {:cont, done} | |
{:cont, {:fail, fail}} -> {:fail, fail} | |
{:fail, fail} -> {:fail, fail} | |
_ -> {:fail, "runtime_error: {:cont, {:cont, x}} | {:cont, {:fail, x}} | {:fail, x} was expected"} | |
end | |
end | |
def mapM(xs, fun) do | |
r = Enum.reduce_while(xs, {:cont, []}, fn x, acc -> | |
{:cont, acc} = acc | |
case (fun.(x)) do | |
{:cont, r} -> {:cont, {:cont, [r | acc]}} | |
r = {:fail, _} -> {:halt, r} | |
end | |
end) | |
case r do | |
{:cont, xs} -> {:cont, Enum.reverse xs} | |
{:fail, msg} -> {:fail, msg} | |
end | |
end | |
defmacro either(value, failure, success) do | |
quote do | |
case unquote(value) do | |
{:cont, x} -> unquote(success).(x) | |
{:fail, x} -> unquote(failure).(x) | |
end | |
end | |
end | |
end |
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
def something(params) do | |
repo_insert = fn x -> MonadError.from_ok2 (Repo.insert x) end | |
repo_update = fn x -> MonadError.from_ok2 (Repo.update x) end | |
repo_insert.(User.changeset(%User{}, params)) | |
|> with(:user, do: repo_insert.(Profile.create_changeset(%Profile{}, (put_in params, ["user_id"], user.id)))) | |
|> with(:profile, do: repo_insert.(Position.create_changeset(%Position{}, (put_in params, ["profile_id"], profile.id)))) | |
|> with(:position, do: repo_update.(Profile.update_changeset(profile, %{position_id: position.id}))) | |
|> with(:profile, do: bind [user: user, profile: profile, position: position]) | |
end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment