Bunch v1.0.0 Bunch View Source

A bunch of general-purpose helper and convenience functions.

Link to this section Summary

Functions

Imports a bunch of Bunch macros: withl/1, withl/2, ~>/2, ~>>/2, quote_expr/1, quote_expr/2

Returns an :error tuple if given value is nil and :ok tuple otherwise

Embeds the argument in a one-element list if it is not a list itself. Otherwise works as identity

Works like quote/2, but doesn't require a do/end block and options are passed as the last argument

Returns given stateful try value along with its status

Works like withl/2, but allows shorter syntax

A labeled version of the with macro

Helper for writing pipeline-like syntax. Maps given value using match clauses or lambda-like syntax

Works similar to ~>/2, but accepts only -> clauses and appends default identity clause at the end

Link to this section Functions

Link to this macro

__using__(args) View Source (macro)

Imports a bunch of Bunch macros: withl/1, withl/2, ~>/2, ~>>/2, quote_expr/1, quote_expr/2

Link to this function

error_if_nil(v, reason) View Source
error_if_nil(value, reason) :: Bunch.Type.try_t(value)
when value: any(), reason: any()

Returns an :error tuple if given value is nil and :ok tuple otherwise.

Examples

iex> map = %{:answer => 42}
iex> Bunch.error_if_nil(map[:answer], :reason)
{:ok, 42}
iex> Bunch.error_if_nil(map[:invalid], :reason)
{:error, :reason}
Link to this function

listify(list) View Source
listify(l) :: l when l: list()
listify(a) :: [a] when a: any()

Embeds the argument in a one-element list if it is not a list itself. Otherwise works as identity.

Works similarly to List.wrap/1, but treats nil as any non-list value, instead of returning empty list in this case.

Examples

iex> Bunch.listify(:a)
[:a]
iex> Bunch.listify([:a, :b, :c])
[:a, :b, :c]
iex> Bunch.listify(nil)
[nil]
Link to this macro

quote_expr(code, opts \\ []) View Source (macro)

Works like quote/2, but doesn't require a do/end block and options are passed as the last argument.

Useful when quoting a single expression.

Examples

iex> use Bunch
iex> quote_expr(String.t())
quote do String.t() end
iex> quote_expr(unquote(x) + 2, unquote: false)
quote unquote: false do unquote(x) + 2 end

Nesting

Nesting calls to quote disables unquoting in the inner call, while placing quote_expr in quote or another quote_expr does not:

iex> use Bunch
iex> quote do quote do unquote(:code) end end == quote do quote do :code end end
false
iex> quote do quote_expr(unquote(:code)) end == quote do quote_expr(:code) end
true
Link to this function

stateful_try_with_status(res) View Source
stateful_try_with_status(result) :: {status, result}
when status: Bunch.Type.try_t(),
     result:
       Bunch.Type.stateful_try_t(state :: any())
       | Bunch.Type.stateful_try_t(value :: any(), state :: any())

Returns given stateful try value along with its status.

Link to this macro

withl(keyword) View Source (macro)
withl(
  keyword :: [
    {key :: atom(), with_clause :: term()}
    | {:do, code_block :: term()}
    | {:else, match_clauses :: term()}
  ]
) :: term()

Works like withl/2, but allows shorter syntax.

Examples

iex> use Bunch
iex> x = 1
iex> y = 2
iex> withl a: true <- x > 0,
...>       b: false <- y |> rem(2) == 0,
...>       do: {x, y},
...>       else: (a: false -> {:error, :x}; b: true -> {:error, :y})
{:error, :y}

For more details and more verbose and readable syntax, check docs for withl/2.

Link to this macro

withl(with_clauses, list) View Source (macro)
withl(keyword(with_clause :: term())) do
  code_block :: term()
else
  match_clauses :: term()
end :: term()

A labeled version of the with macro.

Helps to determine in else block which with clause did not match. Therefore else block is always required. Due to the Elixir syntax requirements, all clauses have to be labeled.

Labels also make it possible to access results of already succeeded matches from else clauses. That is why labels have to be known at the compile time.

There should be at least one clause in the else block for each label that corresponds to a <- clause.

Duplicate labels are allowed.

Examples

iex> use Bunch
iex> list = [-1, 3, 2]
iex> binary = <<1,2>>
iex> withl max: i when i > 0 <- list |> Enum.max(),
...>       bin: <<b::binary-size(i), _::binary>> <- binary do
...>   {list, b}
...> else
...>   max: i -> {:error, :invalid_maximum, i}
...>   bin: b -> {:error, :binary_too_short, b, i}
...> end
{:error, :binary_too_short, <<1,2>>, 3}
Link to this macro

expr ~> mapper View Source (macro)

Helper for writing pipeline-like syntax. Maps given value using match clauses or lambda-like syntax.

Examples

iex> use Bunch
iex> {:ok, 10} ~> ({:ok, x} -> x)
10
iex> 5 ~> &1 + 2
7

Lambda-like expressions are not converted to lambdas under the hood, but result of expr is injected to &1 at the compile time.

Useful especially when dealing with a pipeline of operations (made up e.g. with pipe (|>) operator) some of which are hard to express in such form:

iex> use Bunch
iex> ["Joe", "truck", "jacket"]
...> |> Enum.map(&String.downcase/1)
...> |> Enum.filter(& &1 |> String.starts_with?("j"))
...> ~> ["Words:" | &1]
...> |> Enum.join("\n")
"Words:
joe
jacket"
Link to this macro

expr ~>> mapper_clauses View Source (macro)

Works similar to ~>/2, but accepts only -> clauses and appends default identity clause at the end.

Examples

iex> use Bunch
iex> {:ok, 10} ~>> ({:ok, x} -> {:ok, x+1})
{:ok, 11}
iex> :error ~>> ({:ok, x} -> {:ok, x+1})
:error