Files
n8n-openai-adapter/lib/elixir/lib/kernel/special_forms.ex
T
Rafael Mendonça França 7cdc25538e Uniform use of whitespace after commas on codebase
This make easier to read and also follow some standard in the code
2013-06-15 16:52:48 -03:00

820 lines
23 KiB
Elixir

defmodule Kernel.SpecialForms do
@moduledoc """
In this module we define Elixir special forms. Special forms
cannot be overriden by the developer and are the basic
building blocks of Elixir code.
Some of those forms are lexical (like `alias`, `import`, etc).
The macros `{}`, `[]` and `<<>>` are also special forms used
to define data structures, respectively tuples, lists and binaries.
This module also documents Elixir's pseudo variables (`__MODULE__`,
`__FILE__`, `__ENV__` and `__CALLER__`). Pseudo variables return
information about Elixir's compilation environment and can only
be read, never assigned to.
Finally, it also documents 3 special forms (`__block__`,
`__scope__` and `__aliases__`), which are not intended to be
called directly by the developer but they appear in quoted
contents since they are essential in Elixir's constructions.
"""
@doc """
Defines a new tuple.
## Examples
iex> { 1, 2, 3 }
{ 1, 2, 3 }
"""
defmacro :{}.(args)
@doc """
Defines a new list.
## Examples
iex> [ 1, 2, 3 ]
[ 1, 2, 3 ]
"""
defmacro :[].(args)
@doc """
Defines a new bitstring.
## Examples
iex> << 1, 2, 3 >>
<< 1, 2, 3 >>
## Bitstring types
A bitstring may contain many parts and those may have
specific types. Most of the time, Elixir will figure out
the part's type and won't require any work from you:
iex> <<102, "oo">>
"foo"
Above we have two parts: the first is an integer and the
second is a binary. If we use any other Elixir expression,
Elixir can no longer guess the type:
iex> rest = "oo"
...> <<102, rest>>
** (ArgumentError) argument error
When a variable or expression is given as a binary part,
Elixir defaults the type of that part to an unsigned
little-endian integer. In the example above, since we haven't
specified a type, Elixir expected an integer but we passed a
binary, resulting in `ArgumentError`. We can solve this by
explicitly tagging it as a binary:
<<102, rest :: binary>>
The type can be integer, float, binary, bytes, bitstring,
bits, utf8, utf16 or utf32, e.g.:
<<102 :: float, rest :: binary>>
Integer can be any arbitrary precision integer. A float is an
IEEE 754 binary32 or binary64 floating point number. A bitstring
is an arbitrary series of bits. A binary is a special case of
bitstring that has a total size divisible by 8.
The utf8, utf16, and utf32 types are for UTF code points.
The bits type is an alias for bitstring. The bytes type is an
alias for binary.
The signedness can also be given as signed or unsigned. The
signedness only matters for matching. If unspecified, it
defaults to unsigned. Example:
iex> <<-100 :: signed, _rest :: binary>> = <<-100, "foo">>
<<156,102,111,111>>
This match would have failed if we did not specify that the
value -100 is signed. If we're matching into a variable instead
of a value, the signedness won't be checked; rather, the number
will simply be interpreted as having the given (or implied)
signedness, e.g.:
iex> <<val, _rest :: binary>> = <<-100, "foo">>
...> val
156
Here, `val` is interpreted as unsigned.
Signedness is only relevant on integers.
The endianness of a part can be big, little or native (the
latter meaning it will be resolved at VM load time). Passing
many options can be done by giving a list:
<<102 :: [integer, native], rest :: binary>>
Or:
<<102 :: [unsigned, big, integer], rest :: binary>>
And so on.
Endianness only makes sense for integers and some UTF code
point types (utf16 and utf32).
Finally, we can also specify size and unit for each part. The
unit is multiplied by the size to give the effective size of
the part:
iex> <<102, _rest :: [size(2), unit(8)]>> = "foo"
"foo"
iex> <<102, _rest :: size(16)>> = "foo"
"foo"
iex> <<102, _rest :: size(32)>> = "foo"
** (MatchError) no match of right hand side value: "foo"
In the example above, the first two expressions matches
because the string "foo" takes 24 bits and we are matching
against a part of 24 bits as well, 8 of which are taken by
the integer 102 and the remaining 16 bits are specified on
the rest. On the last example, we expect a rest with size 32,
which won't match.
Size and unit are not applicable to utf8, utf16, and utf32.
The default size for integers is 8. For floats, it is 64. For
binaries, it is the size of the binary. Only the last binary
in a binary match can use the default size (all others must
have their size specified explicitly). Bitstrings do not have
a default size.
Size can also be specified using a syntax shortcut. Instead of
writing `size(8)`, one can write just `8` and it will be interpreted
as `size(8)`
iex> << 1 :: 3 >> == << 1 :: size(3) >>
true
The default unit for integers, floats, and bitstrings is 1. For
binaries, it is 8.
For floats, unit * size must result in 32 or 64, corresponding
to binary32 and binary64, respectively.
"""
defmacro :<<>>.(args)
@doc """
`alias` is used to setup atom aliases, often useful with modules names.
## Examples
`alias` can be used to setup an alias for any module:
defmodule Math do
alias MyKeyword, as: Keyword
end
In the example above, we have set up `MyKeyword` to be alias
as `Keyword`. So now, any reference to `Keyword` will be
automatically replaced by `MyKeyword`.
In case one wants to access the original `Keyword`, it can be done
by accessing Elixir:
Keyword.values #=> uses MyKeyword.values
Elixir.Keyword.values #=> uses Keyword.values
Notice that calling `alias` without the `as:` option automatically
sets an alias based on the last part of the module. For example:
alias Foo.Bar.Baz
Is the same as:
alias Foo.Bar.Baz, as: Baz
## Lexical scope
`import`, `require` and `alias` are called directives and all
have lexical scope. This means you can set up aliases inside
specific functions and it won't affect the overall scope.
"""
defmacro alias(module, opts)
@doc """
`require` is used to require the presence of external
modules so macros can be invoked.
## Examples
Notice that usually modules should not be required before usage,
the only exception is if you want to use the macros from a module.
In such cases, you need to explicitly require them.
Let's suppose you created your own `if` implementation in the module
`MyMacros`. If you want to invoke it, you need to first explicitly
require the `MyMacros`:
defmodule Math do
require MyMacros
MyMacros.if do_something, it_works
end
An attempt to call a macro that was not loaded will raise an error.
## Alias shortcut
`require` also accepts `as:` as an option so it automatically sets
up an alias. Please check `alias` for more information.
"""
defmacro require(module, opts)
@doc """
`import` allows one to easily access functions or macros from
others modules without using the qualified name.
## Examples
If you are using several functions from a given module, you can
import those functions and reference them as local functions,
for example:
iex> import List
...> flatten([1, [2], 3])
[1,2,3]
## Selector
By default, Elixir imports functions and macros from the given
module, except the ones starting with underscore (which are
usually callbacks):
import List
A developer can change this behavior to include all macros and
functions, regardless if it starts with underscore, by passing
`:all` as first argument:
import :all, List
It can also be customized to import only all functions or
all macros:
import :functions, List
import :macros, List
Alternatively, Elixir allows a developer to specify `:only`
or `:except` as a fine grained control on what to import (or
not):
import List, only: [flatten: 1]
## Lexical scope
It is important to notice that `import` is lexical. This means you
can import specific macros inside specific functions:
defmodule Math do
def some_function do
# 1) Disable `if/2` from Kernel
import Kernel, except: [if: 2]
# 2) Require the new `if` macro from MyMacros
import MyMacros
# 3) Use the new macro
if do_something, it_works
end
end
In the example above, we imported macros from `MyMacros`,
replacing the original `if/2` implementation by our own
during that specific function. All other functions in that
module will still be able to use the original one.
## Alias/Require shortcut
All imported modules are also required by default. `import`
also accepts `as:` as an option so it automatically sets up
an alias. Please check `alias` for more information.
## Warnings
If you import a module and you don't use any of the imported
functions or macros from this module, Elixir is going to issue
a warning implying the import is not being used.
In case the import is generated automatically by a macro,
Elixir won't emit any warnings though, since the import
was not explicitly defined.
Both warning behaviors could be changed by explicitily
setting the `:warn` option to true or false.
## Ambiguous function/macro names
If two modules `A` and `B` are imported and they both contain
a `foo` function with an arity of `1`, an error is only emitted
if an ambiguous call to `foo/1` is actually made; that is, the
errors are emitted lazily, not eagerly.
"""
defmacro import(module, opts)
@doc """
Returns the current environment information as a `Macro.Env`
record. In the environment you can access the current filename,
line numbers, set up aliases, the current function and others.
"""
defmacro __ENV__
@doc """
Returns the current module name as an atom or nil otherwise.
Although the module can be accessed in the __ENV__, this macro
is a convenient shortcut.
"""
defmacro __MODULE__
@doc """
Returns the current file name as a binary.
Although the file can be accessed in the __ENV__, this macro
is a convenient shortcut.
"""
defmacro __FILE__
@doc """
Returns the current directory as a binary.
"""
defmacro __DIR__
@doc """
Allows you to get the representation of any expression.
## Examples
iex> quote do: sum(1, 2, 3)
{ :sum, [], [1, 2, 3] }
## Explanation
Any Elixir code can be represented using Elixir data structures.
The building block of Elixir macros is a tuple with three elements,
for example:
{ :sum, [], [1, 2, 3] }
The tuple above represents a function call to sum passing 1, 2 and
3 as arguments. The tuple elements are:
* The first element of the tuple is always an atom or
another tuple in the same representation;
* The second element of the tuple represents metadata;
* The third element of the tuple are the arguments for the
function call. The third argument may be an atom, which is
usually a variable (or a local call);
## Options
* `:unquote` - When false, disables unquoting. Useful when you have a quote
inside another quote and want to control which quote is
able to unquote;
* `:location` - When set to `:keep`, keeps the current line and file on quotes.
Read the Stacktrace information section below for more information;
* `:hygiene` - Allows a developer to disable hygiene selectively;
* `:context` - Sets the context resolution happens at;
## Macro literals
Besides the tuple described above, Elixir has a few literals that
when quoted return themselves. They are:
:sum #=> Atoms
1 #=> Integers
2.0 #=> Floats
[1, 2] #=> Lists
"binaries" #=> Binaries
{key, value} #=> Tuple with two elements
## Hygiene and context
Elixir macros are hygienic via means of deferred resolution.
This means variables, aliases and imports defined inside the
quoted refer to the context that defined the macro and not
the context where the macro is expanded.
For this mechanism to work, every quoted code is attached
to a context. Consider the following example:
defmodule ContextSample do
def hello do
quote do: world
end
end
ContextSample.hello
#=> {:world,[],ContextSample}
Notice how the third element of the returned tuple is the
module name. This means that the variable is associated to the
ContextSample module and only code generated by this module
will be able to access that particular `world` variable.
While this means macros from the same module could have
conflicting variables, it also allows different quotes from
the same module to access them.
The context can be disabled or changed by explicitly setting
the context option. All hygiene mechanisms are based on such
context and we are going to explore each of them in the following
subsections.
### Hygiene in variables
Consider the following example:
defmodule Hygiene do
defmacro no_interference do
quote do: a = 1
end
end
require Hygiene
a = 10
Hygiene.no_interference
a #=> 10
In the example above, `a` returns 10 even if the macro
is apparently setting it to 1 because variables defined
in the macro does not affect the context the macro is executed.
If you want to set or get a variable in the user context, you
can do it with the help of the `var!` macro:
defmodule NoHygiene do
defmacro interference do
quote do: var!(a) = 1
end
end
require NoHygiene
a = 10
NoHygiene.interference
a #=> 1
It is important to understand that quoted variables are scoped
to the module they are defined. That said, even if two modules
define the same quoted variable `a`, their values are going
to be independent:
defmodule Hygiene1 do
defmacro var1 do
quote do: a = 1
end
end
defmodule Hygiene2 do
defmacro var2 do
quote do: a = 2
end
end
Calling macros `var1` and `var2` are not going to change their
each other values for `a`. This is useful because quoted
variables from different modules cannot conflict. If you desire
to explicitly access a variable from another module, we can once
again use `var!` macro, but explicitly passing a second argument:
# Access the variable a from Hygiene1
quote do: var!(a, Hygiene1) = 2
Hygiene for variables can be disabled overall as:
quote hygiene: [vars: false], do: x
### Hygiene in aliases
Aliases inside quote are hygienic by default.
Consider the following example:
defmodule Hygiene do
alias HashDict, as: D
defmacro no_interference do
quote do: D.new
end
end
require Hygiene
Hygiene.no_interference #=> #HashDict<[]>
Notice that, even though the alias `D` is not available
in the context the macro is expanded, the code above works
because `D` still expands to `HashDict`.
In some particular cases you may want to access an alias
or a module defined in the caller. In such scenarios, you
can access it by disabling hygiene with `hygiene: [aliases: false]`
or by using the `alias!` macro inside the quote:
defmodule Hygiene do
# This will expand to Elixir.Nested.hello
defmacro no_interference do
quote do: Nested.hello
end
# This will expand to Nested.hello for
# whatever is Nested in the caller
defmacro interference do
quote do: alias!(Nested).hello
end
end
defmodule Parent do
defmodule Nested do
def hello, do: "world"
end
require Hygiene
Hygiene.no_interference
#=> ** (UndefinedFunctionError) ...
Hygiene.interference
#=> "world"
end
## Hygiene in imports
Similar to aliases, imports in Elixir hygienic. Consider the
following code:
defmodule Hygiene do
defmacrop get_size do
quote do
size("hello")
end
end
def return_size do
import Kernel, except: [size: 1]
get_size
end
end
Hygiene.return_size #=> 5
Notice how `return_size` returns 5 even though the `size/1`
function is not imported.
Elixir is smart enough to delay the resolution to the latest
moment possible. So, if you call `size("hello")` inside quote,
but no `size/1` function is available, it is then expanded on
the caller:
defmodule Lazy do
defmacrop get_size do
import Kernel, except: [size: 1]
quote do
size([a: 1, b: 2])
end
end
def return_size do
import Kernel, except: [size: 1]
import Dict, only: [size: 1]
get_size
end
end
Lazy.return_size #=> 2
As in aliases, imports expansion can be explicitly disabled
via the `hygiene: [imports: false]` option.
## Stacktrace information
One of Elixir goals is to provide proper stacktrace whenever there is an
exception. In order to work properly with macros, the default behavior
in quote is to not set a line. When a macro is invoked and the quoted
expressions is expanded, the call site line is inserted.
This is a good behavior for the majority of the cases, except if the macro
is defining new functions. Consider this example:
defmodule MyServer do
use GenServer.Behaviour
end
`GenServer.Behaviour` defines new functions in our `MyServer` module.
However, if there is an exception in any of these functions, we want
the stacktrace to point to the `GenServer.Behaviour` and not the line
that calls `use GenServer.Behaviour`. For this reason, there is an
option called `:location` that when set to `:keep` keeps the original
line and file lines instead of setting them to 0:
quote location: :keep do
def handle_call(request, _from, state) do
{ :reply, :undef, state }
end
end
It is important to warn though that `location: :keep` evaluates the
code as if it was defined inside `GenServer.Behaviour` file, in
particular, the macro `__FILE__` and exceptions happening inside
the quote will always point to `GenServer.Behaviour` file.
"""
defmacro quote(opts, block)
@doc """
When used inside quoting, marks that the variable should
not be hygienized. The argument can be either a variable
node (i.e. a tuple with three elements where the last
one is an atom) or an atom representing the variable name.
Check `quote/2` for more information.
"""
defmacro var!(var)
@doc """
Defines a variable in the given context.
Check `quote/2` for more information.
"""
defmacro var!(var, context)
@doc """
When used inside quoting, marks that the alias should not
be hygienezed. This means the alias will be expanded when
the macro is expanded.
"""
defmacro alias!(alias)
@doc """
Unquotes the given expression from inside a macro.
## Examples
Imagine the situation you have a variable `name` and
you want to inject it inside some quote. The first attempt
would be:
value = 13
quote do: sum(1, value, 3)
Which would then return:
{ :sum, [], [1, { :value, [], quoted }, 3] }
Which is not the expected result. For this, we use unquote:
value = 13
quote do: sum(1, unquote(value), 3)
#=> { :sum, [], [1, 13, 3] }
"""
name = :unquote
defmacro unquote(name)(expr)
@doc """
Unquotes the given list expanding its arguments. Similar
to unquote.
## Examples
values = [2, 3, 4]
quote do: sum(1, unquote_splicing(values), 5)
#=> { :sum, [], [1, 2, 3, 4, 5] }
"""
name = :unquote_splicing
defmacro unquote(name)(expr)
@doc """
List comprehensions allow you to quickly build a list from another list:
iex> lc n inlist [1, 2, 3, 4], do: n * 2
[2,4,6,8]
A comprehension accepts many generators and also filters. Generators
are defined using both `inlist` and `inbits` operators, allowing you
to loop lists and bitstrings:
# A list generator:
iex> lc n inlist [1, 2, 3, 4], do: n * 2
[2,4,6,8]
# A bit string generator:
iex> lc <<n>> inbits <<1, 2, 3, 4>>, do: n * 2
[2,4,6,8]
# A generator from a variable:
iex> list = [1, 2, 3, 4]
...> lc n inlist list, do: n * 2
[2,4,6,8]
# A comprehension with two generators
iex> lc x inlist [1, 2], y inlist [2, 3], do: x*y
[2,3,4,6]
Filters can also be given:
# A comprehension with a generator and a filter
iex> lc n inlist [1, 2, 3, 4, 5, 6], rem(n, 2) == 0, do: n
[2,4,6]
Bit string generators are quite useful when you need to
organize bit string streams:
iex> pixels = <<213, 45, 132, 64, 76, 32, 76, 0, 0, 234, 32, 15>>
iex> lc <<r::8, g::8, b::8>> inbits pixels, do: {r, g, b}
[{213,45,132},{64,76,32},{76,0,0},{234,32,15}]
"""
defmacro lc(args)
@doc """
Defines a bit comprehension. It follows the same syntax as
a list comprehension but expects each element returned to
be a bitstring. For example, here is how to remove all
spaces from a string:
iex> bc <<c>> inbits " hello world ", c != ? , do: <<c>>
"helloworld"
"""
defmacro bc(args)
@doc """
This is the special form used whenever we have a block
of expressions in Elixir. This special form is private
and should not be invoked directly:
iex> quote do: (1; 2; 3)
{ :__block__, [], [1, 2, 3] }
"""
defmacro __block__(args)
@doc """
This is the special form used whenever we have to temporarily
change the scope information of a block. Used when `quote` is
invoked with `location: :keep` to execute a given block as if
it belonged to another file.
quote location: :keep, do: 1
#=> { :__scope__, [line: 1], [[file: "iex"],[do: 1]] }
Check `quote/1` for more information.
"""
defmacro __scope__(opts, args)
@doc """
This is the special form used to hold aliases information.
It is usually compiled to an atom:
quote do: Foo.Bar #=>
{ :__aliases__, [], [:Foo,:Bar] }
Elixir represents `Foo.Bar` as `__aliases__` so calls can be
unambiguously identified by the operator `:.`. For example:
quote do: Foo.bar #=>
{{:.,[],[{:__aliases__,[],[:Foo]},:bar]},[],[]}
Whenever an expression iterator sees a `:.` as the tuple key,
it can be sure that it represents a call and the second argument
is the list is an atom.
On the other hand, aliases holds some properties:
1) The head element of aliases can be any term;
2) The tail elements of aliases are guaranteed to always be atoms;
3) When the head element of aliases is the atom :Elixir, no expansion happen;
4) When the head element of aliases is not an atom, it is expanded at runtime:
quote do: some_var.Foo
{:__aliases__,[],[{:some_var,[],:quoted},:Bar]}
Since `some_var` is not available at compilation time, the compiler
expands such expression to:
Module.concat [some_var, Foo]
"""
defmacro __aliases__(args)
@doc """
Calls the overriden function when overriding it with `defoverridable`.
See `Kernel.defoverridable` for more information and documentation.
"""
defmacro super(args)
end