View Source Exp (exp v1.0.1)
Execute and inline expressions at compile time.
- In a regular code, all you need is
Exp.inline/1
. - For usage inside of macros when you don't know if arguments are safe,
see
Exp.maybe_inline/1
. Exp.abs/1
is a real-world example of usingExp.maybe_inline/1
.Exp.pure_func?/3
andExp.safe_node?/1
are utility function thatExp.maybe_inline/1
uses to make decision if expression is safe to inline.
Source: github.com/orsinium-labs/exp
Link to this section Summary
Functions
Inlined version of Kernel.abs/1
.
Macro to statically execute and inline an expression at compile time.
A safe implementation of inline/1
for macros.
Check if the function of given name and arity is pure.
Check if the function of given module, name, and arity is pure.
Check if the given AST node is safe to be statically executed.
Link to this section Functions
Inlined version of Kernel.abs/1
.
Returns the arithmetical absolute value of the number
.
examples
Examples
iex> Exp.abs(-2)
2
iex> Exp.abs(2)
2
This is how you can check if it was inlined:
iex> # inlined:
iex> q = quote do: Exp.abs(-2)
iex> 2 = Macro.expand(q, __ENV__)
iex> # not inlined because unsafe:
iex> q = quote do: Exp.abs(node())
iex> {:abs, _, _} = Macro.expand(q, __ENV__)
Macro to statically execute and inline an expression at compile time.
If the expression cannot be inlined, it will explode at compile time. This is your responsibility to make sure the code can be inlined and is safe to inline.
what-to-inline
What to inline
There are requirements for a good candidate to be inlined:
- It doesn't make network requests.
- It doesn't depend on a service or ecto.
- For the same input, it always produces the same output.
- It's not too slow.
- Its result doesn't take too much memory.
A good example of such function is Regex.compile!/2
.
examples
Examples
The usage is straightforward, just wrap whatever you want to inline:
iex> require Exp
iex> Exp.inline(1 + 2)
3
The difference is that the expression inside will be executed at compile time (when expanding the AST) and included into the compiled code:
iex(21)> q = quote do: 1 + 2
iex(22)> {:+, _, [1, 2]} = Macro.expand(q, __ENV__)
iex(23)> q = quote do: Exp.inline(1 + 2)
iex(24)> 3 = Macro.expand(q, __ENV__)
A safe implementation of inline/1
for macros.
It inlines the given quoted expression if and only if all unquote
arguments
are safe to execute at compile time. The safety of the expression itself isn't checked.
The result is also a quoted expression.
It's supposed to be used from macros when you don't know if the macros will be used for a safe to execute code or not.
examples
Examples
Here, var
is safe and so the expression is inlined:
iex> var = quote do: 13
iex> 13 = Exp.maybe_inline(quote do: abs(unquote(var)))
Here, var
is not safe and so the expression is left as-is:
iex> var = quote do: node()
iex> {:abs, _, _} = Exp.maybe_inline(quote do: abs(unquote(var)))
Here, the expression isn't safe but it's still inlined because maybe_inline/2
checks only safety of expressions inside Kernel.SpecialForms.unquote/1
:
iex> Exp.maybe_inline(quote do: node())
:nonode@nohost
See also Exp.abs/1
for a real-world usage example.
@spec pure_func?(atom(), non_neg_integer()) :: boolean()
Check if the function of given name and arity is pure.
examples
Examples
iex> Exp.pure_func?(:abs, 1)
true
iex> Exp.pure_func?(:abs, 2) # bad arity
false
iex> Exp.pure_func?(:send, 2)
false
@spec pure_func?(atom(), atom(), non_neg_integer()) :: boolean()
Check if the function of given module, name, and arity is pure.
examples
Examples
iex> Exp.pure_func?(String, :upcase, 1)
true
iex> Exp.pure_func?(String, :upcase, 4) # bad arity
false
iex> Exp.pure_func?(File, :cwd, 0)
false
iex> Exp.pure_func?(Kernel, :abs, 1)
true
iex> Exp.pure_func?(:erlang, :abs, 1)
true
Check if the given AST node is safe to be statically executed.
This check is conservative. If it doesn't know anything about the function,
the result is false
.
It's used be Exp.maybe_inline/1
to make a decision if the code should be inlined.
examples
Examples
iex> Exp.safe_node?(quote do: 1)
true
iex> Exp.safe_node?(quote do: "hello")
true
iex> Exp.safe_node?(quote do: 1+2)
true
iex> Exp.safe_node?(quote do: div(2, 3))
true
iex> Exp.safe_node?(quote do: File.cwd())
false