Packages

plymio_funcio: Function Utilities

Current section

Files

Jump to
plymio_funcio lib funcio predicate predicate.ex
Raw

lib/funcio/predicate/predicate.ex

defmodule Plymio.Funcio.Predicate do
@moduledoc ~S"""
Predicate Functions.
See `Plymio.Funcio` for an overview and documentation terms.
"""
use Plymio.Funcio.Attribute
@type key :: Plymio.Funcio.key()
@type keys :: Plymio.Funcio.keys()
@type error :: Plymio.Funcio.error()
@type stream :: Plymio.Funcio.stream()
@type fun1_predicate :: Plymio.Funcio.fun1_predicate()
import Plymio.Fontais.Utility,
only: [
list_wrap_flat_just: 1
]
import Plymio.Funcio.Predicate.Utility,
only: [
validate_predicate1_fun: 1,
validate_predicate1_funs: 1
]
@doc ~S"""
`reduce_and_predicate1_funs/1` validates the argument is a one or
more *predicate/1 functions* and reduces them to a single
*predicate/1 function*, returning `{:ok, predicate}`.
Multiple predicates are `AND`-ed together to create a composite
predicate will return `true` only if **all** of the individual
predicates return `true`.
An `AND`-ed predicate can be used with e.g. `Enum.filter/2` or `Enum.reject/2`.
## Examples
iex> predicate1 = fn v -> is_integer(v) end
...> {:ok, and_predicate} = predicate1 |> reduce_and_predicate1_funs
...> false = :not_an_integer |> and_predicate.()
...> false = 3.14 |> and_predicate.()
...> true = 42 |> and_predicate.()
true
iex> predicates = [
...> fn v -> is_integer(v) end,
...> fn v -> v >= 5 end,
...> fn v -> v < 10 end,
...> ]
...> {:ok, and_predicate} = predicates |> reduce_and_predicate1_funs
...> false = :not_an_integer |> and_predicate.()
...> false = 3.14 |> and_predicate.()
...> false = 4 |> and_predicate.()
...> false = 10 |> and_predicate.()
...> true = 5 |> and_predicate.()
...> true = 6 .. 9 |> Enum.all?(and_predicate)
true
iex> predicates = [fn v -> is_integer(v) end, fn _k,v -> v end]
...> {:error, error} = predicates |> reduce_and_predicate1_funs
...> error |> Exception.message |> String.starts_with?("predicate/1 function invalid")
true
"""
@since "0.1.0"
@spec reduce_and_predicate1_funs(any) :: {:ok, fun1_predicate} | {:error, error}
def reduce_and_predicate1_funs(funs)
def reduce_and_predicate1_funs(funs) when is_list(funs) do
with {:ok, funs} <- funs |> list_wrap_flat_just |> validate_predicate1_funs do
funs
|> length
|> case do
1 ->
{:ok, funs |> hd}
_ ->
fun = fn v ->
funs |> Enum.all?(fn f -> v |> f.() end)
end
{:ok, fun}
end
else
{:error, %{__exception__: true}} = result -> result
end
end
def reduce_and_predicate1_funs(fun) do
fun |> validate_predicate1_fun
end
@doc ~S"""
`reduce_or_predicate1_funs/1` validates the argument is a one or
more *predicate/1 functions* and reduces them to a single
*predicate/1 function*, returning `{:ok, predicate}`.
Multiple predicates are `OR`-ed together to create a composite
predicate will return `true` if **any** of the individual predicates
return `true`.
An `OR`-ed predicate can be used with e.g. `Enum.filter/2`.
When using a composite `OR` predicate with e.g. `Enum.reject/2`, it
should be remembered that if **any** of individual predicates
returns `true`, the composite predicate will return `true` and the
reject "fail".
## Examples
iex> predicate1 = fn v -> is_integer(v) end
...> {:ok, or_predicate} = predicate1 |> reduce_or_predicate1_funs
...> false = :not_an_integer |> or_predicate.()
...> false = 3.14 |> or_predicate.()
...> true = 42 |> or_predicate.()
true
iex> predicates = [
...> fn v -> is_atom(v) end,
...> fn v -> is_binary(v) end,
...> fn v -> is_integer(v) && v >= 5 end,
...> fn v -> is_integer(v) && v < 10 end,
...> ]
...> {:ok, or_predicate} = predicates |> reduce_or_predicate1_funs
...> true = :not_an_integer |> or_predicate.()
...> true = "Hello World" |> or_predicate.()
...> false = 3.14 |> or_predicate.()
...> true = 4 |> or_predicate.()
...> true = 10 |> or_predicate.()
...> true = 5 |> or_predicate.()
...> true = 6 .. 9 |> Enum.any?(or_predicate)
true
iex> predicates = [fn v -> is_integer(v) end, fn _k,v -> v end]
...> {:error, error} = predicates |> reduce_or_predicate1_funs
...> error |> Exception.message |> String.starts_with?("predicate/1 function invalid")
true
"""
@since "0.1.0"
@spec reduce_or_predicate1_funs(any) :: {:ok, fun1_predicate} | {:error, error}
def reduce_or_predicate1_funs(funs)
def reduce_or_predicate1_funs(funs) when is_list(funs) do
with {:ok, funs} <- funs |> List.wrap() |> validate_predicate1_funs do
funs
|> length
|> case do
1 ->
{:ok, funs |> hd}
_ ->
fun = fn v ->
funs |> Enum.any?(fn f -> v |> f.() end)
end
{:ok, fun}
end
else
{:error, %{__exception__: true}} = result -> result
end
end
def reduce_or_predicate1_funs(fun) do
fun |> validate_predicate1_fun
end
end