Some macros to enable railsway-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:
- https://github.com/remiq/railway-oriented-programming-elixir
- https://gist.github.com/zabirauf/17ced02bdf9829b6956e (Railway Oriented Programming macros in Elixir)
If available in Hex, the package can be installed as:
- Add rop to your list of dependencies in
mix.exs:
defdepsdo[{:rop,"~> 0.5"}]endCall
useRopin 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
defmoduleTripleArrowExampledouseRopdeftagged_inc(v)doIO.puts"inc for #{v}"# sideeffect for demonstration{:ok,v+1}enddeferror_fn(_)do{:error,"I'm a bad fn!"}enddefraising_fn(_)doraise"I'm raising!"enddefresultdo1|>tagged_inc>>>tagged_inc>>>tagged_incenddeferror_resultdo1|>tagged_inc>>>tagged_inc>>>error_fn>>>tagged_incenddefraising_resultdo1|>tagged_inc>>>tagged_inc>>>raising_fn>>>tagged_incendendiex>TripleArrowExample.resultincfor1incfor2incfor3{: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_resultincfor1incfor2{:error,"I'm a bad fn!"}### raises... We'll fix it in a later example for try_catch!iex>TripleArrowExample.raising_resultincfor1incfor2**(RuntimeError) I'm raising!Wraps a simple function to return a tagged tuple with :ok to comply to the protocol {:ok, result}: e.g.
defmoduleBindExampledouseRopdefinc(v)dov+1enddefonly_last_pipe_tagged_result(v)dov|>inc|>bind(inc)enddefresult_fully_tagged(v)dov|>bind(inc)>>>bind(inc)>>>bind(inc)endendiex>BindExample.only_last_pipe_tagged_result(2){:ok,4}iex>BindExample.result_fully_tagged(2){:ok,5}Wraps raising functions to return a tagged tuple {:error, ErrorMessage} to comply with the protocol
# modified example from TripleArrowExample to handle raising functionsdefmoduleTryCatchExampledouseRopdeftagged_inc(v)doIO.puts"inc for #{v}"# sideeffect for demonstration{:ok,v+1}enddefraising_fn(_)doraise"I'm raising!"enddefresultdo1|>tagged_inc>>>tagged_inc>>>tagged_incenddefraising_result_wrapped(v)dov|>tagged_inc>>>tagged_inc>>>try_catch(raising_fn)>>>tagged_incendendiex>TryCatchExample.raising_result_wrapped(1)incfor1incfor2{:error,%RuntimeError{message: "I'm raising!"}}Like a similar Unix utility it does some work and returns the input. See tee (command), Unix.
defmoduleTeeExampledouseRopdeftagged_inc(v)doIO.puts"inc for #{v}"# sideeffect for demonstration{:ok,v+1}enddefcalc(v)dov|>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)incfor1incfor1incfor1_{:ok,1}A simple utility function to extract the value from {:ok, result} tuple and to raise the error in {:error, ErrorStruct}.
defmoduleOkExampledouseRopdefok_resultdo{:ok,1}|>okenddeferror_resultdo{:error,%ArithmeticError{}}|>okenddefany_value_resultdo"bad value"|>okendendiex>OkExample.ok_result1iex>OkExample.error_result**(ArithmeticError) bad argument inarithmeticexpression(rop) lib/rop.ex:70: Rop.ok/1iex>OkExample.any_value_result**(RuntimeError)badvalue(rop) lib/rop.ex:71: Rop.ok/1- 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://www.zohaib.me/monads-in-elixir-2/
- http://fsharpforfunandprofit.com/posts/recipe-part2/
- https://www.reddit.com/r/programming/comments/30coly/railway_oriented_programming_in_elixir/
- 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
- https://github.com/batate/elixir-pipes - Macros for more flexible composition with the Elixir Pipe operator