Packages

Haskell like `where` sugar for Elixir. A pipe-able `with` special form.

Current section

Files

Jump to
having lib having.ex
Raw

lib/having.ex

defmodule Having do
@moduledoc ~S"""
"""
@doc ~S"""
iex> import Having
iex> having({a, b}, a: 1, b: 2)
{1, 2}
iex> import Having
iex> {a, b} |> having(a: 1, b: 2)
{1, 2}
iex> import Having
iex> a + b
...> |> having(b: 3, a: b * b)
12
iex> import Having
iex> {a, b}
...> |> having do
...> a <- 1
...> b <- 2
...> end
{1, 2}
iex> import Having
iex> {a, b}
...> |> having do
...> {:ok, a} <- 1
...> b <- 2
...> else
...> _ -> :nop
...> end
:nop
iex> import Having
iex> {a, b}
...> |> having(a <- b * 2)
...> |> having(b = 10)
{20, 10}
iex> import Having
iex> {a, b}
...> |> having do
...> a when a > 10 <- b
...> else
...> _ -> {:error, b}
...> end
...> |> having(b = 10)
{:error, 10}
iex> import Having
iex> {a, b}
...> |> having(a when a > 10 <- b, {:error, b})
...> |> having(b = 10)
{:error, 10}
"""
defmacro having(expr, options)
defmacro having(expr, opts) do
opts = Keyword.keyword?(opts) && opts || [do: opts]
bindings = opts |> Keyword.drop([:do, :else]) |> kw_to_bindings
bindings =
Keyword.get(opts, :do)
|> case do
nil -> bindings
{:__block__, _, does} -> bindings ++ does
does -> bindings ++ List.wrap(does)
end
elses = Keyword.get(opts, :else)
{:with, [], bindings ++ [[do: expr]] ++ [elses && [else: elses] || []]}
end
@doc false
defmacro having(expr, binding = {:<-, _, _}, else_expr) do
quote do
with(unquote(binding)) do
unquote(expr)
else
_ -> unquote(else_expr)
end
end
end
defp kw_to_bindings(kw) do
Enum.map kw, fn {key, value} ->
{:=, [], [{key, [], nil}, value]}
end
end
end