Current section
Files
Jump to
Current section
Files
lib/operator.ex
defmodule Operator do
@moduledoc ~S"""
Helpers for defining operator aliases for functions
Operators can be hard to follow, especially with the limited number available
in Elixir. Always having a named function backing an operator makes it easy to
fall back to the named version. Named fall backs are also very useful for
piping (`|>`).
## Examples
defmodule Example do
use Operator
@doc "Divide two numbers"
@operator :~>
def divide(a, b), do: a / b
@doc "Multiply two numbers"
@operator :<~>
def multiply(a, b), do: a * b
end
import Example
divide(10, 5)
#=> 5
10 ~> 2
#=> 5
multiply(10, 2)
#=> 20
10 <~> 2
#=> 20
"""
defmacro __using__(_) do
quote do
import Kernel, except: [def: 2]
require unquote(__MODULE__)
import unquote(__MODULE__)
Module.register_attribute __MODULE__, :operator, []
end
end
alias __MODULE__
@doc ~S"""
Modified `def` that applies the most recent `@operator`
## Examples
@operator x <~> y
# ...
def add(a, b), do: a + b
add(1, 2)
#=> 3
1 <~> 2
#=> 3
"""
defmacro def(head, expr \\ nil) do
{fun_name, ctx, args} = head
quote do
# This is SO STUPID. Just brute forcing the problem for now. Need to revisit.
# The problem is that passing Operator.get to defalias (or defdelegate)
# tried to insert the *literal* Operator.get, rather than evaluating it first.
# Same happens with anonymous functions.
case Operator.get do
:& -> defalias(unquote(fun_name), unquote(ctx), unquote(args), as: :&)
:&& -> defalias(unquote(fun_name), unquote(ctx), unquote(args), as: :&&)
:&&& -> defalias(unquote(fun_name), unquote(ctx), unquote(args), as: :&&&)
:<- -> defalias(unquote(fun_name), unquote(ctx), unquote(args), as: :<-)
::: -> defalias(unquote(fun_name), unquote(ctx), unquote(args), as: :::)
:== -> defalias(unquote(fun_name), unquote(ctx), unquote(args), as: :==)
:!= -> defalias(unquote(fun_name), unquote(ctx), unquote(args), as: :!=)
:=== -> defalias(unquote(fun_name), unquote(ctx), unquote(args), as: :===)
:!== -> defalias(unquote(fun_name), unquote(ctx), unquote(args), as: :!==)
:| -> defalias(unquote(fun_name), unquote(ctx), unquote(args), as: :|)
:|| -> defalias(unquote(fun_name), unquote(ctx), unquote(args), as: :||)
:||| -> defalias(unquote(fun_name), unquote(ctx), unquote(args), as: :|||)
:> -> defalias(unquote(fun_name), unquote(ctx), unquote(args), as: :>)
:< -> defalias(unquote(fun_name), unquote(ctx), unquote(args), as: :<)
:<= -> defalias(unquote(fun_name), unquote(ctx), unquote(args), as: :<=)
:>= -> defalias(unquote(fun_name), unquote(ctx), unquote(args), as: :>=)
:|> -> defalias(unquote(fun_name), unquote(ctx), unquote(args), as: :|>)
:~> -> defalias(unquote(fun_name), unquote(ctx), unquote(args), as: :~>)
:~>> -> defalias(unquote(fun_name), unquote(ctx), unquote(args), as: :~>>)
:>>> -> defalias(unquote(fun_name), unquote(ctx), unquote(args), as: :>>>)
:<~ -> defalias(unquote(fun_name), unquote(ctx), unquote(args), as: :<~)
:<<~ -> defalias(unquote(fun_name), unquote(ctx), unquote(args), as: :<<~)
:<<< -> defalias(unquote(fun_name), unquote(ctx), unquote(args), as: :<<<)
:<~> -> defalias(unquote(fun_name), unquote(ctx), unquote(args), as: :<~>)
:<|> -> defalias(unquote(fun_name), unquote(ctx), unquote(args), as: :<|>)
:^^^ -> defalias(unquote(fun_name), unquote(ctx), unquote(args), as: :^^^)
:<> -> defalias(unquote(fun_name), unquote(ctx), unquote(args), as: :<>)
:+ -> defalias(unquote(fun_name), unquote(ctx), unquote(args), as: :+)
:++ -> defalias(unquote(fun_name), unquote(ctx), unquote(args), as: :++)
:- -> defalias(unquote(fun_name), unquote(ctx), unquote(args), as: :-)
:-- -> defalias(unquote(fun_name), unquote(ctx), unquote(args), as: :--)
:* -> defalias(unquote(fun_name), unquote(ctx), unquote(args), as: :*)
:/ -> defalias(unquote(fun_name), unquote(ctx), unquote(args), as: :/)
:! -> defalias(unquote(fun_name), unquote(ctx), unquote(args), as: :!)
:^ -> defalias(unquote(fun_name), unquote(ctx), unquote(args), as: :^)
:~~~ -> defalias(unquote(fun_name), unquote(ctx), unquote(args), as: :~~~)
:. -> defalias(unquote(fun_name), unquote(ctx), unquote(args), as: :.)
:.. -> defalias(unquote(fun_name), unquote(ctx), unquote(args), as: :..)
:@ -> defalias(unquote(fun_name), unquote(ctx), unquote(args), as: :@)
end
Kernel.def(unquote(head), unquote(expr))
Operator.unset
end
end
@doc ~S"""
Define an operator alias for a named function
## Examples
defalias(max, 2, as: :<|>)
10 <|> 8
#=> 10
"""
defmacro defalias(_, _, _, as: nil), do: :ok
defmacro defalias(fun_name, ctx, args, as: operator) do
quote do
defdelegate(unquote({operator, ctx, args}), to: __MODULE__, as: unquote(fun_name))
end
end
@spec get() :: nil | atom
defmacro get do
quote do: Module.get_attribute(__MODULE__, :operator)
end
@spec unset() :: no_return
defmacro unset do
quote do: Module.put_attribute(__MODULE__, :operator, nil)
end
end