Last active
August 18, 2023 14:32
-
-
Save jcelliott/db4d4d5af0ef409b799287658407e20a to your computer and use it in GitHub Desktop.
Small Elixir implementation of Railway Oriented Programming
This file contains 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 Railway do | |
@moduledoc """ | |
Railway implements the ideas from this blog post: https://zohaib.me/railway-programming-pattern-in-elixir/ | |
It is essentially an implementation of the 'Either' monad constrained to the Elixir :ok/:error | |
result tuple convention. It can replace many straightforward uses of a 'with' statement. | |
Use it like this: | |
``` | |
import Railway | |
value | |
|> function_that_might_fail() | |
~> handle_success() # will only be called if the previous result was '{:ok, _}' | |
~> do_something_else() | |
``` | |
""" | |
# credo:disable-for-next-line Credo.Check.Readability.FunctionNames | |
defmacro left ~> right do | |
quote do | |
(fn -> | |
case unquote(left) do | |
{:ok, x} -> | |
x |> unquote(right) | |
{:error, err} -> | |
{:error, err} | |
other -> | |
raise """ | |
Railway used with unexpected result format. | |
Result should be in the format '{:ok, result}' or '{:error, reason}'. | |
Got #{inspect(other)} | |
""" | |
end | |
end).() | |
end | |
end | |
@doc ~S""" | |
Works like the unix `tee` command. It will pass the input unchanged to the | |
next step in the pipeline, while also passing it to the called function | |
Examples: | |
iex> import Railway | |
iex> "apple" |> tee(String.upcase()) | |
{:ok, "apple"} | |
iex> {:ok, "apple"} ~> tee(String.upcase()) | |
{:ok, "apple"} | |
""" | |
defmacro tee(args, func) do | |
quote do | |
(fn -> | |
unquoted_args = unquote(args) | |
unquoted_args |> unquote(func) | |
{:ok, unquoted_args} | |
end).() | |
end | |
end | |
@doc ~S""" | |
Wraps a value in an {:ok, value} tuple. | |
Examples: | |
iex> import Railway | |
iex> "apple" |> String.upcase() |> ok() | |
{:ok, "APPLE"} | |
""" | |
defmacro ok(value) do | |
quote do | |
{:ok, unquote(value)} | |
end | |
end | |
@doc ~S""" | |
Wraps a value in an {:ok, value} tuple. | |
Examples: | |
iex> import Railway | |
iex> "apple" |> ok(String.upcase) | |
{:ok, "APPLE"} | |
""" | |
defmacro ok(args, func) do | |
quote do | |
(fn -> | |
result = unquote(args) |> unquote(func) | |
{:ok, result} | |
end).() | |
end | |
end | |
end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment