ruby2elixir/plumber_girl
{ "createdAt": "2016-01-12T18:28:56Z", "defaultBranch": "master", "description": "PlumberGirl takes care of your Elixir piping issues!", "fullName": "ruby2elixir/plumber_girl", "homepage": null, "language": "Elixir", "name": "plumber_girl", "pushedAt": "2016-12-23T13:24:58Z", "stargazersCount": 32, "topics": [], "updatedAt": "2023-09-01T11:26:25Z", "url": "https://github.com/ruby2elixir/plumber_girl"}PlumberGirl takes care of your Elixir piping issues!
Section titled “PlumberGirl takes care of your Elixir piping issues!”PlumberGirl provides some macros to enable railway-oriented programming in Elixir.
Collected from code snippets and wrapped into a simple library for your convenience.
For more examples please check the tests here:
Sources for inspiration + copying
Section titled “Sources for inspiration + copying”- http://www.zohaib.me/railway-programming-pattern-in-elixir/
- https://github.com/remiq/railway-oriented-programming-elixir
- https://gist.github.com/zabirauf/17ced02bdf9829b6956e (Railway Oriented Programming macros in Elixir)
Installation
Section titled “Installation”- Add rop to your list of dependencies in
mix.exs:
def deps do [{:plumber_girl, "~> 0.9.5"}]endCall
use PlumberGirlin your module. That will give you access to following macros/functions:
>>>
No need to stop pipelining in case of an error somewhere in the middle
used like: 1 |> fn1 >>> fn2 >>> fn3 >>> fn4
defmodule TripleArrowExample do use PlumberGirl def tagged_inc(v) do IO.puts "inc for #{v}" # sideeffect for demonstration {:ok, v + 1} end
def error_fn(_) do {:error, "I'm a bad fn!"} end
def raising_fn(_) do raise "I'm raising!" end
def result do 1 |> tagged_inc >>> tagged_inc >>> tagged_inc end
def error_result do 1 |> tagged_inc >>> tagged_inc >>> error_fn >>> tagged_inc end
def raising_result do 1 |> tagged_inc >>> tagged_inc >>> raising_fn >>> tagged_inc endend
iex> TripleArrowExample.resultinc for 1inc for 2inc for 3{:ok, 4}
### increases twice, errors and tries to increase again### notice that after errored result we don't execute any function anymore in the pipeline,### e.g. only tagged_inc before error_fn were executed.iex> TripleArrowExample.error_resultinc for 1inc for 2{:error, "I'm a bad fn!"}
### raises... We'll fix it in a later example for try_catch!iex> TripleArrowExample.raising_resultinc for 1inc for 2** (RuntimeError) I'm raising!Wraps a simple function to return a tagged tuple with :ok to comply to the protocol {:ok, result}: e.g.
defmodule BindExample do use PlumberGirl def inc(v) do v + 1 end
def only_last_pipe_tagged_result(v) do v |> inc |> bind(inc) end
def result_fully_tagged(v) do v |> bind(inc) >>> bind(inc) >>> bind(inc) endendiex> BindExample.only_last_pipe_tagged_result(2){:ok, 4}
iex> BindExample.result_fully_tagged(2){:ok, 5}try_catch
Section titled “try_catch”Wraps raising functions to return a tagged tuple {:error, ErrorMessage} to comply with the protocol
# modified example from TripleArrowExample to handle raising functionsdefmodule TryCatchExample do use PlumberGirl def tagged_inc(v) do IO.puts "inc for #{v}" # sideeffect for demonstration {:ok, v + 1} end
def raising_fn(_) do raise "I'm raising!" end
def result do 1 |> tagged_inc >>> tagged_inc >>> tagged_inc end
def raising_result_wrapped(v) do v |> tagged_inc >>> tagged_inc >>> try_catch(raising_fn) >>> tagged_inc endend
iex> TryCatchExample.raising_result_wrapped(1)inc for 1inc for 2{:error, %RuntimeError{message: "I'm raising!"}}Like a similar Unix utility it does some work and returns the input. See tee (command), Unix.
defmodule TeeExample do use PlumberGirl def tagged_inc(v) do IO.puts "inc for #{v}" # sideeffect for demonstration {:ok, v + 1} end
def calc(v) do v |> tee(tagged_inc) >>> tee(tagged_inc) >>> tee(tagged_inc) endend
# notice how the incremented value is not passed through the pipeline,# but just the original argument `1`iex> TeeExample.calc(1)inc for 1inc for 1inc for 1_{:ok, 1}A simple utility function to extract the value from {:ok, result} tuple and to raise the error in {:error, ErrorStruct}.
defmodule OkExample do use PlumberGirl def ok_result do {:ok, 1} |> ok end
def error_result do {:error, %ArithmeticError{}} |> ok end
def any_value_result do "bad value" |> ok endend
iex> OkExample.ok_result1
iex> OkExample.error_result** (ArithmeticError) bad argument in arithmetic expression (rop) lib/rop.ex:70: Rop.ok/1
iex> OkExample.any_value_result** (RuntimeError) bad value (rop) lib/rop.ex:71: Rop.ok/1Background information
Section titled “Background information”Some discussions about Railsway programming:
Section titled “Some discussions about Railsway programming:”- http://www.zohaib.me/railway-programming-pattern-in-elixir/
- http://www.zohaib.me/monads-in-elixir-2/
- http://insights.workshop14.io/2015/10/18/handling-errors-in-elixir-no-one-say-monad.html
- http://blog.danielberkompas.com/2015/09/03/better-pipelines-with-monadex.html
- http://onor.io/2015/08/27/railway-oriented-programming-in-elixir/
- http://www.zohaib.me/railway-programming-pattern-in-elixir/
- http://fsharpforfunandprofit.com/posts/recipe-part2/
- https://www.reddit.com/r/programming/comments/30coly/railway_oriented_programming_in_elixir/
Code (Railway Programming)
Section titled “Code (Railway Programming)”- https://github.com/remiq/railway-oriented-programming-elixir/blob/master/lib/rop.ex
- https://gist.github.com/zabirauf/17ced02bdf9829b6956e (Railway Oriented Programming macros in Elixir) -> Rop
- https://github.com/CrowdHailer/OK/blob/master/lib/ok.ex
- https://gist.github.com/danielberkompas/52216db76d764a68dfa3 -> pipeline.ex
Code (Monads)
Section titled “Code (Monads)”- https://github.com/rmies/monad.git
- https://github.com/rob-brown/MonadEx.git
- https://github.com/slogsdon/elixir-control.git
- https://github.com/niahoo/monk.git
- https://github.com/knrz/towel.git
Why Railway Oriented Programming might be simpler than Monads
Section titled “Why Railway Oriented Programming might be simpler than Monads”Alternatives
Section titled “Alternatives”- https://github.com/batate/elixir-pipes - Macros for more flexible composition with the Elixir Pipe operator
- https://github.com/vic/ok_jose - Pipe elixir functions that match {:ok,}, {:error,} tuples or custom patterns.