3758 lines
94 KiB
Elixir
3758 lines
94 KiB
Elixir
# Use elixir_bootstrap module to be able to bootstrap Kernel.
|
|
# The bootstrap module provides simpler implementations of the
|
|
# functions removed, simple enough to bootstrap.
|
|
import Kernel, except: [@: 1, defmodule: 2, def: 1, def: 2, defp: 2,
|
|
defmacro: 1, defmacro: 2, defmacrop: 2]
|
|
import :elixir_bootstrap
|
|
|
|
defmodule Kernel do
|
|
@moduledoc """
|
|
`Kernel` provides the default macros and functions
|
|
Elixir imports into your environment. These macros and functions
|
|
can be skipped or cherry-picked via the `import` macro. For
|
|
instance, if you want to tell Elixir not to import the `case`
|
|
macro, you can do:
|
|
|
|
import Kernel, except: [case: 2]
|
|
|
|
Elixir also has special forms that are always imported and
|
|
cannot be skipped. These are described in `Kernel.SpecialForms`.
|
|
|
|
Some of the functions described in this module are simply
|
|
a proxy to their Erlang counterpart. Although they are documented
|
|
here for convenience, you can access their original documentation
|
|
at http://www.erlang.org/doc/man/erlang.html.
|
|
"""
|
|
|
|
## Delegations to Erlang with inlining (macros)
|
|
|
|
@doc """
|
|
Returns an integer or float which is the arithmetical absolute value of `number`.
|
|
|
|
Allowed in guard tests.
|
|
|
|
## Examples
|
|
|
|
iex> abs(-3.33)
|
|
3.33
|
|
iex> abs(-3)
|
|
3
|
|
"""
|
|
@spec abs(number) :: number
|
|
def abs(number) do
|
|
:erlang.abs(number)
|
|
end
|
|
|
|
@doc """
|
|
Invokes the given `fun` with the array of arguments `args`.
|
|
Inlines to `:erlang.apply/2`.
|
|
|
|
## Examples
|
|
|
|
iex> apply(fn x -> x * 2 end, [2])
|
|
4
|
|
|
|
"""
|
|
defmacro apply(fun, args) do
|
|
quote do
|
|
:erlang.apply(unquote(fun), unquote(args))
|
|
end
|
|
end
|
|
|
|
@doc """
|
|
Invokes the given `fun` from `module` with the array of arguments `args`.
|
|
Inlines to `:erlang.apply/3`.
|
|
|
|
## Examples
|
|
|
|
iex> apply(Enum, :reverse, [[1, 2, 3]])
|
|
[3,2,1]
|
|
|
|
"""
|
|
defmacro apply(module, fun, args) do
|
|
quote do
|
|
:erlang.apply(unquote(module), unquote(fun), unquote(args))
|
|
end
|
|
end
|
|
|
|
@doc """
|
|
Returns a binary which corresponds to the text representation
|
|
of `some_atom` in UTF8 encoding.
|
|
|
|
## Examples
|
|
|
|
iex> atom_to_binary(:my_atom)
|
|
"my_atom"
|
|
|
|
"""
|
|
defmacro atom_to_binary(some_atom) do
|
|
quote do
|
|
:erlang.atom_to_binary(unquote(some_atom), :utf8)
|
|
end
|
|
end
|
|
|
|
@doc """
|
|
Returns a string which corresponds to the text representation of `atom`.
|
|
|
|
## Examples
|
|
|
|
iex> atom_to_list(:elixir)
|
|
'elixir'
|
|
|
|
"""
|
|
@spec atom_to_list(atom) :: list
|
|
def atom_to_list(atom) do
|
|
:erlang.atom_to_list(atom)
|
|
end
|
|
|
|
@doc """
|
|
Extracts the part of the binary starting at `start` with length `length`.
|
|
Binaries are zero-indexed.
|
|
|
|
If start or length references in any way outside the binary, an
|
|
`ArgumentError` exception is raised.
|
|
|
|
Allowed in guard tests.
|
|
|
|
## Examples
|
|
|
|
iex> binary_part("foo", 1, 2)
|
|
"oo"
|
|
|
|
A negative length can be used to extract bytes at the end of a binary:
|
|
|
|
iex> binary_part("foo", 3, -1)
|
|
"o"
|
|
|
|
"""
|
|
@spec binary_part(binary, pos_integer, integer) :: binary
|
|
def binary_part(binary, start, length) do
|
|
:erlang.binary_part(binary, start, length)
|
|
end
|
|
|
|
|
|
@doc """
|
|
Returns the atom whose text representation is `some_binary` in
|
|
UTF8 encoding.
|
|
|
|
Currently Elixir does not support conversions for binaries which
|
|
contains Unicode characters greater than 16#FF.
|
|
|
|
## Examples
|
|
|
|
iex> binary_to_atom("my_atom")
|
|
:my_atom
|
|
|
|
"""
|
|
defmacro binary_to_atom(some_binary) do
|
|
quote do
|
|
:erlang.binary_to_atom(unquote(some_binary), :utf8)
|
|
end
|
|
end
|
|
|
|
@doc """
|
|
Works like `binary_to_atom/1` but the atom must exist.
|
|
|
|
Currently Elixir does not support conversions for binaries which
|
|
contains Unicode characters greater than 16#FF.
|
|
|
|
## Examples
|
|
|
|
iex> :my_atom
|
|
...> binary_to_existing_atom("my_atom")
|
|
:my_atom
|
|
|
|
iex> binary_to_existing_atom("this_atom_will_never_exist")
|
|
** (ArgumentError) argument error
|
|
|
|
"""
|
|
defmacro binary_to_existing_atom(some_binary) do
|
|
quote do
|
|
:erlang.binary_to_existing_atom(unquote(some_binary), :utf8)
|
|
end
|
|
end
|
|
|
|
@doc """
|
|
Returns a integer whose text representation is `some_binary`.
|
|
|
|
## Examples
|
|
|
|
iex> binary_to_integer("123")
|
|
123
|
|
|
|
"""
|
|
def binary_to_integer(some_binary) do
|
|
:erlang.binary_to_integer(some_binary)
|
|
end
|
|
|
|
@doc """
|
|
Returns an integer whose text representation in base `base`
|
|
is `some_binary`.
|
|
|
|
## Examples
|
|
|
|
iex> binary_to_integer("3FF", 16)
|
|
1023
|
|
|
|
"""
|
|
def binary_to_integer(some_binary, base) do
|
|
:erlang.binary_to_integer(some_binary, base)
|
|
end
|
|
|
|
@doc """
|
|
Returns a float whose text representation is `some_binary`.
|
|
|
|
## Examples
|
|
|
|
iex> binary_to_float("2.2017764e+0")
|
|
2.2017764
|
|
|
|
"""
|
|
def binary_to_float(some_binary) do
|
|
:erlang.binary_to_float(some_binary)
|
|
end
|
|
|
|
@doc """
|
|
Returns an Erlang term which is the result of decoding the binary
|
|
object `binary`, which must be encoded according to the Erlang external
|
|
term format.
|
|
|
|
## Examples
|
|
|
|
iex> binary_to_term(term_to_binary("foo"))
|
|
"foo"
|
|
|
|
"""
|
|
@spec binary_to_term(binary) :: term
|
|
def binary_to_term(binary) do
|
|
:erlang.binary_to_term(binary)
|
|
end
|
|
|
|
@doc """
|
|
As `binary_to_term/1`, but accepts a safe option useful when receiving
|
|
binaries from an untrusted source.
|
|
|
|
When enabled, it prevents decoding data that may be used to attack the
|
|
Erlang system. In the event of receiving unsafe data, decoding fails
|
|
with a badarg error.
|
|
|
|
Currently, this prevents creation of new atoms directly, creation of
|
|
new atoms indirectly (as they are embedded in certain structures like pids,
|
|
refs, funs, etc), and creation of new external function references. None
|
|
of those resources are currently garbage collected, so unchecked creation
|
|
of them can exhaust available memory.
|
|
|
|
## Examples
|
|
|
|
iex> binary_to_term(term_to_binary("foo"), [:safe])
|
|
"foo"
|
|
|
|
"""
|
|
@spec binary_to_term(binary, [] | [:safe]) :: term
|
|
def binary_to_term(binary, options) do
|
|
:erlang.binary_to_term(binary, options)
|
|
end
|
|
|
|
@doc """
|
|
Returns an integer which is the size in bits of `bitstring`.
|
|
|
|
Allowed in guard tests.
|
|
|
|
## Examples
|
|
|
|
iex> bit_size(<<433::16, 3::3>>)
|
|
19
|
|
iex> bit_size(<<1, 2, 3>>)
|
|
24
|
|
|
|
"""
|
|
@spec bit_size(bitstring) :: non_neg_integer
|
|
def bit_size(bitstring) do
|
|
:erlang.bit_size(bitstring)
|
|
end
|
|
|
|
@doc """
|
|
Returns a list of integers which correspond to the bytes of `bitstring`. If the
|
|
number of bits in the binary is not divisible by 8, the last element of the list will
|
|
be a bitstring containing the remaining bits (1 up to 7 bits).
|
|
"""
|
|
@spec bitstring_to_list(bitstring) :: list
|
|
def bitstring_to_list(bitstring) do
|
|
:erlang.bitstring_to_list(bitstring)
|
|
end
|
|
|
|
@doc """
|
|
Returns an integer which is the number of bytes needed to contain `bitstring`.
|
|
(That is, if the number of bits in `bitstring` is not divisible by 8, the resulting
|
|
number of bytes will be rounded up.)
|
|
|
|
Allowed in guard tests.
|
|
|
|
## Examples
|
|
|
|
iex> byte_size(<<433::16, 3::3>>)
|
|
3
|
|
iex> byte_size(<<1, 2, 3>>)
|
|
3
|
|
|
|
"""
|
|
@spec byte_size(bitstring) :: non_neg_integer
|
|
def byte_size(bitstring) do
|
|
:erlang.byte_size(bitstring)
|
|
end
|
|
|
|
@doc """
|
|
Provides an integer division macro according to Erlang semantics.
|
|
Raises an error if one of the arguments is not an integer.
|
|
Can be used in guard tests.
|
|
|
|
## Examples
|
|
|
|
iex> div(5, 2)
|
|
2
|
|
|
|
"""
|
|
defmacro div(left, right) do
|
|
quote do: __op__(:div, unquote(left), unquote(right))
|
|
end
|
|
|
|
@doc """
|
|
Stops the execution of the calling process with the given reason.
|
|
Since evaluating this function causes the process to terminate,
|
|
it has no return value.
|
|
|
|
## Examples
|
|
|
|
exit(:normal)
|
|
exit(:seems_bad)
|
|
|
|
"""
|
|
@spec exit(term) :: no_return
|
|
def exit(reason) do
|
|
:erlang.exit(reason)
|
|
end
|
|
|
|
@doc """
|
|
Returns a char list which corresponds to the text representation of the given float.
|
|
|
|
## Examples
|
|
|
|
iex> float_to_list(7.0)
|
|
'7.00000000000000000000e+00'
|
|
|
|
"""
|
|
@spec float_to_list(number) :: list
|
|
def float_to_list(number) do
|
|
:erlang.float_to_list(number)
|
|
end
|
|
|
|
|
|
@doc """
|
|
Returns a binary which corresponds to the text representation
|
|
of `some_float`.
|
|
|
|
## Examples
|
|
|
|
iex> float_to_binary(7.0)
|
|
"7.00000000000000000000e+00"
|
|
|
|
"""
|
|
def float_to_binary(some_float) do
|
|
:erlang.float_to_binary(some_float)
|
|
end
|
|
|
|
@doc """
|
|
Returns a binary which corresponds to the text representation
|
|
of `float`.
|
|
|
|
## Options
|
|
|
|
* `:decimals` — number of decimal points to show
|
|
* `:scientific` — number of decimal points to show, in scientific format
|
|
* `:compact` — If true, use the most compact representation (ignored with the `scientific` option)
|
|
|
|
## Examples
|
|
|
|
float_to_binary 7.1, [decimals: 2, compact: true] #=> "7.1"
|
|
|
|
"""
|
|
def float_to_binary(float, options) do
|
|
:erlang.float_to_binary(float, expand_compact(options))
|
|
end
|
|
|
|
@doc """
|
|
Returns a list which corresponds to the text representation
|
|
of `float`.
|
|
|
|
## Options
|
|
|
|
* `:decimals` — number of decimal points to show
|
|
* `:scientific` — number of decimal points to show, in scientific format
|
|
* `:compact` — If true, use the most compact representation (ignored with the `scientific` option)
|
|
|
|
## Examples
|
|
|
|
float_to_list 7.1, [decimals: 2, compact: true] #=> '7.1'
|
|
|
|
"""
|
|
def float_to_list(float, options) do
|
|
:erlang.float_to_list(float, expand_compact(options))
|
|
end
|
|
|
|
@doc """
|
|
Returns the head of a list, raises `badarg` if the list is empty.
|
|
"""
|
|
@spec hd(list) :: term
|
|
def hd(list) do
|
|
:erlang.hd(list)
|
|
end
|
|
|
|
@doc """
|
|
Returns a binary which corresponds to the text representation
|
|
of `some_integer`.
|
|
|
|
## Examples
|
|
|
|
iex> integer_to_binary(123)
|
|
"123"
|
|
|
|
"""
|
|
def integer_to_binary(some_integer) do
|
|
:erlang.integer_to_binary(some_integer)
|
|
end
|
|
|
|
@doc """
|
|
Returns a binary which corresponds to the text representation
|
|
of `some_integer` in base `base`.
|
|
|
|
## Examples
|
|
|
|
iex> integer_to_binary(100, 16)
|
|
"64"
|
|
|
|
"""
|
|
def integer_to_binary(some_integer, base) do
|
|
:erlang.integer_to_binary(some_integer, base)
|
|
end
|
|
|
|
@doc """
|
|
Returns a char list which corresponds to the text representation of the given integer.
|
|
|
|
## Examples
|
|
|
|
iex> integer_to_list(7)
|
|
'7'
|
|
|
|
"""
|
|
@spec integer_to_list(integer) :: list
|
|
def integer_to_list(number) do
|
|
:erlang.integer_to_list(number)
|
|
end
|
|
|
|
@doc """
|
|
Returns a char list which corresponds to the text representation of the
|
|
given integer in the given case.
|
|
|
|
## Examples
|
|
|
|
iex> integer_to_list(1023, 16)
|
|
'3FF'
|
|
|
|
"""
|
|
@spec integer_to_list(integer, pos_integer) :: list
|
|
def integer_to_list(number, base) do
|
|
:erlang.integer_to_list(number, base)
|
|
end
|
|
|
|
@doc """
|
|
Returns the size of an iolist.
|
|
|
|
## Examples
|
|
|
|
iex> iolist_size([1, 2|<<3, 4>>])
|
|
4
|
|
|
|
"""
|
|
@spec iolist_size(iolist) :: non_neg_integer
|
|
def iolist_size(item) do
|
|
:erlang.iolist_size(item)
|
|
end
|
|
|
|
@doc """
|
|
Returns a binary which is made from the integers and binaries in iolist.
|
|
|
|
Notice that this function treats lists of integers as raw bytes
|
|
and does not perform any kind of encoding conversion. If you want to convert
|
|
from a char list to a string (both utf-8 encoded), please use
|
|
`String.from_char_list!/1` instead.
|
|
|
|
If this function receives a binary, the same binary is returned.
|
|
|
|
## Examples
|
|
|
|
iex> bin1 = <<1, 2, 3>>
|
|
...> bin2 = <<4, 5>>
|
|
...> bin3 = <<6>>
|
|
...> iolist_to_binary([bin1, 1, [2, 3, bin2], 4|bin3])
|
|
<<1,2,3,1,2,3,4,5,4,6>>
|
|
|
|
iex> bin = <<1, 2, 3>>
|
|
...> iolist_to_binary(bin)
|
|
<<1,2,3>>
|
|
|
|
"""
|
|
@spec iolist_to_binary(iolist | binary) :: binary
|
|
def iolist_to_binary(item) do
|
|
:erlang.iolist_to_binary(item)
|
|
end
|
|
|
|
@doc false
|
|
@spec is_alive :: boolean
|
|
def is_alive do
|
|
IO.write "is_alive/0 is deprecated, please use Node.alive?/0 instead\n#{Exception.format_stacktrace}"
|
|
:erlang.is_alive
|
|
end
|
|
|
|
@doc """
|
|
Returns `true` if `term` is an atom; otherwise returns `false`.
|
|
|
|
Allowed in guard tests.
|
|
"""
|
|
@spec is_atom(term) :: boolean
|
|
def is_atom(term) do
|
|
:erlang.is_atom(term)
|
|
end
|
|
|
|
@doc """
|
|
Returns `true` if `term` is a binary; otherwise returns `false`.
|
|
|
|
A binary always contains a complete number of bytes.
|
|
|
|
Allowed in guard tests.
|
|
"""
|
|
@spec is_binary(term) :: boolean
|
|
def is_binary(term) do
|
|
:erlang.is_binary(term)
|
|
end
|
|
|
|
@doc """
|
|
Returns `true` if `term` is a bitstring (including a binary); otherwise returns `false`.
|
|
|
|
Allowed in guard tests.
|
|
"""
|
|
@spec is_bitstring(term) :: boolean
|
|
def is_bitstring(term) do
|
|
:erlang.is_bitstring(term)
|
|
end
|
|
|
|
@doc """
|
|
Returns `true` if `term` is either the atom `true` or the atom `false` (i.e. a boolean);
|
|
otherwise returns false.
|
|
|
|
Allowed in guard tests.
|
|
"""
|
|
@spec is_boolean(term) :: boolean
|
|
def is_boolean(term) do
|
|
:erlang.is_boolean(term)
|
|
end
|
|
|
|
@doc """
|
|
Returns `true` if `term` is a floating point number; otherwise returns `false`.
|
|
|
|
Allowed in guard tests.
|
|
"""
|
|
@spec is_float(term) :: boolean
|
|
def is_float(term) do
|
|
:erlang.is_float(term)
|
|
end
|
|
|
|
@doc """
|
|
Returns `true` if `term` is a function; otherwise returns `false`.
|
|
|
|
Allowed in guard tests.
|
|
"""
|
|
@spec is_function(term) :: boolean
|
|
def is_function(term) do
|
|
:erlang.is_function(term)
|
|
end
|
|
|
|
@doc """
|
|
Returns `true` if `term` is a function that can be applied with `arity` number of arguments;
|
|
otherwise returns `false`.
|
|
|
|
Allowed in guard tests.
|
|
"""
|
|
@spec is_function(term, non_neg_integer) :: boolean
|
|
def is_function(term, arity) do
|
|
:erlang.is_function(term, arity)
|
|
end
|
|
|
|
@doc """
|
|
Returns `true` if `term` is an integer; otherwise returns `false`.
|
|
|
|
Allowed in guard tests.
|
|
"""
|
|
@spec is_integer(term) :: boolean
|
|
def is_integer(term) do
|
|
:erlang.is_integer(term)
|
|
end
|
|
|
|
@doc """
|
|
Returns `true` if `term` is a list with zero or more elements; otherwise returns `false`.
|
|
|
|
Allowed in guard tests.
|
|
"""
|
|
@spec is_list(term) :: boolean
|
|
def is_list(term) do
|
|
:erlang.is_list(term)
|
|
end
|
|
|
|
@doc """
|
|
Returns `true` if `term` is either an integer or a floating point number;
|
|
otherwise returns `false`.
|
|
|
|
Allowed in guard tests.
|
|
"""
|
|
@spec is_number(term) :: boolean
|
|
def is_number(term) do
|
|
:erlang.is_number(term)
|
|
end
|
|
|
|
@doc """
|
|
Returns `true` if `term` is a pid (process identifier); otherwise returns `false`.
|
|
|
|
Allowed in guard tests.
|
|
"""
|
|
@spec is_pid(term) :: boolean
|
|
def is_pid(term) do
|
|
:erlang.is_pid(term)
|
|
end
|
|
|
|
@doc """
|
|
Returns `true` if `term` is a port identifier; otherwise returns `false`.
|
|
|
|
Allowed in guard tests.
|
|
"""
|
|
@spec is_port(term) :: boolean
|
|
def is_port(term) do
|
|
:erlang.is_port(term)
|
|
end
|
|
|
|
@doc """
|
|
Returns `true` if `term` is a reference; otherwise returns `false`.
|
|
|
|
Allowed in guard tests.
|
|
"""
|
|
@spec is_reference(term) :: boolean
|
|
def is_reference(term) do
|
|
:erlang.is_reference(term)
|
|
end
|
|
|
|
@doc """
|
|
Returns `true` if `term` is a tuple; otherwise returns `false`.
|
|
|
|
Allowed in guard tests.
|
|
"""
|
|
@spec is_tuple(term) :: boolean
|
|
def is_tuple(term) do
|
|
:erlang.is_tuple(term)
|
|
end
|
|
|
|
@doc """
|
|
Returns the length of `list`.
|
|
|
|
Allowed in guard tests.
|
|
|
|
## Examples
|
|
|
|
iex> length([1, 2, 3, 4, 5, 6, 7, 8, 9])
|
|
9
|
|
"""
|
|
@spec length(list) :: non_neg_integer
|
|
def length(list) do
|
|
:erlang.length(list)
|
|
end
|
|
|
|
@doc """
|
|
Returns the atom whose text representation is `list`.
|
|
|
|
## Examples
|
|
|
|
iex> list_to_atom('elixir')
|
|
:elixir
|
|
"""
|
|
@spec list_to_atom(list) :: atom
|
|
def list_to_atom(list) do
|
|
:erlang.list_to_atom(list)
|
|
end
|
|
|
|
@doc """
|
|
Returns a bitstring which is made from the integers and bitstrings in `bitstring_list`.
|
|
(the last tail in `bitstring_list` is allowed to be a bitstring.)
|
|
|
|
## Examples
|
|
|
|
iex> bin1 = <<1, 2, 3>>
|
|
...> bin2 = <<4, 5>>
|
|
...> bin3 = <<6, 7::4>>
|
|
...> list_to_bitstring([bin1, 1, [2, 3, bin2], 4|bin3])
|
|
<<1,2,3,1,2,3,4,5,4,6,7::size(4)>>
|
|
|
|
"""
|
|
@spec list_to_bitstring(maybe_improper_list(char | binary | iolist | bitstring, binary | bitstring | [])) :: bitstring
|
|
def list_to_bitstring(bitstring_list) do
|
|
:erlang.list_to_bitstring(bitstring_list)
|
|
end
|
|
|
|
@doc """
|
|
Returns the atom whose text representation is `list`,
|
|
but only if there already exists such atom.
|
|
"""
|
|
@spec list_to_existing_atom(list) :: atom
|
|
def list_to_existing_atom(list) do
|
|
:erlang.list_to_existing_atom(list)
|
|
end
|
|
|
|
@doc """
|
|
Returns the float whose text representation is `list`.
|
|
|
|
## Examples
|
|
|
|
iex> list_to_float('2.2017764e+0')
|
|
2.2017764
|
|
"""
|
|
@spec list_to_float(list) :: float
|
|
def list_to_float(list) do
|
|
:erlang.list_to_float(list)
|
|
end
|
|
|
|
@doc """
|
|
Returns an integer whose text representation is `list`.
|
|
|
|
## Examples
|
|
|
|
iex> list_to_integer('123')
|
|
123
|
|
"""
|
|
@spec list_to_integer(list) :: integer
|
|
def list_to_integer(list) do
|
|
:erlang.list_to_integer(list)
|
|
end
|
|
|
|
@doc """
|
|
Returns an integer whose text representation in base `base` is `list`.
|
|
|
|
## Examples
|
|
|
|
iex> list_to_integer('3FF', 16)
|
|
1023
|
|
"""
|
|
@spec list_to_integer(list, non_neg_integer) :: integer
|
|
def list_to_integer(list, base) do
|
|
:erlang.list_to_integer(list, base)
|
|
end
|
|
|
|
@doc """
|
|
Returns a tuple which corresponds to `list`. `list` can contain any Erlang terms.
|
|
|
|
## Examples
|
|
|
|
iex> list_to_tuple([:share, [:elixir, 163]])
|
|
{:share, [:elixir, 163]}
|
|
"""
|
|
@spec list_to_tuple(list) :: tuple
|
|
def list_to_tuple(list) do
|
|
:erlang.list_to_tuple(list)
|
|
end
|
|
|
|
@doc """
|
|
Returns an almost unique reference.
|
|
|
|
The returned reference will re-occur after approximately 2^82 calls;
|
|
therefore it is unique enough for practical purposes.
|
|
|
|
## Examples
|
|
|
|
make_ref() #=> #Reference<0.0.0.135>
|
|
|
|
"""
|
|
@spec make_ref() :: reference
|
|
def make_ref() do
|
|
:erlang.make_ref()
|
|
end
|
|
|
|
@doc """
|
|
Return the biggest of the two given terms according to
|
|
Erlang's term ordering. If the terms compare equal, the
|
|
first one is returned.
|
|
|
|
## Examples
|
|
|
|
iex> max(1, 2)
|
|
2
|
|
|
|
"""
|
|
@spec max(term, term) :: term
|
|
def max(first, second) do
|
|
:erlang.max(first, second)
|
|
end
|
|
|
|
@doc """
|
|
Return the smallest of the two given terms according to
|
|
Erlang's term ordering. If the terms compare equal, the
|
|
first one is returned.
|
|
|
|
## Examples
|
|
|
|
iex> min(1, 2)
|
|
1
|
|
|
|
"""
|
|
@spec min(term, term) :: term
|
|
def min(first, second) do
|
|
:erlang.min(first, second)
|
|
end
|
|
|
|
@doc """
|
|
Returns an atom representing the name of the local node.
|
|
If the node is not alive, `nonode@nohost` is returned instead.
|
|
|
|
Allowed in guard tests.
|
|
"""
|
|
@spec node() :: node
|
|
def node do
|
|
:erlang.node
|
|
end
|
|
|
|
@doc """
|
|
Returns the node where the given argument is located.
|
|
The argument can be a pid, a reference, or a port.
|
|
If the local node is not alive, `nonode@nohost` is returned.
|
|
|
|
Allowed in guard tests.
|
|
"""
|
|
@spec node(pid|reference|port) :: node
|
|
def node(arg) do
|
|
:erlang.node(arg)
|
|
end
|
|
|
|
@doc """
|
|
Provides an integer remainder macro according to Erlang semantics.
|
|
Raises an error if one of the arguments is not an integer.
|
|
Can be used in guard tests.
|
|
|
|
## Examples
|
|
|
|
iex> rem(5, 2)
|
|
1
|
|
|
|
"""
|
|
defmacro rem(left, right) do
|
|
quote do: __op__(:rem, unquote(left), unquote(right))
|
|
end
|
|
|
|
@doc """
|
|
Returns an integer by rounding the given number.
|
|
Allowed in guard tests.
|
|
|
|
## Examples
|
|
|
|
iex> round(5.5)
|
|
6
|
|
|
|
"""
|
|
@spec round(number) :: integer
|
|
def round(number) do
|
|
:erlang.round(number)
|
|
end
|
|
|
|
@doc """
|
|
Sends a message to the given `dest` and returns the message.
|
|
|
|
`dest` may be a remote or local pid, a (local) port, a locally
|
|
registered name, or a tuple `{registed_name, node}` for a registered
|
|
name at another node.
|
|
|
|
## Examples
|
|
|
|
iex> send self(), :hello
|
|
:hello
|
|
|
|
"""
|
|
@spec send(dest :: pid | port | atom | { atom, node }, msg) :: msg when msg: any
|
|
def send(dest, msg) do
|
|
:erlang.send(dest, msg)
|
|
end
|
|
|
|
@doc """
|
|
Returns the pid (process identifier) of the calling process.
|
|
Allowed in guard clauses.
|
|
"""
|
|
@spec self() :: pid
|
|
def self() do
|
|
:erlang.self()
|
|
end
|
|
|
|
@doc """
|
|
Returns the size of the given argument, which must be a tuple
|
|
or a binary. If possible, please use `tuple_size` or `byte_size`.
|
|
"""
|
|
@spec size(tuple|binary) :: non_neg_integer
|
|
def size(arg) do
|
|
:erlang.size(arg)
|
|
end
|
|
|
|
@doc """
|
|
Spawns the given function and returns its pid.
|
|
|
|
Check the modules `Process` and `Node` for other functions
|
|
to handle processes, including spawning functions in nodes.
|
|
|
|
## Examples
|
|
|
|
current = Kernel.self
|
|
child = spawn(fn -> send current, { Kernel.self, 1 + 2 } end)
|
|
|
|
receive do
|
|
{ ^child, 3 } -> IO.puts "Received 3 back"
|
|
end
|
|
|
|
"""
|
|
@spec spawn((() -> any)) :: pid
|
|
def spawn(fun) do
|
|
:erlang.spawn(fun)
|
|
end
|
|
|
|
@doc """
|
|
Spawns the given module and function passing the given args
|
|
and returns its pid.
|
|
|
|
Check the modules `Process` and `Node` for other functions
|
|
to handle processes, including spawning functions in nodes.
|
|
|
|
## Examples
|
|
|
|
spawn(SomeModule, :function, [1, 2, 3])
|
|
|
|
"""
|
|
@spec spawn(module, atom, list) :: pid
|
|
def spawn(module, fun, args) do
|
|
:erlang.spawn(module, fun, args)
|
|
end
|
|
|
|
@doc """
|
|
Spawns the given function, links it to the current process and returns its pid.
|
|
|
|
Check the modules `Process` and `Node` for other functions
|
|
to handle processes, including spawning functions in nodes.
|
|
|
|
## Examples
|
|
|
|
current = Kernel.self
|
|
child = spawn_link(fn -> send current, { Kernel.self, 1 + 2 } end)
|
|
|
|
receive do
|
|
{ ^child, 3 } -> IO.puts "Received 3 back"
|
|
end
|
|
|
|
"""
|
|
@spec spawn_link((() -> any)) :: pid
|
|
def spawn_link(fun) do
|
|
:erlang.spawn_link(fun)
|
|
end
|
|
|
|
@doc """
|
|
Spawns the given module and function passing the given args,
|
|
links it to the current process and returns its pid.
|
|
|
|
Check the modules `Process` and `Node` for other functions
|
|
to handle processes, including spawning functions in nodes.
|
|
|
|
## Examples
|
|
|
|
spawn_link(SomeModule, :function, [1, 2, 3])
|
|
|
|
"""
|
|
@spec spawn_link(module, atom, list) :: pid
|
|
def spawn_link(module, fun, args) do
|
|
:erlang.spawn_link(module, fun, args)
|
|
end
|
|
|
|
@doc """
|
|
Returns a binary which is the result of encoding the given `term`
|
|
according to the Erlang external term format.
|
|
|
|
This can be used for a variety of purposes, for example, writing a term
|
|
to a file in an efficient way, or sending an Erlang term to some type
|
|
of communications channel not supported by distributed.
|
|
"""
|
|
@spec term_to_binary(term) :: binary
|
|
def term_to_binary(term) do
|
|
:erlang.term_to_binary(term)
|
|
end
|
|
|
|
@doc """
|
|
The same as `term_to_binary/1` but also supports two options:
|
|
|
|
* `compressed`: the level of compression to be used from 0 to 9;
|
|
* `minor_version`: used to control the details of encoding. Can be 0 or 1,
|
|
please read http://www.erlang.org/doc/man/erlang.html#term_to_binary-2
|
|
for more details
|
|
|
|
"""
|
|
@spec term_to_binary(term, list({:compressed, 0..9}|{:minor_version, 0}|{:minor_version, 1})) :: binary
|
|
def term_to_binary(term, opts) do
|
|
:erlang.term_to_binary(term, opts)
|
|
end
|
|
|
|
@doc """
|
|
A non-local return from a function. Check `try/2` for more information.
|
|
"""
|
|
@spec throw(term) :: no_return
|
|
def throw(term) do
|
|
:erlang.throw(term)
|
|
end
|
|
|
|
@doc """
|
|
Returns the tail of a list. Raises `ArgumentError` if the list is empty.
|
|
"""
|
|
@spec tl(maybe_improper_list) :: maybe_improper_list
|
|
def tl(list) do
|
|
:erlang.tl(list)
|
|
end
|
|
|
|
@doc """
|
|
Returns an integer by truncating the given number.
|
|
Allowed in guard clauses.
|
|
|
|
## Examples
|
|
|
|
iex> trunc(5.5)
|
|
5
|
|
|
|
"""
|
|
@spec trunc(number) :: integer
|
|
def trunc(number) do
|
|
:erlang.trunc(number)
|
|
end
|
|
|
|
@doc """
|
|
Returns the size of a tuple.
|
|
"""
|
|
@spec tuple_size(tuple) :: non_neg_integer
|
|
def tuple_size(tuple) do
|
|
:erlang.tuple_size(tuple)
|
|
end
|
|
|
|
@doc """
|
|
Converts a tuple to a list.
|
|
"""
|
|
@spec tuple_to_list(tuple) :: list
|
|
def tuple_to_list(tuple) do
|
|
:erlang.tuple_to_list(tuple)
|
|
end
|
|
|
|
@doc """
|
|
Arithmetic plus. Allowed in guard clauses.
|
|
|
|
## Examples
|
|
|
|
iex> 1 + 2
|
|
3
|
|
|
|
"""
|
|
defmacro left + right do
|
|
quote do: __op__(:+, unquote(left), unquote(right))
|
|
end
|
|
|
|
@doc """
|
|
Arithmetic minus. Allowed in guard clauses.
|
|
|
|
## Examples
|
|
|
|
iex> 1 - 2
|
|
-1
|
|
|
|
"""
|
|
defmacro left - right do
|
|
quote do: __op__(:-, unquote(left), unquote(right))
|
|
end
|
|
|
|
@doc """
|
|
Arithmetic unary plus. Allowed in guard clauses.
|
|
|
|
## Examples
|
|
|
|
iex> +1
|
|
1
|
|
|
|
"""
|
|
defmacro (+value) when is_number(value), do: value
|
|
defmacro (+value) do
|
|
quote do: :erlang.+(unquote(value))
|
|
end
|
|
|
|
@doc """
|
|
Arithmetic unary minus. Allowed in guard clauses.
|
|
|
|
## Examples
|
|
|
|
iex> -2
|
|
-2
|
|
|
|
"""
|
|
defmacro (-value) when is_number(value), do: :erlang.-(value)
|
|
defmacro (-value) do
|
|
quote do: :erlang.-(unquote(value))
|
|
end
|
|
|
|
@doc """
|
|
Arithmetic multiplication. Allowed in guard clauses.
|
|
|
|
## Examples
|
|
|
|
iex> 1 * 2
|
|
2
|
|
|
|
"""
|
|
defmacro left * right do
|
|
quote do: __op__(:*, unquote(left), unquote(right))
|
|
end
|
|
|
|
@doc """
|
|
Arithmetic division. Unlike other languages,
|
|
the result is always a float. Use `div` and `rem` if you want
|
|
a natural division or the remainder. Allowed in guard clauses.
|
|
|
|
## Examples
|
|
|
|
iex> 1 / 2
|
|
0.5
|
|
iex> 2 / 1
|
|
2.0
|
|
|
|
"""
|
|
defmacro left / right do
|
|
quote do: __op__(:/, unquote(left), unquote(right))
|
|
end
|
|
|
|
@doc false
|
|
def pid <- msg do
|
|
IO.write "<-/2 is deprecated, please use send/2 instead\n#{Exception.format_stacktrace}"
|
|
:erlang.!(pid, msg)
|
|
end
|
|
|
|
@doc """
|
|
Concatenates two lists. Allowed in guard clauses.
|
|
|
|
## Examples
|
|
|
|
iex> [1] ++ [2, 3]
|
|
[1,2,3]
|
|
|
|
iex> 'foo' ++ 'bar'
|
|
'foobar'
|
|
|
|
"""
|
|
# We compile down to the operator because it is
|
|
# automatically expanded when the left side is known.
|
|
defmacro left ++ right do
|
|
quote do: __op__(:++, unquote(left), unquote(right))
|
|
end
|
|
|
|
@doc """
|
|
Removes the first occurrence of an item on the left
|
|
for each item on the right. Allowed in guard clauses.
|
|
|
|
## Examples
|
|
|
|
iex> [1, 2, 3] -- [1, 2]
|
|
[3]
|
|
|
|
iex> [1, 2, 3, 2, 1] -- [1, 2, 2]
|
|
[3,1]
|
|
|
|
"""
|
|
defmacro left -- right do
|
|
quote do: __op__(:--, unquote(left), unquote(right))
|
|
end
|
|
|
|
@doc """
|
|
Boolean or. Requires only the first argument to be a
|
|
boolean since it short-circuits. Allowed in guard clauses.
|
|
|
|
## Examples
|
|
|
|
iex> true or false
|
|
true
|
|
|
|
"""
|
|
defmacro left or right do
|
|
quote do: __op__(:orelse, unquote(left), unquote(right))
|
|
end
|
|
|
|
@doc """
|
|
Boolean and. Requires only the first argument to be a
|
|
boolean since it short-circuits. Allowed in guard clauses.
|
|
|
|
## Examples
|
|
|
|
iex> true and false
|
|
false
|
|
|
|
"""
|
|
defmacro left and right do
|
|
quote do: __op__(:andalso, unquote(left), unquote(right))
|
|
end
|
|
|
|
@doc """
|
|
Boolean exclusive-or. Arguments must be booleans.
|
|
Returns `true` if and only if both arguments are different.
|
|
Allowed in guard clauses.
|
|
|
|
## Examples
|
|
|
|
iex> true xor false
|
|
true
|
|
iex> true xor true
|
|
false
|
|
|
|
"""
|
|
defmacro left xor right do
|
|
quote do: __op__(:xor, unquote(left), unquote(right))
|
|
end
|
|
|
|
@doc """
|
|
Boolean not. Argument must be a boolean.
|
|
Allowed in guard clauses.
|
|
|
|
## Examples
|
|
|
|
iex> not false
|
|
true
|
|
|
|
"""
|
|
defmacro not(arg) do
|
|
quote do: __op__(:not, unquote(arg))
|
|
end
|
|
|
|
@doc """
|
|
Returns `true` if left is less than right.
|
|
Like Erlang, Elixir can compare any term. Allowed in guard clauses.
|
|
|
|
## Examples
|
|
|
|
iex> 1 < 2
|
|
true
|
|
|
|
"""
|
|
defmacro left < right do
|
|
quote do: __op__(:<, unquote(left), unquote(right))
|
|
end
|
|
|
|
@doc """
|
|
Returns `true` if left is more than right.
|
|
Like Erlang, Elixir can compare any term. Allowed in guard clauses.
|
|
|
|
## Examples
|
|
|
|
iex> 1 > 2
|
|
false
|
|
|
|
"""
|
|
defmacro left > right do
|
|
quote do: __op__(:>, unquote(left), unquote(right))
|
|
end
|
|
|
|
@doc """
|
|
Returns `true` if left is less than or equal to right.
|
|
Like Erlang, Elixir can compare any term. Allowed in guard clauses.
|
|
|
|
## Examples
|
|
|
|
iex> 1 <= 2
|
|
true
|
|
|
|
"""
|
|
defmacro left <= right do
|
|
quote do: __op__(:"=<", unquote(left), unquote(right))
|
|
end
|
|
|
|
@doc """
|
|
Returns `true` if left is more than or equal to right.
|
|
Like Erlang, Elixir can compare any term. Allowed in guard clauses.
|
|
|
|
## Examples
|
|
|
|
iex> 1 >= 2
|
|
false
|
|
|
|
"""
|
|
defmacro left >= right do
|
|
quote do: __op__(:>=, unquote(left), unquote(right))
|
|
end
|
|
|
|
@doc """
|
|
Returns `true` if the two items are equal.
|
|
|
|
This operator considers 1 and 1.0 to be equal. For strict
|
|
comparison, use `===` instead.
|
|
|
|
Like Erlang, Elixir can compare any term. Allowed in guard clauses.
|
|
|
|
## Examples
|
|
|
|
iex> 1 == 2
|
|
false
|
|
|
|
iex> 1 == 1.0
|
|
true
|
|
|
|
"""
|
|
defmacro left == right do
|
|
quote do: __op__(:==, unquote(left), unquote(right))
|
|
end
|
|
|
|
@doc """
|
|
Returns `true` if the two items are not equal.
|
|
|
|
This operator considers 1 and 1.0 to be equal. For strict
|
|
comparison, use `!==` instead.
|
|
|
|
Like Erlang, Elixir can compare any term. Allowed in guard clauses.
|
|
|
|
## Examples
|
|
|
|
iex> 1 != 2
|
|
true
|
|
iex> 1 != 1.0
|
|
false
|
|
|
|
"""
|
|
defmacro left != right do
|
|
quote do: __op__(:"/=", unquote(left), unquote(right))
|
|
end
|
|
|
|
@doc """
|
|
Returns `true` if the two items are strictly equal.
|
|
Like Erlang, Elixir can compare any term. Allowed in guard clauses.
|
|
|
|
## Examples
|
|
|
|
iex> 1 === 2
|
|
false
|
|
|
|
iex> 1 === 1.0
|
|
false
|
|
|
|
"""
|
|
defmacro left === right do
|
|
quote do: __op__(:"=:=", unquote(left), unquote(right))
|
|
end
|
|
|
|
@doc """
|
|
Returns `true` if the two items are strictly not equal.
|
|
Like Erlang, Elixir can compare any term. Allowed in guard clauses.
|
|
|
|
## Examples
|
|
|
|
iex> 1 !== 2
|
|
true
|
|
|
|
iex> 1 !== 1.0
|
|
true
|
|
|
|
"""
|
|
defmacro left !== right do
|
|
quote do: __op__(:"=/=", unquote(left), unquote(right))
|
|
end
|
|
|
|
@doc """
|
|
Receives any argument and returns `true` if it is `false`
|
|
or `nil`. Returns `false` otherwise. Not allowed in guard
|
|
clauses.
|
|
|
|
## Examples
|
|
|
|
iex> !1
|
|
false
|
|
iex> ![1, 2, 3]
|
|
false
|
|
iex> !false
|
|
true
|
|
iex> !nil
|
|
true
|
|
|
|
"""
|
|
defmacro !(arg)
|
|
|
|
defmacro !({ :!, _, [arg] }) do
|
|
quote do
|
|
case unquote(arg) do
|
|
unquote(cond_var) when unquote(cond_var) in [false, nil] -> false
|
|
_ -> true
|
|
end
|
|
end
|
|
end
|
|
|
|
defmacro !(arg) do
|
|
quote do
|
|
case unquote(arg) do
|
|
unquote(cond_var) when unquote(cond_var) in [false, nil] -> true
|
|
_ -> false
|
|
end
|
|
end
|
|
end
|
|
|
|
## Implemented in Elixir
|
|
|
|
@doc """
|
|
Concatenates two binaries.
|
|
|
|
## Examples
|
|
|
|
iex> "foo" <> "bar"
|
|
"foobar"
|
|
|
|
The `<>` operator can also be used in guard clauses as
|
|
long as the first part is a literal binary:
|
|
|
|
iex> "foo" <> x = "foobar"
|
|
...> x
|
|
"bar"
|
|
|
|
"""
|
|
defmacro left <> right do
|
|
concats = extract_concatenations({ :<>, [], [left, right] })
|
|
quote do: << unquote_splicing(concats) >>
|
|
end
|
|
|
|
# Extracts concatenations in order to optimize many
|
|
# concatenations into one single clause.
|
|
defp extract_concatenations({ :<>, _, [left, right] }) do
|
|
[wrap_concatenation(left)|extract_concatenations(right)]
|
|
end
|
|
|
|
defp extract_concatenations(other) do
|
|
[wrap_concatenation(other)]
|
|
end
|
|
|
|
defp wrap_concatenation(binary) when is_binary(binary) do
|
|
binary
|
|
end
|
|
|
|
defp wrap_concatenation(other) do
|
|
{ :::, [], [other, { :binary, [], nil }] }
|
|
end
|
|
|
|
@doc """
|
|
Raises an error.
|
|
|
|
If the argument is a binary, it raises `RuntimeError`
|
|
using the given argument as message.
|
|
|
|
If anything else, becomes a call to `raise(argument, [])`.
|
|
|
|
## Examples
|
|
|
|
raise "Given values do not match"
|
|
|
|
try do
|
|
1 + :foo
|
|
rescue
|
|
x in [ArithmeticError] ->
|
|
IO.puts "that was expected"
|
|
raise x
|
|
end
|
|
|
|
"""
|
|
@spec raise(binary | atom | tuple) :: no_return
|
|
defmacro raise(msg) do
|
|
# Try to figure out the type at compilation time
|
|
# to avoid dead code and make dialyzer happy.
|
|
msg = case not is_binary(msg) and bootstraped?(Macro.Env) do
|
|
true -> Macro.expand(msg, __CALLER__)
|
|
false -> msg
|
|
end
|
|
|
|
case msg do
|
|
msg when is_binary(msg) ->
|
|
quote do
|
|
:erlang.error RuntimeError.exception(message: unquote(msg))
|
|
end
|
|
{ :<<>>, _, _ } = msg ->
|
|
quote do
|
|
:erlang.error RuntimeError.exception(message: unquote(msg))
|
|
end
|
|
alias when is_atom(alias) ->
|
|
quote do
|
|
:erlang.error unquote(alias).exception([])
|
|
end
|
|
_ ->
|
|
quote do
|
|
case unquote(msg) do
|
|
msg when is_binary(msg) -> :erlang.error RuntimeError.exception(message: msg)
|
|
msg -> :erlang.error msg.exception([])
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
@doc """
|
|
Raises an error.
|
|
|
|
Calls `.exception` on the given argument passing
|
|
the args in order to retrieve the appropriate exception
|
|
structure.
|
|
|
|
Any module defined via `defexception` automatically
|
|
implements `exception(args)` callback expected by `raise/2`.
|
|
|
|
## Examples
|
|
|
|
iex> raise(ArgumentError, message: "Sample")
|
|
** (ArgumentError) Sample
|
|
|
|
"""
|
|
@spec raise(tuple | atom, list) :: no_return
|
|
defmacro raise(exception, args) do
|
|
quote do
|
|
:erlang.error unquote(exception).exception(unquote(args))
|
|
end
|
|
end
|
|
|
|
@doc """
|
|
Re-raises an exception with the given stacktrace.
|
|
|
|
## Examples
|
|
|
|
try do
|
|
raise "Oops"
|
|
rescue
|
|
exception ->
|
|
stacktrace = System.stacktrace
|
|
if exception.message == "Oops" do
|
|
raise exception, [], stacktrace
|
|
end
|
|
end
|
|
|
|
Notice that `System.stacktrace` returns the stacktrace
|
|
of the last exception. That said, it is common to assign
|
|
the stacktrace as the first expression inside a `rescue`
|
|
clause as any other exception potentially raised (and
|
|
rescued) in between the rescue clause and the raise call
|
|
may change the `System.stacktrace` value.
|
|
"""
|
|
@spec raise(tuple | atom, list, list) :: no_return
|
|
defmacro raise(exception, args, stacktrace) do
|
|
quote do
|
|
:erlang.raise :error, unquote(exception).exception(unquote(args)), unquote(stacktrace)
|
|
end
|
|
end
|
|
|
|
@doc """
|
|
Get the element at the zero-based `index` in `tuple`.
|
|
|
|
Implemented as a macro so it can be used in guards.
|
|
|
|
## Example
|
|
|
|
iex> tuple = { :foo, :bar, 3 }
|
|
...> elem(tuple, 1)
|
|
:bar
|
|
|
|
"""
|
|
defmacro elem(tuple, index) when is_integer(index) do
|
|
quote do: :erlang.element(unquote(index + 1), unquote(tuple))
|
|
end
|
|
|
|
defmacro elem(tuple, index) do
|
|
quote do: :erlang.element(unquote(index) + 1, unquote(tuple))
|
|
end
|
|
|
|
@doc """
|
|
Sets the element in `tuple` at the zero-based `index` to the given `value`.
|
|
|
|
## Example
|
|
|
|
iex> tuple = { :foo, :bar, 3 }
|
|
...> set_elem(tuple, 0, :baz)
|
|
{ :baz, :bar, 3 }
|
|
|
|
"""
|
|
defmacro set_elem(tuple, index, value) when is_integer(index) do
|
|
quote do: :erlang.setelement(unquote(index + 1), unquote(tuple), unquote(value))
|
|
end
|
|
|
|
defmacro set_elem(tuple, index, value) do
|
|
quote do: :erlang.setelement(unquote(index) + 1, unquote(tuple), unquote(value))
|
|
end
|
|
|
|
@doc """
|
|
Checks if the given structure is an exception.
|
|
|
|
## Examples
|
|
|
|
iex> is_exception((fn -> ArithmeticError.new end).())
|
|
true
|
|
iex> is_exception((fn -> 1 end).())
|
|
false
|
|
|
|
"""
|
|
defmacro is_exception(thing) do
|
|
case __CALLER__.in_guard? do
|
|
true ->
|
|
quote do
|
|
is_tuple(unquote(thing)) and tuple_size(unquote(thing)) > 1 and
|
|
:erlang.element(2, unquote(thing)) == :__exception__
|
|
end
|
|
false ->
|
|
quote do
|
|
result = unquote(thing)
|
|
is_tuple(result) and tuple_size(result) > 1 and
|
|
:erlang.element(2, result) == :__exception__
|
|
end
|
|
end
|
|
end
|
|
|
|
@doc """
|
|
Checks if the given structure is a record. It is basically
|
|
a convenient macro that checks the structure is a tuple and
|
|
the first element matches the given kind.
|
|
|
|
## Examples
|
|
|
|
defrecord Config, sample: nil
|
|
|
|
is_record(Config.new, Config) #=> true
|
|
is_record(Config.new, List) #=> false
|
|
|
|
"""
|
|
defmacro is_record(thing, kind) do
|
|
case __CALLER__.in_guard? do
|
|
true ->
|
|
quote do
|
|
is_tuple(unquote(thing)) and tuple_size(unquote(thing)) > 0
|
|
and :erlang.element(1, unquote(thing)) == unquote(kind)
|
|
end
|
|
false ->
|
|
quote do
|
|
result = unquote(thing)
|
|
is_tuple(result) and tuple_size(result) > 0
|
|
and :erlang.element(1, result) == unquote(kind)
|
|
end
|
|
end
|
|
end
|
|
|
|
@doc """
|
|
Checks if the given argument is a record.
|
|
"""
|
|
defmacro is_record(thing) do
|
|
case __CALLER__.in_guard? do
|
|
true ->
|
|
quote do
|
|
is_tuple(unquote(thing)) and tuple_size(unquote(thing)) > 0
|
|
and is_atom(:erlang.element(1, unquote(thing)))
|
|
end
|
|
false ->
|
|
quote do
|
|
result = unquote(thing)
|
|
is_tuple(result) and tuple_size(result) > 0
|
|
and is_atom(:erlang.element(1, result))
|
|
end
|
|
end
|
|
end
|
|
|
|
@doc """
|
|
Checks if the given argument is a regex.
|
|
"""
|
|
defmacro is_regex(thing) do
|
|
quote do
|
|
is_record(unquote(thing), Regex)
|
|
end
|
|
end
|
|
|
|
@doc """
|
|
Checks if the given argument is a range.
|
|
"""
|
|
defmacro is_range(thing) do
|
|
quote do
|
|
is_record(unquote(thing), Range)
|
|
end
|
|
end
|
|
|
|
@doc """
|
|
Matches the term on the left against the regular expression or string on the
|
|
right. Returns true if `left` matches `right` (if it's a regular expression)
|
|
or contains `right` (if it's a string).
|
|
|
|
## Examples
|
|
|
|
iex> "abcd" =~ %r/c(d)/
|
|
true
|
|
|
|
iex> "abcd" =~ %r/e/
|
|
false
|
|
|
|
iex> "abcd" =~ "bc"
|
|
true
|
|
|
|
iex> "abcd" =~ "ad"
|
|
false
|
|
|
|
"""
|
|
def left =~ right when is_binary(left) and is_binary(right) do
|
|
:binary.match(left, right) != :nomatch
|
|
end
|
|
|
|
def left =~ right when is_binary(left) and is_tuple(right) and
|
|
tuple_size(right) > 0 and elem(right, 0) == Regex do
|
|
Regex.match?(right, left)
|
|
end
|
|
|
|
@doc %S"""
|
|
Inspect the given argument according to the `Inspect` protocol.
|
|
The second argument is a keywords list with options to control
|
|
inspection.
|
|
|
|
## Options
|
|
|
|
The following options are supported:
|
|
|
|
* `:records` - when false, records are not formatted by the inspect protocol,
|
|
they are instead printed as just tuples, defaults to true;
|
|
|
|
* `:binaries` - when `:as_strings` all binaries will be printed as strings,
|
|
non-printable bytes will be escaped; when `:as_binaries` all
|
|
binaries will be printed in bit syntax; when the default
|
|
`:infer`, the binary will be printed as a string if it is
|
|
printable, otherwise in bit syntax;
|
|
|
|
* `:char_lists` - when `:as_char_lists` all lists will be printed as char lists,
|
|
non-printable elements will be escaped; when `:as_lists` all
|
|
lists will be printed as lists; when the default `:infer`, the
|
|
list will be printed as a char list if it is printable,
|
|
otherwise as list;
|
|
|
|
* `:limit` - limits the number of items that are printed for tuples, bitstrings,
|
|
and lists, does not apply to strings nor char lists, defaults to 50;
|
|
|
|
* `:pretty` - if set to true enables pretty printing, defaults to false;
|
|
|
|
* `:width` - the width available for inspect to layout the data structure
|
|
representation. Defaults to the smaller of 80 or the terminal width;
|
|
|
|
## Examples
|
|
|
|
iex> inspect(:foo)
|
|
":foo"
|
|
|
|
iex> inspect [1, 2, 3, 4, 5], limit: 3
|
|
"[1, 2, 3, ...]"
|
|
|
|
iex> inspect(ArgumentError[])
|
|
"ArgumentError[message: \"argument error\"]"
|
|
|
|
iex> inspect(ArgumentError[], records: false)
|
|
"{ArgumentError, :__exception__, \"argument error\"}"
|
|
|
|
iex> inspect("josé" <> <<0>>)
|
|
"<<106, 111, 115, 195, 169, 0>>"
|
|
|
|
iex> inspect("josé" <> <<0>>, binaries: :as_strings)
|
|
"\"josé\\000\""
|
|
|
|
iex> inspect("josé", binaries: :as_binaries)
|
|
"<<106, 111, 115, 195, 169>>"
|
|
|
|
Note that the inspect protocol does not necessarily return a valid
|
|
representation of an Elixir term. In such cases, the inspected result
|
|
must start with `#`. For example, inspecting a function will return:
|
|
|
|
inspect &(&1 + &2)
|
|
#=> #Function<...>
|
|
|
|
"""
|
|
@spec inspect(Inspect.t, Keyword.t) :: String.t
|
|
def inspect(arg, opts // [])
|
|
|
|
def inspect(arg, opts) when is_tuple(opts) and tuple_size(opts) > 0 and elem(opts, 0) == Inspect.Opts do
|
|
IO.write "Kernel.inspect/2 with Inspect.Opts is deprecated, please use Inspect.Algebra.to_doc/2 instead\n#{Exception.format_stacktrace}"
|
|
Inspect.Algebra.to_doc(arg, opts)
|
|
end
|
|
|
|
def inspect(arg, opts) when is_list(opts) do
|
|
case Keyword.get(opts, :raw) do
|
|
nil ->
|
|
:ok
|
|
raw ->
|
|
IO.write "Kernel.inspect/2 with :raw option is deprecated, please use :records instead\n#{Exception.format_stacktrace}"
|
|
opts = opts ++ [records: not raw]
|
|
end
|
|
|
|
opts = Inspect.Opts.new(opts)
|
|
limit = case opts.pretty do
|
|
true -> opts.width
|
|
false -> :infinity
|
|
end
|
|
Inspect.Algebra.pretty(Inspect.Algebra.to_doc(arg, opts), limit)
|
|
end
|
|
|
|
@doc """
|
|
Converts the argument to a string according to the String.Chars protocol.
|
|
This is the function invoked when there is string interpolation.
|
|
|
|
## Examples
|
|
|
|
iex> to_string(:foo)
|
|
"foo"
|
|
|
|
"""
|
|
# If it is a binary at compilation time, simply return it.
|
|
defmacro to_string(arg) when is_binary(arg), do: arg
|
|
|
|
defmacro to_string(arg) do
|
|
quote do: String.Chars.to_string(unquote(arg))
|
|
end
|
|
|
|
@doc """
|
|
Convert the argument to a list according to the List.Chars protocol.
|
|
|
|
## Examples
|
|
|
|
iex> to_char_list(:foo)
|
|
'foo'
|
|
|
|
"""
|
|
defmacro to_char_list(arg) do
|
|
quote do: List.Chars.to_char_list(unquote(arg))
|
|
end
|
|
|
|
@doc """
|
|
Checks if the given argument is nil or not.
|
|
Allowed in guard clauses.
|
|
|
|
## Examples
|
|
|
|
iex> nil?(1)
|
|
false
|
|
iex> nil?(nil)
|
|
true
|
|
|
|
"""
|
|
defmacro nil?(x) do
|
|
quote do: unquote(x) == nil
|
|
end
|
|
|
|
@doc """
|
|
A convenient macro that checks if the right side matches
|
|
the left side. The left side is allowed to be a match pattern.
|
|
|
|
## Examples
|
|
|
|
iex> match?(1, 1)
|
|
true
|
|
iex> match?(1, 2)
|
|
false
|
|
iex> match?({1, _}, {1, 2})
|
|
true
|
|
|
|
Match can also be used to filter or find a value in an enumerable:
|
|
|
|
list = [{:a, 1}, {:b, 2}, {:a, 3}]
|
|
Enum.filter list, &match?({:a, _}, &1)
|
|
|
|
Guard clauses can also be given to the match:
|
|
|
|
list = [{:a, 1}, {:b, 2}, {:a, 3}]
|
|
Enum.filter list, &match?({:a, x } when x < 2, &1)
|
|
|
|
However, variables assigned in the match will not be available
|
|
outside of the function call:
|
|
|
|
iex> match?(x, 1)
|
|
true
|
|
iex> binding([:x]) == []
|
|
true
|
|
|
|
"""
|
|
defmacro match?(pattern, expr)
|
|
|
|
# Special case underscore since it always matches
|
|
defmacro match?({ :_, _, atom }, _right) when is_atom(atom) do
|
|
true
|
|
end
|
|
|
|
defmacro match?(left, right) do
|
|
quote do
|
|
case unquote(right) do
|
|
unquote(left) ->
|
|
true
|
|
_ ->
|
|
false
|
|
end
|
|
end
|
|
end
|
|
|
|
@doc """
|
|
This macro is a shortcut to read and add attributes to the module
|
|
being compiled. Elixir module attributes are similar to Erlang's with
|
|
some differences. The canonical example for attributes is annotating
|
|
that a module implements the OTP behavior called `gen_server`:
|
|
|
|
defmodule MyServer do
|
|
@behavior :gen_server
|
|
# ... callbacks ...
|
|
end
|
|
|
|
By default Elixir supports all Erlang module attributes, but any developer
|
|
can also add custom attributes:
|
|
|
|
defmodule MyServer do
|
|
@my_data 13
|
|
IO.inspect @my_data #=> 13
|
|
end
|
|
|
|
Unlike Erlang, such attributes are not stored in the module by
|
|
default since it is common in Elixir to use such attributes to store
|
|
temporary data. A developer can configure an attribute to behave closer
|
|
to Erlang by calling `Module.register_attribute/3`.
|
|
|
|
Finally, notice that attributes can also be read inside functions:
|
|
|
|
defmodule MyServer do
|
|
@my_data 11
|
|
def first_data, do: @my_data
|
|
@my_data 13
|
|
def second_data, do: @my_data
|
|
end
|
|
|
|
MyServer.first_data #=> 11
|
|
MyServer.second_data #=> 13
|
|
|
|
It is important to note that reading an attribute takes a snapshot of
|
|
its current value. In other words, the value is read at compilation
|
|
time and not at runtime. Check the module `Module` for other functions
|
|
to manipulate module attributes.
|
|
"""
|
|
defmacro @(expr)
|
|
|
|
# Typespecs attributes are special cased by the compiler so far
|
|
defmacro @({ name, _, args }) do
|
|
# Check for Macro as it is compiled later than Module
|
|
case bootstraped?(Module) do
|
|
false -> nil
|
|
true ->
|
|
assert_module_scope(__CALLER__, :@, 1)
|
|
function? = env_function(__CALLER__) != nil
|
|
|
|
case is_list(args) and length(args) == 1 and typespec(name) do
|
|
false ->
|
|
case name == :typedoc and internal? do
|
|
true -> nil
|
|
false -> do_at(args, name, function?, __CALLER__)
|
|
end
|
|
macro ->
|
|
# Do not compile typespecs during internal compilation
|
|
case internal? do
|
|
true -> nil
|
|
false -> quote do: Kernel.Typespec.unquote(macro)(unquote(hd(args)))
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
# @attribute value
|
|
defp do_at([arg], name, function?, _env) do
|
|
case function? do
|
|
true ->
|
|
raise ArgumentError, message: "cannot dynamically set attribute @#{name} inside function"
|
|
false ->
|
|
quote do: Module.put_attribute(__MODULE__, unquote(name), unquote(arg))
|
|
end
|
|
end
|
|
|
|
# @attribute or @attribute()
|
|
defp do_at(args, name, function?, env) when is_atom(args) or args == [] do
|
|
case function? do
|
|
true ->
|
|
stack =
|
|
case bootstraped?(Macro.Env) do
|
|
true -> env.stacktrace
|
|
false -> []
|
|
end
|
|
attr = Module.get_attribute(env_module(env), name, stack)
|
|
:erlang.element(1, :elixir_quote.escape(attr, false))
|
|
false ->
|
|
quote do: Module.get_attribute(__MODULE__, unquote(name), true)
|
|
end
|
|
end
|
|
|
|
# All other cases
|
|
defp do_at(args, name, _function?, _env) do
|
|
raise ArgumentError, message: "expected 0 or 1 argument for @#{name}, got: #{length(args)}"
|
|
end
|
|
|
|
defp typespec(:type), do: :deftype
|
|
defp typespec(:typep), do: :deftypep
|
|
defp typespec(:opaque), do: :defopaque
|
|
defp typespec(:spec), do: :defspec
|
|
defp typespec(:callback), do: :defcallback
|
|
defp typespec(_), do: false
|
|
|
|
@doc """
|
|
Returns the binding as a keyword list where the variable name
|
|
is the key and the variable value is the value.
|
|
|
|
## Examples
|
|
|
|
iex> x = 1
|
|
iex> binding()
|
|
[x: 1]
|
|
iex> x = 2
|
|
iex> binding()
|
|
[x: 2]
|
|
|
|
"""
|
|
defmacro binding() do
|
|
do_binding(nil, __CALLER__.vars, __CALLER__.in_match?)
|
|
end
|
|
|
|
@doc """
|
|
Receives a list of atoms at compilation time and returns the
|
|
binding of the given variables as a keyword list where the
|
|
variable name is the key and the variable value is the value.
|
|
|
|
In case a variable in the list does not exist in the binding,
|
|
it is not included in the returned result.
|
|
|
|
## Examples
|
|
|
|
iex> x = 1
|
|
iex> binding([:x, :y])
|
|
[x: 1]
|
|
|
|
"""
|
|
defmacro binding(list) when is_list(list) do
|
|
do_binding(list, nil, __CALLER__.vars, __CALLER__.in_match?)
|
|
end
|
|
|
|
defmacro binding(context) when is_atom(context) do
|
|
do_binding(context, __CALLER__.vars, __CALLER__.in_match?)
|
|
end
|
|
|
|
@doc """
|
|
Receives a list of atoms at compilation time and returns the
|
|
binding of the given variables in the given context as a keyword
|
|
list where the variable name is the key and the variable value
|
|
is the value.
|
|
|
|
In case a variable in the list does not exist in the binding,
|
|
it is not included in the returned result.
|
|
|
|
## Examples
|
|
|
|
iex> var!(x, :foo) = 1
|
|
iex> binding([:x, :y])
|
|
[]
|
|
iex> binding([:x, :y], :foo)
|
|
[x: 1]
|
|
|
|
"""
|
|
defmacro binding(list, context) when is_list(list) and is_atom(context) do
|
|
do_binding(list, context, __CALLER__.vars, __CALLER__.in_match?)
|
|
end
|
|
|
|
defp do_binding(context, vars, in_match) do
|
|
lc { v, c } inlist vars, c == context, v != :_@CALLER do
|
|
{ v, wrap_binding(in_match, { v, [], c }) }
|
|
end
|
|
end
|
|
|
|
defp do_binding(list, context, vars, in_match) do
|
|
lc { v, c } inlist vars, c == context, :lists.member(v, list) do
|
|
{ v, wrap_binding(in_match, { v, [], c }) }
|
|
end
|
|
end
|
|
|
|
defp wrap_binding(true, var) do
|
|
quote do: ^(unquote(var))
|
|
end
|
|
|
|
defp wrap_binding(_, var) do
|
|
var
|
|
end
|
|
|
|
@doc """
|
|
Provides an `if` macro. This macro expects the first argument to
|
|
be a condition and the rest are keyword arguments.
|
|
|
|
## One-liner examples
|
|
|
|
if(foo, do: bar)
|
|
|
|
In the example above, `bar` will be returned if `foo` evaluates to
|
|
`true` (i.e. it is neither `false` nor `nil`). Otherwise, `nil` will be returned.
|
|
|
|
An `else` option can be given to specify the opposite:
|
|
|
|
if(foo, do: bar, else: baz)
|
|
|
|
## Blocks examples
|
|
|
|
Elixir also allows you to pass a block to the `if` macro. The first
|
|
example above would be translated to:
|
|
|
|
if foo do
|
|
bar
|
|
end
|
|
|
|
Notice that `do/end` becomes delimiters. The second example would
|
|
then translate to:
|
|
|
|
if foo do
|
|
bar
|
|
else
|
|
baz
|
|
end
|
|
|
|
If you want to compare more than two clauses, you can use the `cond/1`
|
|
macro.
|
|
"""
|
|
defmacro if(condition, clauses) do
|
|
do_clause = Keyword.get(clauses, :do, nil)
|
|
else_clause = Keyword.get(clauses, :else, nil)
|
|
|
|
quote do
|
|
case unquote(condition) do
|
|
unquote(cond_var) when unquote(cond_var) in [false, nil] -> unquote(else_clause)
|
|
_ -> unquote(do_clause)
|
|
end
|
|
end
|
|
end
|
|
|
|
@doc """
|
|
Evaluates the expression corresponding to the first clause that
|
|
evaluates to true. Raises an error if all conditions evaluate to
|
|
to nil or false.
|
|
|
|
## Examples
|
|
|
|
cond do
|
|
1 + 1 == 1 ->
|
|
"This will never match"
|
|
2 * 2 != 4 ->
|
|
"Nor this"
|
|
true ->
|
|
"This will"
|
|
end
|
|
|
|
"""
|
|
defmacro cond([do: pairs]) do
|
|
[{ :->, meta, [[condition], clause] }|t] = :lists.reverse pairs
|
|
|
|
new_acc =
|
|
case condition do
|
|
{ :_, _, atom } when is_atom(atom) ->
|
|
raise ArgumentError, message: <<"unbound variable _ inside cond. ",
|
|
"If you want the last clause to match, you probably meant to use true ->">>
|
|
x when is_atom(x) and x != false and x != nil ->
|
|
clause
|
|
_ ->
|
|
quote line: get_line(meta) do
|
|
case !unquote(condition) do
|
|
false -> unquote(clause)
|
|
end
|
|
end
|
|
end
|
|
|
|
build_cond_clauses(t, new_acc, meta)
|
|
end
|
|
|
|
# Builds cond clauses by nesting them recursively.
|
|
#
|
|
# case !foo do
|
|
# false -> 1
|
|
# true ->
|
|
# case !bar do
|
|
# false -> 2
|
|
# true -> 3
|
|
# end
|
|
# end
|
|
#
|
|
defp build_cond_clauses([{ :->, new, [[condition], clause] }|t], acc, old) do
|
|
clauses = [falsy_clause(old, acc), truthy_clause(new, clause)]
|
|
acc = quote do: (case unquote(condition), do: unquote(clauses))
|
|
build_cond_clauses(t, acc, new)
|
|
end
|
|
|
|
defp build_cond_clauses([], acc, _), do: acc
|
|
|
|
defp falsy_clause(meta, acc) do
|
|
{ :->, meta, [[quote(do: unquote(cond_var) when unquote(cond_var) in [false, nil])], acc] }
|
|
end
|
|
|
|
defp truthy_clause(meta, clause) do
|
|
{ :->, meta, [[quote(do: _)], clause] }
|
|
end
|
|
|
|
# Setting cond: true in metadata turns on a small optimization
|
|
# in Elixir compiler. In the long run, we want to bring this
|
|
# optimization to Elixir land, but not right now.
|
|
defp cond_var do
|
|
{ :x, [cond: true], Kernel }
|
|
end
|
|
|
|
defp get_line(meta) do
|
|
case :lists.keyfind(:line, 1, meta) do
|
|
{ :line, line } -> line
|
|
false -> 0
|
|
end
|
|
end
|
|
|
|
@doc """
|
|
Evaluates and returns the do-block passed in as a second argument
|
|
unless clause evaluates to true.
|
|
Returns nil otherwise.
|
|
See also `if`.
|
|
|
|
## Examples
|
|
|
|
iex> unless(1, do: "Hello")
|
|
nil
|
|
iex> unless(false, do: "Hello")
|
|
"Hello"
|
|
|
|
"""
|
|
defmacro unless(clause, options) do
|
|
do_clause = Keyword.get(options, :do, nil)
|
|
else_clause = Keyword.get(options, :else, nil)
|
|
quote do
|
|
if(unquote(clause), do: unquote(else_clause), else: unquote(do_clause))
|
|
end
|
|
end
|
|
|
|
@doc """
|
|
Allows you to destructure two lists, assigning each term in the right to the
|
|
matching term in the left. Unlike pattern matching via `=`, if the sizes of
|
|
the left and right lists don't match, destructuring simply stops instead of
|
|
raising an error.
|
|
|
|
## Examples
|
|
|
|
iex> destructure([x, y, z], [1, 2, 3, 4, 5])
|
|
...> {x, y, z}
|
|
{1, 2, 3}
|
|
|
|
Notice in the example above, even though the right
|
|
size has more entries than the left, destructuring works
|
|
fine. If the right size is smaller, the remaining items
|
|
are simply assigned to nil:
|
|
|
|
iex> destructure([x, y, z], [1])
|
|
...> {x, y, z}
|
|
{1, nil, nil}
|
|
|
|
The left side supports any expression you would use
|
|
on the left side of a match:
|
|
|
|
x = 1
|
|
destructure([^x, y, z], [1, 2, 3])
|
|
|
|
The example above will only work if x matches
|
|
the first value from the right side. Otherwise,
|
|
it will raise a CaseClauseError.
|
|
"""
|
|
defmacro destructure(left, right) when is_list(left) do
|
|
Enum.reduce left, right, fn item, acc ->
|
|
{ :case, meta, args } =
|
|
quote do
|
|
case unquote(acc) do
|
|
[unquote(item)|t] ->
|
|
t
|
|
other when other == [] or other == nil ->
|
|
unquote(item) = nil
|
|
end
|
|
end
|
|
{ :case, [{:export_all,true}|meta], args }
|
|
end
|
|
end
|
|
|
|
@doc """
|
|
Returns a range with the specified start and end.
|
|
Includes both ends.
|
|
|
|
## Examples
|
|
|
|
iex> 0 in 1..3
|
|
false
|
|
iex> 1 in 1..3
|
|
true
|
|
iex> 2 in 1..3
|
|
true
|
|
iex> 3 in 1..3
|
|
true
|
|
|
|
"""
|
|
defmacro first .. last do
|
|
{ :{}, [], [Elixir.Range, first, last] }
|
|
end
|
|
|
|
@doc """
|
|
Provides a short-circuit operator that evaluates and returns
|
|
the second expression only if the first one evaluates to true
|
|
(i.e. it is not nil nor false). Returns the first expression
|
|
otherwise.
|
|
|
|
## Examples
|
|
|
|
iex> true && true
|
|
true
|
|
iex> nil && true
|
|
nil
|
|
iex> true && 1
|
|
1
|
|
iex> false && throw(:bad)
|
|
false
|
|
|
|
Notice that, unlike Erlang's `and` operator,
|
|
this operator accepts any expression as an argument,
|
|
not only booleans, however it is not allowed in guards.
|
|
"""
|
|
defmacro left && right do
|
|
quote do
|
|
case unquote(left) do
|
|
x when x in [false, nil] ->
|
|
x
|
|
_ ->
|
|
unquote(right)
|
|
end
|
|
end
|
|
end
|
|
|
|
@doc """
|
|
Provides a short-circuit operator that evaluates and returns the second
|
|
expression only if the first one does not evaluate to true (i.e. it
|
|
is either nil or false). Returns the first expression otherwise.
|
|
|
|
## Examples
|
|
|
|
iex> false || false
|
|
false
|
|
iex> nil || true
|
|
true
|
|
iex> false || 1
|
|
1
|
|
iex> true || throw(:bad)
|
|
true
|
|
|
|
Notice that, unlike Erlang's `or` operator,
|
|
this operator accepts any expression as an argument,
|
|
not only booleans, however it is not allowed in guards.
|
|
"""
|
|
defmacro left || right do
|
|
quote do
|
|
case unquote(left) do
|
|
x when x in [false, nil] ->
|
|
unquote(right)
|
|
x ->
|
|
x
|
|
end
|
|
end
|
|
end
|
|
|
|
@doc """
|
|
`|>` is called the pipeline operator as it is useful
|
|
to write pipeline style expressions. This operator
|
|
introduces the expression on the left as the first
|
|
argument to the function call on the right.
|
|
|
|
## Examples
|
|
|
|
iex> [1, [2], 3] |> List.flatten |> Enum.map(&(&1 * 2))
|
|
[2,4,6]
|
|
|
|
The expression above is simply translated to:
|
|
|
|
Enum.map(List.flatten([1, [2], 3]), &(&1 * 2))
|
|
|
|
Be aware of operator precedence when using this operator.
|
|
For example, the following expression:
|
|
|
|
String.graphemes "Hello" |> Enum.reverse
|
|
|
|
Is translated to:
|
|
|
|
String.graphemes("Hello" |> Enum.reverse)
|
|
|
|
Which will result in an error as Enumerable protocol
|
|
is not defined for binaries. Adding explicit parenthesis
|
|
resolves the ambiguity:
|
|
|
|
String.graphemes("Hello") |> Enum.reverse
|
|
|
|
"""
|
|
defmacro left |> right do
|
|
:lists.foldl fn x, acc -> Macro.pipe(acc, x) end, left, Macro.unpipe(right)
|
|
end
|
|
|
|
@doc """
|
|
Returns true if the `module` is loaded and contains a
|
|
public `function` with the given `arity`, otherwise false.
|
|
|
|
In case a tuple module is given, the `arity` is automatically
|
|
increased by one.
|
|
|
|
Notice that this function does not load the module in case
|
|
it is not loaded. Check `Code.ensure_loaded/1` for more
|
|
information.
|
|
"""
|
|
@spec function_exported?(atom | tuple, atom, integer) :: boolean
|
|
def function_exported?(module, function, arity) do
|
|
case is_tuple(module) do
|
|
true ->
|
|
:erlang.function_exported(elem(module, 0), function, arity + 1)
|
|
false ->
|
|
:erlang.function_exported(module, function, arity)
|
|
end
|
|
end
|
|
|
|
@doc """
|
|
Returns true if the `module` is loaded and contains a
|
|
public `macro` with the given `arity`, otherwise false.
|
|
|
|
Notice that this function does not load the module in case
|
|
it is not loaded. Check `Code.ensure_loaded/1` for more
|
|
information.
|
|
"""
|
|
@spec macro_exported?(atom, atom, integer) :: boolean
|
|
def macro_exported?(module, macro, arity) do
|
|
:lists.member({macro, arity}, module.__info__(:macros))
|
|
end
|
|
|
|
@doc """
|
|
Access the given element using the qualifier according
|
|
to the `Access` protocol. All calls in the form `foo[bar]`
|
|
are translated to `access(foo, bar)`.
|
|
|
|
The usage of this protocol is to access a raw value in a
|
|
keyword list.
|
|
|
|
sample = [a: 1, b: 2, c: 3]
|
|
sample[:b] #=> 2
|
|
|
|
## Aliases
|
|
|
|
Whenever invoked on an alias or an atom, the access protocol is
|
|
expanded at compilation time rather than on runtime. This feature
|
|
is used by records to allow a developer to match against an specific
|
|
part of a record:
|
|
|
|
def increment(State[counter: counter, other: 13] = state) do
|
|
state.counter(counter + 1)
|
|
end
|
|
|
|
In the example above, we use the Access protocol to match the
|
|
counter field in the record `State`. Considering the record
|
|
definition is as follows:
|
|
|
|
defrecord State, counter: 0, other: nil
|
|
|
|
The clause above is translated to:
|
|
|
|
def increment({ State, counter, 13 } = state) do
|
|
state.counter(counter + 1)
|
|
end
|
|
|
|
The same pattern can be used to create a new record:
|
|
|
|
def new_state(counter) do
|
|
State[counter: counter]
|
|
end
|
|
|
|
The example above is faster than `State.new(counter: :counter)` because
|
|
the record is expanded at compilation time and not at runtime. If a field
|
|
is not specified on creation, it will have its default value.
|
|
|
|
Finally, as in Erlang, Elixir also allows the following syntax:
|
|
|
|
new_uri = State[_: 1]
|
|
|
|
In this case **all** fields will be set to `1`. Notice that,
|
|
as in Erlang, in case an expression is given, it will be
|
|
evaluated multiple times:
|
|
|
|
new_uri = State[_: IO.puts "Hello"]
|
|
|
|
In this case, `"Hello"` will be printed twice (one per each field).
|
|
"""
|
|
defmacro access(element, args) when is_list(args) do
|
|
caller = __CALLER__
|
|
atom = Macro.expand(element, caller)
|
|
|
|
case is_atom(atom) and atom != nil do
|
|
true ->
|
|
fields =
|
|
try do
|
|
case :lists.member(atom, caller.context_modules) and Module.open?(atom) do
|
|
true -> Module.get_attribute(atom, :record_fields)
|
|
false -> atom.__record__(:fields)
|
|
end
|
|
rescue
|
|
UndefinedFunctionError ->
|
|
# We first try to call __record__ and just then check if
|
|
# it is loaded so we allow the ParallelCompiler to solve
|
|
# conflicts.
|
|
case :code.ensure_loaded(atom) do
|
|
{ :error, _ } ->
|
|
:elixir_aliases.ensure_loaded(caller.line, atom, :elixir_env.ex_to_env(caller))
|
|
_ ->
|
|
raise ArgumentError, message: "cannot access module #{inspect atom} because it is not a record"
|
|
end
|
|
end
|
|
|
|
Record.access(atom, fields, args, caller)
|
|
false ->
|
|
case caller.in_match? or caller.in_guard? do
|
|
true -> raise ArgumentError, message: "dynamic access cannot be invoked inside match and guard clauses"
|
|
false -> :ok
|
|
end
|
|
|
|
case args do
|
|
[h] -> quote do: Access.access(unquote(element), unquote(h))
|
|
_ -> raise ArgumentError, message: "expected one argument in access"
|
|
end
|
|
end
|
|
end
|
|
|
|
@doc """
|
|
Checks if the element on the left side is member of the
|
|
collection on the right side.
|
|
|
|
## Examples
|
|
|
|
iex> x = 1
|
|
...> x in [1, 2, 3]
|
|
true
|
|
|
|
This macro simply translates the expression above to:
|
|
|
|
Enum.member?([1,2,3], x)
|
|
|
|
## Guards
|
|
|
|
The in operator can be used on guard clauses as long as the
|
|
right side is a range or a list. Elixir will then expand the
|
|
operator to a valid guard expression. For example:
|
|
|
|
when x in [1,2,3]
|
|
|
|
Translates to:
|
|
|
|
when x === 1 or x === 2 or x === 3
|
|
|
|
When using ranges:
|
|
|
|
when x in 1..3
|
|
|
|
Translates to:
|
|
|
|
when x >= 1 and x <= 3
|
|
|
|
"""
|
|
defmacro left in right do
|
|
cache = (env_context(__CALLER__) == nil)
|
|
|
|
right = case bootstraped?(Macro.Env) do
|
|
true -> Macro.expand(right, __CALLER__)
|
|
false ->
|
|
case right do
|
|
# For bootstrapping we special case @attributes
|
|
{ :@, _, [{ name, _, atom }] } when is_atom(name) and is_atom(atom) ->
|
|
Module.get_attribute(env_module(__CALLER__), name, true)
|
|
_ ->
|
|
right
|
|
end
|
|
end
|
|
|
|
case right do
|
|
_ when cache ->
|
|
quote do: Elixir.Enum.member?(unquote(right), unquote(left))
|
|
[] ->
|
|
false
|
|
[h|t] ->
|
|
:lists.foldr(fn x, acc ->
|
|
quote do
|
|
unquote(comp(left, x)) or unquote(acc)
|
|
end
|
|
end, comp(left, h), t)
|
|
{ :{}, _, [Elixir.Range, first, last] } ->
|
|
in_range(left, Macro.expand(first, __CALLER__), Macro.expand(last, __CALLER__))
|
|
first .. last ->
|
|
# This range came from a module attribute, so it is a
|
|
# literal value and we need to escape it.
|
|
in_range(left, Macro.escape(first), Macro.escape(last))
|
|
_ ->
|
|
raise ArgumentError, message: <<"invalid args for operator in, it expects a compile time list ",
|
|
"or range on the right side when used in guard expressions">>
|
|
end
|
|
end
|
|
|
|
defp in_range(left, first, last) do
|
|
case opt_in?(first) and opt_in?(last) do
|
|
true ->
|
|
case first <= last do
|
|
true -> increasing_compare(left, first, last)
|
|
false -> decreasing_compare(left, first, last)
|
|
end
|
|
false ->
|
|
quote do
|
|
(:erlang."=<"(unquote(first), unquote(last)) and
|
|
unquote(increasing_compare(left, first, last)))
|
|
or
|
|
(:erlang."<"(unquote(last), unquote(first)) and
|
|
unquote(decreasing_compare(left, first, last)))
|
|
end
|
|
end
|
|
end
|
|
|
|
defp opt_in?(x), do: is_integer(x) or is_float(x) or is_atom(x)
|
|
|
|
defp comp(left, right) do
|
|
quote(do: :erlang."=:="(unquote(left), unquote(right)))
|
|
end
|
|
|
|
defp increasing_compare(var, first, last) do
|
|
quote do
|
|
:erlang.">="(unquote(var), unquote(first)) and
|
|
:erlang."=<"(unquote(var), unquote(last))
|
|
end
|
|
end
|
|
|
|
defp decreasing_compare(var, first, last) do
|
|
quote do
|
|
:erlang."=<"(unquote(var), unquote(first)) and
|
|
:erlang.">="(unquote(var), unquote(last))
|
|
end
|
|
end
|
|
|
|
@doc """
|
|
When used inside quoting, marks that the variable should
|
|
not be hygienized. The argument can be either a variable
|
|
unquoted or an atom representing the variable name.
|
|
|
|
Check `Kernel.SpecialForms.quote/2` for more information.
|
|
"""
|
|
defmacro var!(var, context // nil)
|
|
|
|
defmacro var!(var, context) when is_atom(var) do
|
|
do_var!(var, [], context, __CALLER__)
|
|
end
|
|
|
|
defmacro var!({ name, meta, atom }, context) when is_atom(name) and is_atom(atom) do
|
|
do_var!(name, meta, context, __CALLER__)
|
|
end
|
|
|
|
defmacro var!(x, _context) do
|
|
raise ArgumentError, message: "expected a var to be given to var!, got: #{Macro.to_string(x)}"
|
|
end
|
|
|
|
defp do_var!(name, meta, context, env) do
|
|
# Remove counter and force them to be vars
|
|
meta = :lists.keydelete(:counter, 1, meta)
|
|
meta = :lists.keystore(:var, 1, meta, { :var, true })
|
|
|
|
case Macro.expand(context, env) do
|
|
x when is_atom(x) ->
|
|
{ name, meta, x }
|
|
x ->
|
|
raise ArgumentError, message: "expected var! context to expand to an atom, got: #{Macro.to_string(x)}"
|
|
end
|
|
end
|
|
|
|
@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.
|
|
|
|
Check `Kernel.SpecialForms.quote/2` for more information.
|
|
"""
|
|
defmacro alias!(alias)
|
|
|
|
defmacro alias!(alias) when is_atom(alias) do
|
|
alias
|
|
end
|
|
|
|
defmacro alias!({ :__aliases__, meta, args }) do
|
|
# Simply remove the alias metadata from the node
|
|
# so it does not affect expansion.
|
|
{ :__aliases__, :lists.keydelete(:alias, 1, meta), args }
|
|
end
|
|
|
|
## Definitions implemented in Elixir
|
|
|
|
|
|
@doc """
|
|
Defines a module given by name with the given contents.
|
|
|
|
It returns the module name, the module binary and the
|
|
block contents result.
|
|
|
|
## Examples
|
|
|
|
defmodule Foo do
|
|
def bar, do: :baz
|
|
end
|
|
|
|
Foo.bar #=> :baz
|
|
|
|
## Nesting
|
|
|
|
Nesting a module inside another module affects its name:
|
|
|
|
defmodule Foo do
|
|
defmodule Bar do
|
|
end
|
|
end
|
|
|
|
In the example above, two modules `Foo` and `Foo.Bar` are created.
|
|
When nesting, Elixir automatically creates an alias, allowing the
|
|
second module `Foo.Bar` to be accessed as `Bar` in the same lexical
|
|
scope.
|
|
|
|
This means that, if the module `Bar` is moved to another file,
|
|
the references to `Bar` needs to be updated or an alias needs to
|
|
be explicitly set with the help of `Kernel.SpecialForms.alias/2`.
|
|
|
|
## Dynamic names
|
|
|
|
Elixir module names can be dynamically generated. This is very
|
|
useful for macros. For instance, one could write:
|
|
|
|
defmodule binary_to_atom("Foo\#{1}") do
|
|
# contents ...
|
|
end
|
|
|
|
Elixir will accept any module name as long as the expression
|
|
returns an atom. Note that, when a dynamic name is used, Elixir
|
|
won't nest the name under the current module nor automatically
|
|
set up an alias.
|
|
"""
|
|
defmacro defmodule(alias, do: block) do
|
|
env = __CALLER__
|
|
boot? = bootstraped?(Macro.Env)
|
|
|
|
expanded =
|
|
case boot? do
|
|
true -> Macro.expand(alias, env)
|
|
false -> alias
|
|
end
|
|
|
|
{ expanded, with_alias } =
|
|
case boot? and is_atom(expanded) do
|
|
true ->
|
|
# Expand the module considering the current environment/nesting
|
|
full = expand_module(alias, expanded, env)
|
|
|
|
# Generate the alias for this module definition
|
|
{ new, old } = module_nesting(env_module(env), full)
|
|
meta = [defined: full, context: true] ++ alias_meta(alias)
|
|
|
|
{ full, { :alias, meta, [old, [as: new, warn: false]] } }
|
|
false ->
|
|
{ expanded, nil }
|
|
end
|
|
|
|
{ escaped, _ } = :elixir_quote.escape(block, false)
|
|
module_vars = module_vars(env_vars(env), 0)
|
|
|
|
quote do
|
|
unquote(with_alias)
|
|
:elixir_module.compile(unquote(expanded), unquote(escaped),
|
|
unquote(module_vars), __ENV__)
|
|
end
|
|
end
|
|
|
|
defp alias_meta({ :__aliases__, meta, _ }), do: meta
|
|
defp alias_meta(_), do: []
|
|
|
|
# defmodule :foo
|
|
defp expand_module(raw, _module, _env) when is_atom(raw),
|
|
do: raw
|
|
|
|
# defmodule Hello
|
|
defp expand_module({ :__aliases__, _, [h] }, _module, env),
|
|
do: :elixir_aliases.concat([env.module, h])
|
|
|
|
# defmodule Hello.World
|
|
defp expand_module({ :__aliases__, _, _ } = alias, module, env) do
|
|
case :elixir_aliases.expand(alias, env.aliases, env.macro_aliases, env.lexical_tracker) do
|
|
atom when is_atom(atom) ->
|
|
module
|
|
aliases when is_list(aliases) ->
|
|
:elixir_aliases.concat([env.module, module])
|
|
end
|
|
end
|
|
|
|
# defmodule Elixir.Hello.World
|
|
defp expand_module(_raw, module, env),
|
|
do: :elixir_aliases.concat([env.module, module])
|
|
|
|
# quote vars to be injected into the module definition
|
|
defp module_vars([{ key, kind }|vars], counter) do
|
|
var =
|
|
case is_atom(kind) do
|
|
true -> { key, [], kind }
|
|
false -> { key, [counter: kind], nil }
|
|
end
|
|
|
|
args = [key, kind, binary_to_atom(<<"_@", integer_to_binary(counter)::binary>>), var]
|
|
[{ :{}, [], args }|module_vars(vars, counter+1)]
|
|
end
|
|
|
|
defp module_vars([], _counter) do
|
|
[]
|
|
end
|
|
|
|
# Gets two modules names and return an alias
|
|
# which can be passed down to the alias directive
|
|
# and it will create a proper shortcut representing
|
|
# the given nesting.
|
|
#
|
|
# Examples:
|
|
#
|
|
# module_nesting('Elixir.Foo.Bar', 'Elixir.Foo.Bar.Baz.Bat')
|
|
# { 'Elixir.Baz', 'Elixir.Foo.Bar.Baz' }
|
|
#
|
|
# In case there is no nesting/no module:
|
|
#
|
|
# module_nesting(nil, 'Elixir.Foo.Bar.Baz.Bat')
|
|
# { false, 'Elixir.Foo.Bar.Baz.Bat' }
|
|
#
|
|
defp module_nesting(nil, full),
|
|
do: { false, full }
|
|
|
|
defp module_nesting(prefix, full) do
|
|
case split_module(prefix) do
|
|
[] -> { false, full }
|
|
prefix -> module_nesting(prefix, split_module(full), [], full)
|
|
end
|
|
end
|
|
|
|
defp module_nesting([x|t1], [x|t2], acc, full),
|
|
do: module_nesting(t1, t2, [x|acc], full)
|
|
defp module_nesting([], [h|_], acc, _full),
|
|
do: { binary_to_atom(<<"Elixir.", h::binary>>),
|
|
:elixir_aliases.concat(:lists.reverse([h|acc])) }
|
|
defp module_nesting(_, _, _acc, full),
|
|
do: { false, full }
|
|
|
|
defp split_module(atom) do
|
|
case :binary.split(atom_to_binary(atom), ".", [:global]) do
|
|
["Elixir"|t] -> t
|
|
_ -> []
|
|
end
|
|
end
|
|
|
|
@doc """
|
|
Defines a function with the given name and contents.
|
|
|
|
## Examples
|
|
|
|
defmodule Foo do
|
|
def bar, do: :baz
|
|
end
|
|
|
|
Foo.bar #=> :baz
|
|
|
|
A function that expects arguments can be defined as follow:
|
|
|
|
defmodule Foo do
|
|
def sum(a, b) do
|
|
a + b
|
|
end
|
|
end
|
|
|
|
In the example above, we defined a function `sum` that receives
|
|
two arguments and sums them.
|
|
|
|
"""
|
|
defmacro def(call, expr // nil) do
|
|
define(:def, call, expr, __CALLER__)
|
|
end
|
|
|
|
@doc """
|
|
Defines a function that is private. Private functions are
|
|
only accessible from within the module in which they are defined.
|
|
|
|
Check `def/2` for more information
|
|
|
|
## Examples
|
|
|
|
defmodule Foo do
|
|
def bar do
|
|
sum(1, 2)
|
|
end
|
|
|
|
defp sum(a, b), do: a + b
|
|
end
|
|
|
|
In the example above, `sum` is private and accessing it
|
|
through `Foo.sum` will raise an error.
|
|
"""
|
|
defmacro defp(call, expr // nil) do
|
|
define(:defp, call, expr, __CALLER__)
|
|
end
|
|
|
|
@doc """
|
|
Defines a macro with the given name and contents.
|
|
|
|
## Examples
|
|
|
|
defmodule MyLogic do
|
|
defmacro unless(expr, opts) do
|
|
quote do
|
|
if !unquote(expr), unquote(opts)
|
|
end
|
|
end
|
|
end
|
|
|
|
require MyLogic
|
|
MyLogic.unless false do
|
|
IO.puts "It works"
|
|
end
|
|
|
|
"""
|
|
defmacro defmacro(call, expr // nil) do
|
|
define(:defmacro, call, expr, __CALLER__)
|
|
end
|
|
|
|
@doc """
|
|
Defines a macro that is private. Private macros are
|
|
only accessible from the same module in which they are defined.
|
|
|
|
Check `defmacro/2` for more information
|
|
"""
|
|
defmacro defmacrop(call, expr // nil) do
|
|
define(:defmacrop, call, expr, __CALLER__)
|
|
end
|
|
|
|
defp define(kind, call, expr, env) do
|
|
assert_module_scope(env, kind, 2)
|
|
assert_no_function_scope(env, kind, 2)
|
|
line = env_line(env)
|
|
|
|
{ call, uc } = :elixir_quote.escape(call, true)
|
|
{ expr, ue } = :elixir_quote.escape(expr, true)
|
|
|
|
# Do not check clauses if any expression was unquoted
|
|
check_clauses = not(ue or uc)
|
|
pos = :elixir_locals.cache_env(env)
|
|
|
|
quote do
|
|
:elixir_def.store_definition(unquote(line), unquote(kind), unquote(check_clauses),
|
|
unquote(call), unquote(expr), unquote(pos))
|
|
end
|
|
end
|
|
|
|
@doc %S"""
|
|
Exports a module with a record definition and runtime operations.
|
|
|
|
Please see the `Record` module's documentation for an introduction
|
|
to records in Elixir. The following sections are going into details
|
|
specific to `defrecord`.
|
|
|
|
## Examples
|
|
|
|
defrecord User, name: nil, age: 0
|
|
|
|
The following line defines a module that exports information
|
|
about a record. The definition above provides a shortcut
|
|
syntax for creating and updating the record at compilation
|
|
time:
|
|
|
|
user = User[]
|
|
#=> User[name: nil, age: 0]
|
|
|
|
User[name: "José", age: 25]
|
|
#=> User[name: "José", age: 25]
|
|
|
|
And also a set of functions for working with the record
|
|
at runtime:
|
|
|
|
user = User.new(age: 25)
|
|
user.name #=> Returns the value of name
|
|
user.name("José") #=> Updates the value of name
|
|
|
|
# Update multiple attributes at once:
|
|
user.update(name: "Other", age: 25)
|
|
|
|
# Obtain the keywords representation of a record:
|
|
user.to_keywords #=> [name: "José", age: 25]
|
|
|
|
Since a record is simply a tuple where the first element is
|
|
the record name, we can get the raw record representation as
|
|
follows:
|
|
|
|
inspect User.new, records: false
|
|
#=> { User, nil, 0 }
|
|
|
|
In addition to defining readers and writers for each attribute, Elixir also
|
|
defines an `update_#{attribute}` function to update the value. Such
|
|
functions expect a function as an argument that receives the current
|
|
value and must return the new one. For example, every time the file
|
|
is accessed, the accesses counter can be incremented with:
|
|
|
|
user.update_age(fn(old) -> old + 1 end)
|
|
|
|
## Types
|
|
|
|
Every record defines a type named `t` that can be accessed in typespecs.
|
|
Those types can be specified inside the record definition:
|
|
|
|
defrecord User do
|
|
record_type name: string, age: integer
|
|
end
|
|
|
|
All fields without a specified type are assumed to have type `term`.
|
|
|
|
Assuming the `User` record defined above, it could be used in typespecs
|
|
as follow:
|
|
|
|
@spec handle_user(User.t) :: boolean()
|
|
|
|
## Runtime introspection
|
|
|
|
At runtime, developers can use `__record__` to get information
|
|
about the given record:
|
|
|
|
User.__record__(:name)
|
|
#=> User
|
|
|
|
User.__record__(:fields)
|
|
#=> [name: nil, age: 0]
|
|
|
|
In order to quickly access the index of a field, one can use
|
|
the `__record__` function with `:index` as the first argument:
|
|
|
|
User.__record__(:index, :age)
|
|
#=> 2
|
|
|
|
User.__record__(:index, :unknown)
|
|
#=> nil
|
|
|
|
## Compile-time introspection
|
|
|
|
At compile time, one can access the following information about the record
|
|
from within the record module:
|
|
|
|
* `@record_fields` — a keyword list of record fields with defaults
|
|
* `@record_types` — a keyword list of record fields with types
|
|
|
|
For example:
|
|
|
|
defrecord Foo, bar: nil do
|
|
record_type bar: nil | integer
|
|
IO.inspect @record_fields
|
|
IO.inspect @record_types
|
|
end
|
|
|
|
Prints out:
|
|
|
|
[bar: nil]
|
|
[bar: {:|,[line: ...],[nil,{:integer,[line: ...],nil}]}]
|
|
|
|
Where the last line is a quoted representation of
|
|
|
|
[bar: nil | integer]
|
|
|
|
"""
|
|
defmacro defrecord(name, fields, do_block // [])
|
|
|
|
defmacro defrecord(name, fields, do_block) do
|
|
case is_list(fields) and Keyword.get(fields, :do, false) do
|
|
false -> Record.defrecord(name, fields, do_block)
|
|
other -> Record.defrecord(name, Keyword.delete(fields, :do), do: other)
|
|
end
|
|
end
|
|
|
|
@doc %S"""
|
|
Defines a set of private macros to manipulate a record definition.
|
|
|
|
This macro defines a set of macros private to the current module to
|
|
manipulate the record exclusively at compilation time.
|
|
|
|
Please see the `Record` module's documentation for an introduction
|
|
to records in Elixir.
|
|
|
|
## Examples
|
|
|
|
defmodule User do
|
|
defrecordp :user, [name: "José", age: "25"]
|
|
end
|
|
|
|
In the example above, a set of macros named `user` but with different
|
|
arities will be defined to manipulate the underlying record:
|
|
|
|
# To create records
|
|
user() #=> { :user, "José", 25 }
|
|
user(age: 26) #=> { :user, "José", 26 }
|
|
|
|
# To get a field from the record
|
|
user(record, :name) #=> "José"
|
|
|
|
# To get many fields from the record
|
|
user(record, [:name, :age]) #=> ["José", 25]
|
|
|
|
# To update the record
|
|
user(record, age: 26) #=> { :user, "José", 26 }
|
|
|
|
# To convert the record to keywords
|
|
user(record) #=> [name: "José", age: 25]
|
|
|
|
# To match against the record
|
|
user(name: name) = record
|
|
name #=> "José"
|
|
|
|
By default, Elixir uses the record name as the first element of the tuple.
|
|
In some cases though, this might be undesirable and one can explicitly
|
|
define what the first element of the record should be:
|
|
|
|
defmodule MyServer do
|
|
defrecordp :state, MyServer, data: nil
|
|
end
|
|
|
|
This way, the record created will have `MyServer` as the first element,
|
|
not `:state`:
|
|
|
|
state() #=> { MyServer, nil }
|
|
|
|
## Types
|
|
|
|
`defrecordp` allows a developer to generate a type
|
|
automatically by simply providing a type to its fields.
|
|
The following definition:
|
|
|
|
defrecordp :user,
|
|
name: "José" :: binary,
|
|
age: 25 :: integer
|
|
|
|
Will generate the following type:
|
|
|
|
@typep user_t :: { :user, binary, integer }
|
|
|
|
"""
|
|
defmacro defrecordp(name, tag // nil, fields) do
|
|
Record.defrecordp(name, Macro.expand(tag, __CALLER__), fields)
|
|
end
|
|
|
|
@doc %S"""
|
|
Defines an exception.
|
|
|
|
Exceptions are simply records with three differences:
|
|
|
|
1. Exceptions are required to define a function `exception/1`
|
|
that receives keyword arguments and returns the exception.
|
|
This function is a callback usually invoked by `raise/2`;
|
|
|
|
2. Exceptions are required to provide a `message` field.
|
|
This field must return a String with a formatted error message;
|
|
|
|
3. Unlike records, exceptions are documented by default.
|
|
|
|
Since exceptions are records, `defexception/3` has exactly
|
|
the same API as `defrecord/3`.
|
|
|
|
## Raising exceptions
|
|
|
|
The most common way to raise an exception is via the `raise/2`
|
|
function:
|
|
|
|
defexception MyException, [:message]
|
|
raise MyException,
|
|
message: "did not get what was expected, got: #{inspect value}"
|
|
|
|
In many cases it is more convenient to pass the
|
|
expected value to `raise` and generate the message in the `exception/1`
|
|
callback:
|
|
|
|
defexception MyException, [:message] do
|
|
def exception(opts) do
|
|
msg = "did not get what was expected, got: #{inspect opts[:actual]}"
|
|
MyException[message: msg]
|
|
end
|
|
end
|
|
|
|
raise MyException, actual: value
|
|
|
|
The example above is the preferred mechanism for customizing
|
|
exception messages.
|
|
"""
|
|
defmacro defexception(name, fields, do_block // []) do
|
|
{ fields, do_block } =
|
|
case is_list(fields) and Keyword.get(fields, :do, false) do
|
|
false -> { fields, do_block }
|
|
other -> { Keyword.delete(fields, :do), [do: other] }
|
|
end
|
|
|
|
do_block = Keyword.put(do_block, :do, quote do
|
|
@moduledoc nil
|
|
record_type message: String.t
|
|
|
|
@doc false
|
|
def exception(args), do: new(args)
|
|
|
|
@doc false
|
|
def exception(args, self), do: update(args, self)
|
|
|
|
defoverridable exception: 1, exception: 2
|
|
unquote(Keyword.get do_block, :do)
|
|
end)
|
|
|
|
fields = quote do: [__exception__: :__exception__] ++ unquote(fields)
|
|
record = Record.defrecord(name, fields, do_block)
|
|
|
|
quote do
|
|
{ :module, name, _, _ } = unquote(record)
|
|
|
|
unless :erlang.function_exported(name, :message, 1) do
|
|
Kernel.raise "expected exception #{inspect name} to implement message/1"
|
|
end
|
|
end
|
|
end
|
|
|
|
@doc """
|
|
Defines a module as a protocol and specifies the API that
|
|
should be defined by its implementations.
|
|
|
|
## Examples
|
|
|
|
In Elixir, only `false` and `nil` are considered falsy values.
|
|
Everything else evaluates to true in `if` clauses. Depending
|
|
on the application, it may be important to specify a `blank?`
|
|
protocol that returns a boolean for other data types that should
|
|
be considered `blank?`. For instance, an empty list or an empty
|
|
binary could be considered blanks.
|
|
|
|
We could implement this protocol as follow:
|
|
|
|
defprotocol Blank do
|
|
@doc "Returns true if data is considered blank/empty"
|
|
def blank?(data)
|
|
end
|
|
|
|
Now that the protocol is defined, we can implement it. We need
|
|
to implement the protocol for each Elixir type. For example:
|
|
|
|
# Integers are never blank
|
|
defimpl Blank, for: Integer do
|
|
def blank?(number), do: false
|
|
end
|
|
|
|
# Just empty list is blank
|
|
defimpl Blank, for: List do
|
|
def blank?([]), do: true
|
|
def blank?(_), do: false
|
|
end
|
|
|
|
# Just the atoms false and nil are blank
|
|
defimpl Blank, for: Atom do
|
|
def blank?(false), do: true
|
|
def blank?(nil), do: true
|
|
def blank?(_), do: false
|
|
end
|
|
|
|
And we would have to define the implementation for all types.
|
|
The supported types available are:
|
|
|
|
* Record (see below)
|
|
* Tuple
|
|
* Atom
|
|
* List
|
|
* BitString
|
|
* Integer
|
|
* Float
|
|
* Function
|
|
* PID
|
|
* Port
|
|
* Reference
|
|
* Any (see below)
|
|
|
|
## Protocols + Records
|
|
|
|
The real benefit of protocols comes when mixed with records.
|
|
For instance, Elixir ships with many data types implemented as
|
|
records, like `HashDict` and `HashSet`. We can implement the
|
|
`Blank` protocol for those types as well:
|
|
|
|
defimpl Blank, for: HashDict do
|
|
def blank?(dict), do: Dict.empty?(dict)
|
|
end
|
|
|
|
Since records are tuples, if a protocol is not found a given
|
|
type, it will fallback to `Tuple`.
|
|
|
|
## Fallback to any
|
|
|
|
In some cases, it may be convenient to provide a default
|
|
implementation for all types. This can be achieved by
|
|
setting `@fallback_to_any` to `true` in the protocol
|
|
definition:
|
|
|
|
defprotocol Blank do
|
|
@fallback_to_any true
|
|
def blank?(data)
|
|
end
|
|
|
|
Which can now be implemented as:
|
|
|
|
defimpl Blank, for: Any do
|
|
def blank?(_), do: true
|
|
end
|
|
|
|
One may wonder why such fallback is not true by default.
|
|
|
|
It is two-fold: first, the majority of protocols cannot
|
|
implement an action in a generic way for all types. In fact,
|
|
providing a default implementation may be harmful, because users
|
|
may rely on the default implementation instead of providing a
|
|
specialized one.
|
|
|
|
Second, falling back to `Any` adds an extra lookup to all types,
|
|
which is unnecessary overhead unless an implementation for Any is
|
|
required.
|
|
|
|
## Types
|
|
|
|
As in records, defining a protocol automatically defines a type
|
|
named `t`, which can be used as:
|
|
|
|
@spec present?(Blank.t) :: boolean
|
|
def present?(blank) do
|
|
not Blank.blank?(blank)
|
|
end
|
|
|
|
The `@spec` above expresses that all types allowed to implement the
|
|
given protocol are valid argument types for the given function.
|
|
|
|
## Reflection
|
|
|
|
Any protocol module contains three extra functions:
|
|
|
|
|
|
* `__protocol__/1` - returns the protocol name when :name is given,
|
|
and a keyword list with the protocol functions
|
|
when :functions is given;
|
|
|
|
* `impl_for/1` - receives a structure and returns the module that implements
|
|
the protocol for the structure, nil otherwise;
|
|
|
|
* `impl_for!/1` - same as above but raises an error if an implementation is not found
|
|
|
|
## Consolidation
|
|
|
|
In order to cope with code loading in development, protocols in
|
|
Elixir provide a slow implementation of protocol dispatching in
|
|
development.
|
|
|
|
In order to speed up dispatching in production environments, where
|
|
all implementations are now up-front, Elixir provides a feature
|
|
called protocol consolidation. For this reason, all protocols are
|
|
compiled with `debug_info` set to true, regardless of the option
|
|
set by `elixirc` compiler.
|
|
|
|
For more information on how to apply protocol consolidation to
|
|
a given project, please check the `mix compile.protocols` task.
|
|
"""
|
|
defmacro defprotocol(name, do: block) do
|
|
Protocol.defprotocol(name, do: block)
|
|
end
|
|
|
|
@doc """
|
|
Defines an implementation for the given protocol. See
|
|
`defprotocol/2` for examples.
|
|
|
|
Inside an implementation, the name of the protocol can be accessed
|
|
via `@protocol` and the current target as `@for`.
|
|
"""
|
|
defmacro defimpl(name, opts, do_block // []) do
|
|
merged = Keyword.merge(opts, do_block)
|
|
merged = Keyword.put_new(merged, :for, __CALLER__.module)
|
|
Protocol.defimpl(name, merged)
|
|
end
|
|
|
|
@doc """
|
|
Makes the given functions in the current module overridable. An overridable
|
|
function is lazily defined, allowing a developer to customize it.
|
|
|
|
## Example
|
|
|
|
defmodule DefaultMod do
|
|
defmacro __using__(_opts) do
|
|
quote do
|
|
def test(x, y) do
|
|
x + y
|
|
end
|
|
|
|
defoverridable [test: 2]
|
|
end
|
|
end
|
|
end
|
|
|
|
defmodule InheritMod do
|
|
use DefaultMod
|
|
|
|
def test(x, y) do
|
|
x * y + super(x, y)
|
|
end
|
|
end
|
|
|
|
As seen as in the example `super` can be used to call the default
|
|
implementation.
|
|
"""
|
|
defmacro defoverridable(tuples) do
|
|
quote do
|
|
Module.make_overridable(__MODULE__, unquote(tuples))
|
|
end
|
|
end
|
|
|
|
@doc """
|
|
`use` is a simple mechanism for using a given module into
|
|
the current context.
|
|
|
|
## Examples
|
|
|
|
For example, in order to write tests using the ExUnit framework,
|
|
a developer should use the `ExUnit.Case` module:
|
|
|
|
defmodule AssertionTest do
|
|
use ExUnit.Case, async: true
|
|
|
|
def test_always_pass do
|
|
true = true
|
|
end
|
|
end
|
|
|
|
By calling `use`, a hook called `__using__` will be invoked in
|
|
`ExUnit.Case` which will then do the proper setup.
|
|
|
|
Simply put, `use` is simply a translation to:
|
|
|
|
defmodule AssertionTest do
|
|
require ExUnit.Case
|
|
ExUnit.Case.__using__([async: true])
|
|
|
|
def test_always_pass do
|
|
true = true
|
|
end
|
|
end
|
|
|
|
"""
|
|
defmacro use(module, args // []) do
|
|
expanded = Macro.expand(module, __CALLER__)
|
|
|
|
case is_atom(expanded) do
|
|
false ->
|
|
raise ArgumentError, message: "invalid arguments for use, expected an atom or alias as argument"
|
|
true ->
|
|
quote do
|
|
require unquote(expanded)
|
|
unquote(expanded).__using__(unquote(args))
|
|
end
|
|
end
|
|
end
|
|
|
|
@doc """
|
|
Defines the given functions in the current module that will
|
|
delegate to the given `target`. Functions defined with
|
|
`defdelegate` are public and are allowed to be invoked
|
|
from external. If you find yourself wishing to define a
|
|
delegation as private, you should likely use import
|
|
instead.
|
|
|
|
Delegation only works with functions, delegating to macros
|
|
is not supported.
|
|
|
|
## Options
|
|
|
|
* `:to` - The expression to delegate to. Any expression
|
|
is allowed and its results will be calculated on runtime;
|
|
|
|
* `:as` - The function to call on the target given in `:to`.
|
|
This parameter is optional and defaults to the name being
|
|
delegated.
|
|
|
|
* `:append_first` - If true, when delegated, first argument
|
|
passed to the delegate will be relocated to the end of the
|
|
arguments when dispatched to the target. The motivation behind
|
|
this is because Elixir normalizes the "handle" as a first
|
|
argument and some Erlang modules expect it as last argument.
|
|
|
|
## Examples
|
|
|
|
defmodule MyList do
|
|
defdelegate reverse(list), to: :lists
|
|
defdelegate [reverse(list), map(callback, list)], to: :lists
|
|
defdelegate other_reverse(list), to: :lists, as: :reverse
|
|
end
|
|
|
|
MyList.reverse([1, 2, 3])
|
|
#=> [3,2,1]
|
|
|
|
MyList.other_reverse([1, 2, 3])
|
|
#=> [3,2,1]
|
|
|
|
"""
|
|
defmacro defdelegate(funs, opts) do
|
|
funs = Macro.escape(funs, unquote: true)
|
|
quote bind_quoted: [funs: funs, opts: opts] do
|
|
target = Keyword.get(opts, :to) ||
|
|
raise ArgumentError, message: "Expected to: to be given as argument"
|
|
|
|
append_first = Keyword.get(opts, :append_first, false)
|
|
|
|
lc fun inlist List.wrap(funs) do
|
|
{ name, args } =
|
|
case Macro.decompose_call(fun) do
|
|
{ _, _ } = pair -> pair
|
|
_ -> raise ArgumentError, message: "invalid syntax in defdelegate #{Macro.to_string(fun)}"
|
|
end
|
|
|
|
actual_args =
|
|
case append_first and args != [] do
|
|
true -> tl(args) ++ [hd(args)]
|
|
false -> args
|
|
end
|
|
|
|
fun = Keyword.get(opts, :as, name)
|
|
|
|
def unquote(name)(unquote_splicing(args)) do
|
|
unquote(target).unquote(fun)(unquote_splicing(actual_args))
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
## Sigils
|
|
|
|
@doc """
|
|
Handles the sigil %S. It simply returns a string
|
|
without escaping characters and without interpolations.
|
|
|
|
## Examples
|
|
|
|
iex> %S(foo)
|
|
"foo"
|
|
iex> %S(f\#{o}o)
|
|
"f\\\#{o}o"
|
|
|
|
"""
|
|
defmacro sigil_S(string, []) do
|
|
string
|
|
end
|
|
|
|
@doc """
|
|
Handles the sigil %s. It returns a string as if it was double quoted
|
|
string, unescaping characters and replacing interpolations.
|
|
|
|
## Examples
|
|
|
|
iex> %s(foo)
|
|
"foo"
|
|
iex> %s(f\#{:o}o)
|
|
"foo"
|
|
|
|
"""
|
|
defmacro sigil_s({ :<<>>, line, pieces }, []) do
|
|
{ :<<>>, line, Macro.unescape_tokens(pieces) }
|
|
end
|
|
|
|
@doc """
|
|
Handles the sigil %C. It simply returns a char list
|
|
without escaping characters and without interpolations.
|
|
|
|
## Examples
|
|
|
|
iex> %C(foo)
|
|
'foo'
|
|
iex> %C(f\#{o}o)
|
|
'f\\\#{o}o'
|
|
|
|
"""
|
|
defmacro sigil_C({ :<<>>, _line, [string] }, []) when is_binary(string) do
|
|
String.to_char_list!(string)
|
|
end
|
|
|
|
@doc """
|
|
Handles the sigil %c. It returns a char list as if it were a single
|
|
quoted string, unescaping characters and replacing interpolations.
|
|
|
|
## Examples
|
|
|
|
iex> %c(foo)
|
|
'foo'
|
|
iex> %c(f\#{:o}o)
|
|
'foo'
|
|
|
|
"""
|
|
|
|
# We can skip the runtime conversion if we are
|
|
# creating a binary made solely of series of chars.
|
|
defmacro sigil_c({ :<<>>, _line, [string] }, []) when is_binary(string) do
|
|
String.to_char_list!(Macro.unescape_string(string))
|
|
end
|
|
|
|
defmacro sigil_c({ :<<>>, line, pieces }, []) do
|
|
binary = { :<<>>, line, Macro.unescape_tokens(pieces) }
|
|
quote do: String.to_char_list!(unquote(binary))
|
|
end
|
|
|
|
@doc """
|
|
Handles the sigil %r. It returns a Regex pattern.
|
|
|
|
## Examples
|
|
|
|
iex> Regex.match?(%r(foo), "foo")
|
|
true
|
|
|
|
"""
|
|
defmacro sigil_r({ :<<>>, _line, [string] }, options) when is_binary(string) do
|
|
binary = Macro.unescape_string(string, fn(x) -> Regex.unescape_map(x) end)
|
|
regex = Regex.compile!(binary, :binary.list_to_bin(options))
|
|
Macro.escape(regex)
|
|
end
|
|
|
|
defmacro sigil_r({ :<<>>, line, pieces }, options) do
|
|
binary = { :<<>>, line, Macro.unescape_tokens(pieces, fn(x) -> Regex.unescape_map(x) end) }
|
|
quote do: Regex.compile!(unquote(binary), unquote(:binary.list_to_bin(options)))
|
|
end
|
|
|
|
@doc """
|
|
Handles the sigil %R. It returns a Regex pattern without escaping
|
|
nor interpreting interpolations.
|
|
|
|
## Examples
|
|
|
|
iex> Regex.match?(%R(f\#{1,3}o), "f\#o")
|
|
true
|
|
|
|
"""
|
|
defmacro sigil_R({ :<<>>, _line, [string] }, options) when is_binary(string) do
|
|
regex = Regex.compile!(string, :binary.list_to_bin(options))
|
|
Macro.escape(regex)
|
|
end
|
|
|
|
@doc """
|
|
Handles the sigil %w. It returns a list of "words" split by whitespace.
|
|
|
|
## Modifiers
|
|
|
|
* `s`: strings (default)
|
|
* `a`: atoms
|
|
* `c`: char lists
|
|
|
|
## Examples
|
|
|
|
iex> %w(foo \#{:bar} baz)
|
|
["foo", "bar", "baz"]
|
|
iex> %w(--source test/enum_test.exs)
|
|
["--source", "test/enum_test.exs"]
|
|
iex> %w(foo bar baz)a
|
|
[:foo, :bar, :baz]
|
|
|
|
"""
|
|
|
|
defmacro sigil_w({ :<<>>, _line, [string] }, modifiers) when is_binary(string) do
|
|
split_words(Macro.unescape_string(string), modifiers)
|
|
end
|
|
|
|
defmacro sigil_w({ :<<>>, line, pieces }, modifiers) do
|
|
binary = { :<<>>, line, Macro.unescape_tokens(pieces) }
|
|
split_words(binary, modifiers)
|
|
end
|
|
|
|
@doc """
|
|
Handles the sigil %W. It returns a list of "words" split by whitespace
|
|
without escaping nor interpreting interpolations.
|
|
|
|
## Modifiers
|
|
|
|
* `s`: strings (default)
|
|
* `a`: atoms
|
|
* `c`: char lists
|
|
|
|
## Examples
|
|
|
|
iex> %W(foo \#{bar} baz)
|
|
["foo", "\\\#{bar}", "baz"]
|
|
|
|
"""
|
|
defmacro sigil_W({ :<<>>, _line, [string] }, modifiers) when is_binary(string) do
|
|
split_words(string, modifiers)
|
|
end
|
|
|
|
defp split_words("", _modifiers), do: []
|
|
|
|
defp split_words(string, modifiers) do
|
|
mod =
|
|
case modifiers do
|
|
[] -> ?s
|
|
[mod] when mod == ?s or mod == ?a or mod == ?c -> mod
|
|
_else -> raise ArgumentError, message: "modifier must be one of: s, a, c"
|
|
end
|
|
|
|
case is_binary(string) do
|
|
true ->
|
|
case mod do
|
|
?s -> String.split(string)
|
|
?a -> lc p inlist String.split(string), do: binary_to_atom(p)
|
|
?c -> lc p inlist String.split(string), do: String.to_char_list!(p)
|
|
end
|
|
false ->
|
|
case mod do
|
|
?s -> quote do: String.split(unquote(string))
|
|
?a -> quote do: lc(p inlist String.split(unquote(string)), do: binary_to_atom(p))
|
|
?c -> quote do: lc(p inlist String.split(unquote(string)), do: String.to_char_list!(p))
|
|
end
|
|
end
|
|
end
|
|
|
|
## Shared functions
|
|
|
|
# We need this check only for bootstrap purposes.
|
|
# Once Kernel is loaded and we recompile, it is a no-op.
|
|
case :code.ensure_loaded(Kernel) do
|
|
{ :module, _ } ->
|
|
defp bootstraped?(_), do: true
|
|
defp internal?, do: false
|
|
{ :error, _ } ->
|
|
defp bootstraped?(module), do: :code.ensure_loaded(module) == { :module, module }
|
|
defp internal?, do: :elixir_compiler.get_opt(:internal)
|
|
end
|
|
|
|
defp assert_module_scope(env, fun, arity) do
|
|
case env_module(env) do
|
|
nil -> raise ArgumentError, message: "cannot invoke #{fun}/#{arity} outside module"
|
|
_ -> :ok
|
|
end
|
|
end
|
|
|
|
defp assert_no_function_scope(env, fun, arity) do
|
|
case env_function(env) do
|
|
nil -> :ok
|
|
_ -> raise ArgumentError, message: "cannot invoke #{fun}/#{arity} inside function/macro"
|
|
end
|
|
end
|
|
|
|
defp env_module(env), do: :erlang.element(2, env)
|
|
defp env_line(env), do: :erlang.element(4, env)
|
|
defp env_function(env), do: :erlang.element(5, env)
|
|
defp env_context(env), do: :erlang.element(6, env)
|
|
defp env_vars(env), do: :erlang.element(13, env)
|
|
|
|
defp expand_compact([{ :compact, false }|t]), do: expand_compact(t)
|
|
defp expand_compact([{ :compact, true }|t]), do: [:compact|expand_compact(t)]
|
|
defp expand_compact([h|t]), do: [h|expand_compact(t)]
|
|
defp expand_compact([]), do: []
|
|
end
|