defmodule Dreamy do
@moduledoc """
Dreamy provides useful macros, functions, types & operators to make elixir even dreamier 😴
"""
defmacro __using__(_) do
quote do
alias Dreamy.{Defaults, Either, Option, Result, Types}
import Dreamy.{Defaults, Either, Monodic, Option, Result}
import Dreamy,
only: [
fallthrough: 2,
otherwise: 3,
or_else: 2,
const: 2,
>>>: 2
]
end
end
defmodule Types do
@moduledoc "Useful Type definitions"
@typedoc "Type union of `t` & `nil`"
@type nullable(t) :: t | nil
@typedoc "Type for documenting the value within an enumerable"
@type enumerable(_t) :: Enumerable.t()
@typedoc "Monodic type that can hold a value"
@type option(t) :: Dreamy.Option.t(t)
@typedoc "Monodic type for :ok, :error tuples"
@type result(ok, err) :: Dreamy.Result.t(ok, err)
@typedoc "Monodic type representing a left or right tuple"
@type either(l, r) :: Dreamy.Either.t(l, r)
end
@doc """
Macro for adding a default catchall -> clause to case statements, that returns the input value
## Examples
```
iex> use Dreamy
...> fallthrough {:error, "error"} do
...> {:ok, v} -> v
...> end
{:error, "error"}
iex> use Dreamy
...> fallthrough {:ok, "OK"} do
...> {:ok, "OK"} -> "OK"
...> end
"OK"
```
"""
defmacro fallthrough(val, do: code) do
code =
code ++
[
{:->, [], [[{:other, [], nil}], {:other, [], nil}]}
]
quote do
case unquote(val) do
unquote(code)
end
end
end
@doc """
Macro for adding a default catchall -> clause to case statements, that returns the default value
## Examples
```
iex> use Dreamy
...> otherwise nil, {:error, :not_found} do
...> {:ok, _} -> :ok
...> end
{:error, :not_found}
iex> use Dreamy
...> otherwise {:ok, nil}, {:error, :not_found} do
...> {:ok, _} -> :ok
...> end
:ok
```
"""
defmacro otherwise(val, default, do: code) do
code =
code ++
[
{:->, [], [[{:_, [], nil}], default]}
]
quote do
case unquote(val) do
unquote(code)
end
end
end
@doc """
Macro for adding a default catchall `true` clause to cond statements, that returns the default value
## Examples
```
iex> use Dreamy
...> x = 5
...> or_else "Less than 10" do
...> x == 10 -> "10"
...> x > 10 -> "Greater than 10"
...> end
"Less than 10"
iex> use Dreamy
...> x = 11
...> or_else "Less than 10" do
...> x == 10 -> "10"
...> x > 10 -> "Greater than 10"
...> end
"Greater than 10"
```
"""
defmacro or_else(default, do: code) do
code =
code ++
[
{:->, [], [[true], default]}
]
quote do
cond do
unquote(code)
end
end
end
@doc """
Macro for defining a constant attribute and function
## Examples
```
iex> use Dreamy
iex> const :example, "XYZ"
iex> @example
"XYZ"
iex> example()
"XYZ"
```
"""
defmacro const(name, code) do
caller = __CALLER__.module
Module.register_attribute(caller, name, accumulate: false)
Module.eval_quoted(
caller,
quote do
Module.put_attribute(__MODULE__, unquote(name), unquote(code))
def unquote(name)(), do: unquote(code)
end
)
end
@doc """
Operator for Enum.map
## Examples
```
iex> use Dreamy
...> x = fn y -> y + 1 end
...> y = fn z -> z * 2 end
...> [1, 2]
...> >>> x
...> >>> y
[4, 6]
iex> use Dreamy
...> x = fn y -> y - 1 end
...> [5, 7]
...> >>> x
...> >>> (&div(&1, 2))
...> >>> x
[1, 2]
```
"""
defmacro enumerable >>> func do
quote do
Enum.map(unquote(enumerable), unquote(func))
end
end
end