pipet v0.1.4 Pipet View Source

Macro for conditionally piping a value through a series of expressions.

Prior art

Pipet was heavily inspired by, and would not exist without:

Link to this section Summary

Functions

Conditionally pipe a value through a series of operations

Link to this section Functions

Link to this macro pipet(subject, list) View Source (macro)
pipet(Macro.t, [{:do, [Macro.t]}]) :: Macro.t

Conditionally pipe a value through a series of operations.

pipes is a series of conditional expressions. At each step of the way, if the condition succeeds, then the value will be applied as the first argument of the last expression of the body of the condition as in |>, and if the condition fails the expression will be skipped. The supported forms of conditional expressions are:

  • if
  • unless
  • cond
  • case
  • with

Raw function calls are also supported, in which case the value is piped through just like in |>

Examples

Basic if conditions are supported:

pipet [1, 2, 3] do
  if do_increment?(), do: Enum.map(& &1 + 1)
  if do_double?(),    do: Enum.map(& &1 * 1)
  if do_string?(),    do: Enum.map(&to_string/1)
end

case and cond use whichever branch succeeds:

pipet [1, 2, 3] do
  case operation do
    :increment -> Enum.map(& &1 + 1)
    :double    -> map(& &1 * 1)
    :string    -> Enum.map(&to_string/1)
  end
end

with pipes through the do block if all pattern matches succeed, or uses the first successful else block if a pattern fails.

pipet [1, 2, 3] do
  with {:ok, x} <- get_value() do
    Enum.map(& &1 + x)
  else
    :something_else -> Enum.map(& &1 + 1)
  end
end

Conditional expressions can be combined with bare function calls, which will always execute:

pipet ["1", "2", "3"] do
  String.to_integer()
  if do_increment?(), do: Enum.map(& &1 + 1)
end

Multi-expression bodies pipe through the last expression:

pipet [1, 2, 3] do
  if do_add_num?() do
    num = 3
    Enum.map(& &1 + num)
  end

  case something() do
    {:ok, x} ->
      x = x + 1
      Enum.map(& &1 + x)

    :error ->
      Enum.map(& &1 - 2)
  end
end

Notes

  • pipet evaluates conditions in order, not all at once. For example, the following:

    def print_hello_and_return_true() do IO.puts “hello” true end

    pipet 1 do if print_hello_and_return_true() do

    IO.puts "world"
    increment()

    end unless print_hello_and_return_true() do

    IO.puts "goodbye"

    end end

    prints:

    hello world hello

  • the rules for case and with are as usual - if none of the branches of a case block or of an else block in a with statement match a CaseClauseError or WithClauseError respectively will be thrown. If you want a fallthrough case, you can provide a call to the identity function:

    pipet 1 do case {:foo, :bar} do

    :never_matches -> increment()
    _ -> (& &1).()

    end end

    pipet 1 do with {:ok, x} <- :some_tuple do

    increment()

    else

    :doesnt_match -> increment()
    _ -> (& &1).()

    end end