View Source Exp (exp v1.0.1)

Execute and inline expressions at compile time.

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

@spec abs(Macro.t()) :: Macro.t()

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__)
@spec inline(Macro.t()) :: Macro.t()

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__)
Link to this macro

maybe_inline(block)

View Source (macro)
@spec maybe_inline(Macro.t()) :: Macro.t()

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
Link to this function

pure_func?(module, function, arity)

View Source
@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
@spec safe_node?(Macro.t()) :: boolean()

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