solution v1.0.2 Solution View Source

A Macro-based approach to working with ok/error tuples

This module exposes two main things:

  1. guard-clause macros is_ok/1, is_error/1 and is_okerror/1 (as well as arity-2 variants of the same), to be used whenever you like.
  2. scase/2 and swith/2, replacements for case and with-statements respectively that allow you to pattern match on ok/error tuples more effectively, as well as bind to one ore multiple of the values stored inside.

Link to this section Summary

Functions

Matches any error datatype.

Matches {:error, res} (as well as tuples with more elements). res is then bound.

Turns a nillable type (that can be either nil or a non-nil value) into an ok/error tuple.

Changes an :ok into an :error, an {:ok, ...} into an {:error, ...} and vice-versa.

Matches when x is one of the following

Matches when x is a long-enough ok-tuple that has more than n_elems elements.

Matches when x is one of the following

Matches when x is a long-enough ok-tuple that has more than n_elems elements.

Matches when either is_ok(x) or is_error(x) matches.

Matches when x is a long-enough ok-tuple that has more than n_elems elements.

Matches any ok datatype.

Matches {:ok, res} (as well as tuples with more elements). res is then bound.

Matches any ok/error datatype.

Matches any ok or error type. tag is then bound to :ok or :error.

Matches {:ok, res}, {:error, res} (as well as tuples with more elements). tag and res are bound.

Works like a normal case-statement, but will expand ok(), error() and okerror()macros to the left side of ->.

Works like a normal with-statement, but will expand ok(), error() and okerror() macros to the left side of <-.

Link to this section Functions

Matches any error datatype.

(See also is_error/1)

Has to be used inside the LHS of a scase or swith statement.

Matches {:error, res} (as well as tuples with more elements). res is then bound.

(See also is_error/1)

Has to be used inside the LHS of a scase or swith statement.

Turns a nillable type (that can be either nil or a non-nil value) into an ok/error tuple.

Also handles Erlang's 'nil'-type equivalent: the atom :undefined.

iex> from_nillable(nil)
{:error, nil}
iex> from_nillable(42)
{:ok, 42}
iex> (%{a: "yes!"} |> Map.get(:a) |> from_nillable())
{:ok, "yes!"}
iex> (%{a: "yes!"} |> Map.get(:b) |> from_nillable())
{:error, nil}
iex> from_nillable(:undefined)
{:error, :undefined}

Changes an :ok into an :error, an {:ok, ...} into an {:error, ...} and vice-versa.

iex> invert_okerror(:ok)
:error
iex> invert_okerror({:ok, 1,2,3})
{:error, 1,2,3}
iex> invert_okerror({:error, "failure"})
{:ok, "failure"}
iex> invert_okerror("improper datatype")
** (ArgumentError) argument error

Matches when x is one of the following:

  • :error
  • {:error, _}
  • {:error, _, _}
  • or a longer tuple where the first element is the atom :error. ({:error, ...})

    iex> is_error(:error) true iex> is_error({:error, 42}) true iex> is_error({:error, "I", "have", "many", "elements"}) true iex> is_error(:asdf) false iex> is_error({:ok, "success!"}) false

Link to this macro

is_error(x, n_elems) View Source (macro)

Matches when x is a long-enough ok-tuple that has more than n_elems elements.

Matches when x is one of the following:

  • :ok
  • {:ok, _}
  • {:ok, _, _}
  • or a longer tuple where the first element is the atom :ok ({:ok, ...})

    iex> is_ok(:ok) true iex> is_ok({:ok, 42}) true iex> is_ok({:ok, "I", "have", "many", "elements"}) true iex> is_ok(:asdf) false iex> is_ok({:error, "failure"}) false

Link to this macro

is_ok(x, n_elems) View Source (macro)

Matches when x is a long-enough ok-tuple that has more than n_elems elements.

Matches when either is_ok(x) or is_error(x) matches.

iex> is_okerror({:ok, "Yay!"})
true
iex> is_okerror({:error, "Nay"})
true
iex> is_okerror(false)
false
iex> is_okerror({})
false
iex> is_okerror({:ok, "the", "quick", "brown", "fox"})
true
Link to this macro

is_okerror(x, n_elems) View Source (macro)

Matches when x is a long-enough ok-tuple that has more than n_elems elements.

Warning: Will not match plain :ok or :error!

Matches any ok datatype.

(See also is_ok/1)

Has to be used inside the LHS of a scase or swith statement.

Matches {:ok, res} (as well as tuples with more elements). res is then bound.

(See also is_ok/1)

Has to be used inside the LHS of a scase or swith statement.

Matches any ok/error datatype.

Has to be used inside the LHS of a scase or swith statement.

Matches any ok or error type. tag is then bound to :ok or :error.

(See also is_okerror/1)

Has to be used inside the LHS of a scase or swith statement.

Link to this macro

okerror(tag, res) View Source (macro)

Matches {:ok, res}, {:error, res} (as well as tuples with more elements). tag and res are bound.

(See also is_okerror/1)

tag is bound to the value :ok or :error depending on the tuple. res is bound to what the second element might be.

Has to be used inside the LHS of a scase or swith statement.

Link to this macro

scase(input, conditions) View Source (macro)

Works like a normal case-statement, but will expand ok(), error() and okerror()macros to the left side of ->.

iex> scase {:ok, 10} do
...>  ok() -> "Yay!"
...>  _ -> "Failure"
...>  end
"Yay!"

You can also pass arguments to ok(), error() or okerror() which will then be bound and available to be used inside the case expression:

iex> scase {:ok, "foo", 42} do
...>   ok(res, extra) ->
...>     "result: #{res}, extra: #{extra}"
...>  _ ->
...>    "Failure"
...> end
"result: foo, extra: 42"

Note that for ok() and error(), the first argument will match the first element after the :ok or :error tag. On the other hand, for okerror(), the first argument will match the tag :ok or :error.

Note: It is not required to import Solution to use the macros inside swith without prefixing them.

Link to this macro

swith(statements, conditions) View Source (macro)

Works like a normal with-statement, but will expand ok(), error() and okerror() macros to the left side of <-.

iex> x = {:ok, 10}
iex> y = {:ok, 33}
iex> swith ok(res) <- x,
...>       ok(res2) <- y do
...>      "We have: #{res} #{res2}"
...>    else
...>      _ -> "Failure"
...>  end
"We have: 10 33"

You can also pass arguments to ok(), error() or okerror() which will then be bound and available to be used inside the rest of the swith-expression:

iex> x = {:ok, 10}
iex> y = {:error, 33}
iex> z = {:ok, %{a: 42}}
iex> swith ok(res) <- x,
...>       error(res2) <- y,
...>       okerror(tag, metamap) <- z,
...>       %{a: val} = metamap do
...>         "We have: #{res} #{res2} #{tag} #{val}"
...>   else
...>       _ -> "Failure"
...>   end
"We have: 10 33 ok 42"

Note that for ok() and error(), the first argument will match the first element after the :ok or :error tag. On the other hand, for okerror(), the first argument will match the tag :ok or :error.

Note: It is not required to import Solution to use the macros inside swith without prefixing them.