Exordia
Yeah yet another module with convenience macros/functions for piping.
But this one is the best one.
Installation
The package can be installed by adding exordia to your list of dependencies in
mix.exs:
def deps do
[
{:exordia, "~> 0.8"}
]
endBackground
The normal pipe operator is rather limited if you want to do anything other than crash on any failure. It can also be annoying how even if you want to crash on failure, some functions only provide an {:ok, result} or {:error, reason} return that you're obligated to handle.
The with language construct is interesting but unless everything in the with block returns a distinguishable error message, it can be difficult to determine where in the block a failure occurred.
Concept
So instead of that shit, we strip the :ok's off of everything and turn :error, {:error, x} and {:error, x, y} into an Error struct so everything piped through is either a raw value or an Error struct.
The ~> operator pipes the value to the function unless it is an error, in which case the function isn't called at all and the error falls through. So if you only use the ~> operator, any error will drop through as the return value of the chain.
The <~ operator is the opposite -- it only pipes unhandled errors -- non-errors and handled errors fall through. (The Error struct has a :handled field which marks an error as handled and prevents further error handlers from picking it up.
This allows for a very elegant pattern.
Also we support _ as a placeholder if you want to pipe to an argument other than the first and some other cool stuff.
use Exordia
user
~> authenticate()
<~ handle_auth_errors()
~> validate_input()
<~ handle_validation_errors()
~> process_input()
<~ handle_processing_errors()
~> show_success_message()
<~ handle_success_errors()Caveat
We provide functions to convert an Error to a tuple or a value to an :ok tuple for when this is needed, but if you're pulling some value that might randomly be an :ok or :error tuple, just use |> for that portion of your code and use better programming practices in the future.
So why not use an existing solution or monads or something?
I haven't seen any other solutions that allow for this level of elegance.
Haskell-style monads are cool and all but the attempts I've seen to shoehorn them into Elixir don't seem useful. I think more language-level support would be needed for this to be of any real value.