defmodule Swiss do
@moduledoc """
# Swiss
Swiss is a bundle of extensions to the standard lib. It includes several
helper functions for dealing with standard types.
## API
The root module has generic helper functions; check each sub-module's docs for
each type's API.
"""
@doc """
More idiomatic `!is_nil/1`. Defined as a macro so it can be used in guards.
### Examples
iex> Swiss.is_present(nil)
false
iex> Swiss.is_present([])
true
iex> Swiss.is_present(42)
true
"""
defmacro is_present(val) do
quote do
not is_nil(unquote(val))
end
end
@doc """
Applies the given `func` to `value` and returns its result.
### Examples
iex> Swiss.thru(42, &(12 + &1))
54
"""
@spec thru(value :: any(), func :: function()) :: any()
def thru(value, func), do: func.(value)
@doc """
Applies the given `func` to `value` and returns value.
### Examples
iex> Swiss.tap(42, &(12 + &1))
42
"""
@spec tap(value :: any(), func :: function()) :: any()
def tap(value, func) do
func.(value)
value
end
@doc """
Applies the given `apply_fn` to the given `value` if the given `predicate_fn`
returns true.
By default, `predicate_fn` is `is_present/1`.
### Examples
iex> Swiss.apply_if(42, &(&1 + 8))
50
iex> Swiss.apply_if(42, &(&1 + 8), &(&1 > 40))
50
iex> Swiss.apply_if(42, &(&1 + 8), &(&1 < 40))
42
iex> Swiss.apply_if(42, &(&1 + 8), true)
50
iex> Swiss.apply_if(42, &(&1 + 8), false)
42
"""
@spec apply_if(
value :: any(),
apply_fn :: (any() -> any()),
predicate_fn :: (any() -> boolean())
) :: any()
def apply_if(val, apply_fn, predicate_fn \\ &is_present/1)
def apply_if(val, apply_fn, predicate_fn) when is_function(predicate_fn, 1),
do: apply_if(val, apply_fn, predicate_fn.(val))
def apply_if(val, apply_fn, condition) when is_boolean(condition) do
if condition,
do: apply_fn.(val),
else: val
end
@doc """
Applies the given `apply_fn` to the given `value` unless the given
`predicate_fn` returns true.
By default, `predicate_fn` is `is_nil/1`.
### Examples
iex> Swiss.apply_unless(nil, &(&1 + 8))
nil
iex> Swiss.apply_unless(42, &(&1 + 8))
50
iex> Swiss.apply_unless(42, &(&1 + 8), &(&1 > 40))
42
iex> Swiss.apply_unless(42, &(&1 + 8), &(&1 < 40))
50
iex> Swiss.apply_unless(42, &(&1 + 8), false)
50
iex> Swiss.apply_unless(42, &(&1 + 8), true)
42
"""
@spec apply_unless(
value :: any(),
apply_fn :: (any() -> any()),
predicate_fn :: (any() -> boolean())
) :: any()
def apply_unless(val, apply_fn, predicate_fn \\ &is_nil/1)
def apply_unless(val, apply_fn, predicate_fn) when is_function(predicate_fn, 1),
do: apply_unless(val, apply_fn, predicate_fn.(val))
def apply_unless(val, apply_fn, condition) when is_boolean(condition) do
if condition,
do: val,
else: apply_fn.(val)
end
@doc """
Wrapper that makes any function usable directly in `Kernel.get_in/2`.
### Examples
iex> get_in([%{"life" => 42}], [Swiss.nextable(&List.first/1), "life"])
42
"""
def nextable(fun) do
fn :get, el, next ->
next.(fun.(el))
end
end
end