Files
n8n-openai-adapter/lib/elixir/builtin.ex
T
2012-05-13 15:33:57 +02:00

2283 lines
53 KiB
Elixir

import Elixir.Builtin, except: [raise: 1, raise: 2]
defmodule Elixir.Builtin do
@moduledoc """
`Elixir.Builtin` provides the default macros and functions
Elixir imports into your environment. Those macros and functions
can be skipped or cherry-picked via the `import` macro. For
instance, if you want to tell Elixir to not import the `case`
macro, you can do:
import Elixir.Builtin, except: [case: 2]
Elixir also has special forms that are always imported and
cannot be skipped. These are described in `Elixir.SpecialForms`.
Some of the functions described in this module are simply
a proxy to its Erlang counterpart. Although they are documented
here for convenience, you can access their original documentation
at http://www.erlang.org/doc/man/erlang.html.
"""
@doc """
Arithmetic plus. Allowed in guard clauses.
## Examples
1 + 2 #=> 3
"""
defmacro :+.(left, right)
@doc """
Arithmetic minus. Allowed in guard clauses.
## Examples
1 - 2 #=> -1
"""
defmacro :-.(left, right)
@doc """
Arithmetic multiplication. Allowed in guard clauses.
## Examples
1 * 2 #=> 2
"""
defmacro :*.(left, right)
@doc """
Arithmetic division. Differently from 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
1 / 2 #=> 0.5
2 / 1 #=> 2.0
"""
defmacro :/.(left, right)
@doc """
Sends a message to the process identified on the left.
A process can be identified bu its PID or, if it is registered,
by an atom.
## Examples
process = Process.self
process <- { :ok, "Sending myself a message" }
"""
defmacro :<-.(left, right)
@doc """
Concatenates two lists. Allowed in guard clauses.
## Examples
[1] ++ [2,3]
#=> [1,2,3]
'foo' ++ 'bar'
#=> 'foobar'
"""
defmacro :++.(left, right)
@doc """
Removes the first occorrence of an item on the left
for each item on the right. Allowed in guard clauses.
## Examples
[1,2,3] -- [1,2]
#=> [3]
[1,2,3,2,1] -- [1,2,2]
#=> [3,1]
"""
defmacro :--.(left, right)
@doc """
Boolean or. Arguments needs to necessarily be booleans.
Allowed in guard clauses.
## Examples
true or false
#=> true
"""
defmacro :or.(left, right)
@doc """
Boolean and. Arguments needs to necessarily be booleans.
Allowed in guard clauses.
## Examples
true and false
#=> false
"""
defmacro :and.(left, right)
@doc """
Boolean xor. Arguments needs to necessarily be booleans.
Allowed in guard clauses.
## Examples
true xor false
#=> true
"""
defmacro :xor.(left, right)
@doc """
Boolean not. Argument needs to necessarily be a boolean.
Allowed in guard clauses.
## Examples
not false
#=> true
"""
defmacro :not.(arg)
@doc """
Return true if left is less than right.
As Erlang, Elixir can compare any term. Allowed in guard clauses.
## Examples
1 < 2
#=> true
"""
defmacro :<.(left, right)
@doc """
Return true if left is more than right.
As Erlang, Elixir can compare any term. Allowed in guard clauses.
## Examples
1 > 2
#=> false
"""
defmacro :>.(left, right)
@doc """
Return true if left is less than or equal to right.
As Erlang, Elixir can compare any term. Allowed in guard clauses.
## Examples
1 <= 2
#=> true
"""
defmacro :<=.(left, right)
@doc """
Return true if left is more than or equal to right.
As Erlang, Elixir can compare any term. Allowed in guard clauses.
## Examples
1 >= 2
#=> false
"""
defmacro :>=.(left, right)
@doc """
Returns true if the two items are equal.
This operator considers 1 and 1.0 to be equal. For strict
comparison, use `===` instead.
As Erlang, Elixir can compare any term. Allowed in guard clauses.
## Examples
1 == 2
#=> false
1 == 1.0
#=> true
"""
defmacro :==.(left, right)
@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.
As Erlang, Elixir can compare any term. Allowed in guard clauses.
## Examples
1 != 2
#=> true
1 != 1.0
#=> false
"""
defmacro :!=.(left, right)
@doc """
Returns true if the two items are strictly equal.
As Erlang, Elixir can compare any term. Allowed in guard clauses.
## Examples
1 === 2
#=> false
1 === 1.0
#=> false
"""
defmacro :===.(left, right)
@doc """
Returns true if the two items are strictly not equal.
As Erlang, Elixir can compare any term. Allowed in guard clauses.
## Examples
1 !== 2
#=> true
1 !== 1.0
#=> true
"""
defmacro :!==.(left, right)
@doc """
When used inside quoting, marks that the variable should not
be hygienezed. Check `Elixir.SpecialForms.quote/1` for more
information.
"""
defmacro var!(var)
@doc """
Returns an integer or float which is the arithmetical absolute value of `number`.
Allowed in guard tests.
## Examples
abs(-3.33) #=> 3.33
abs(-3) #=> 3
"""
def abs(number)
@doc """
Returns a binary which corresponds to the text representation of `atom`.
If `encoding` is latin1, there will be one byte for each character in the text
representation. If `encoding` is utf8 or unicode, the characters will be encoded
using UTF-8 (meaning that characters from 16#80 up to 0xFF will be encoded in
two bytes).
## Examples
atom_to_binary(:elixir, utf8) #=> "elixir"
"""
def atom_to_binary(atom, encoding)
@doc """
Returns a string which corresponds to the text representation of `atom`.
## Examples
atom_to_list(:elixir) #=> 'elixir'
"""
def atom_to_list(atom)
@doc """
Returns the atom whose text representation is `binary`. If `encoding` is latin1,
no translation of bytes in the binary is done. If `encoding` is utf8 or unicode,
the binary must contain valid UTF-8 sequences; furthermore, only Unicode
characters up to 0xFF are allowed.
## Examples
binary_to_atom("elixir", :utf8) #=> :elixir
"""
def binary_to_atom(binary, encoding)
@doc """
Works like `binary_to_atom/2`, but the atom must already exist.
"""
def binary_to_existing_atom(binary, encoding)
@doc """
Returns a list of integers which correspond to the bytes of `binary`.
"""
def binary_to_list(binary)
@doc """
As binary_to_list/1, but returns a list of integers corresponding to the bytes
from position `start` to position `stop` in `binary`. Positions in the binary
are numbered starting from 1.
"""
def binary_to_list(binary, start, stop)
@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
binary_to_term(term_to_binary("foo")) #=> true
"""
def binary_to_term(binary)
@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
binary_to_term(term_to_binary("foo"), [:safe])
"""
def binary_to_term(binary, options)
@doc """
Returns an integer which is the size in bits of `bitstring`.
Allowed in guard tests.
## Examples
bit_size(<<433|16,3|3>>) #=> 19
bit_size(<<1,2,3>>) #=> 24
"""
def bit_size(bitstring)
@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).
"""
def bitstring_to_list(bitstring)
@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
byte_size(<<433|16,3|3>>) #=> 3
byte_size(<<1,2,3>>) #=> 3
"""
def byte_size(bitstring)
@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)
"""
def exit(reason)
@doc """
Converts the given number to a float. Allowed in guard clauses.
"""
def float(number)
@doc """
Returns a char list which corresponds to the text representation of the given float.
## Examples
float_to_list(7.0)
#=> '7.00000000000000000000e+00'
"""
def float_to_list(number)
@doc """
The same as halt(0, []).
"""
def halt()
@doc """
The same as halt(status, []).
"""
def halt(status)
@doc """
Halts the Erlang runtime system where the first argument status must be a
non-negative integer, a char list, or the atom `:abort`.
* If an integer, the runtime system exits with the integer value which
is returned to the Operating System;
* If a char list, an erlang crash dump is produced with status as slogan,
and then the runtime system exits with status code 1;
* If `:abort`, the runtime system aborts producing a core dump, if that is
enabled in the operating system.
Note that on many platforms, only the status codes 0-255 are supported
by the operating system.
For integer status, Erlang runtime system closes all ports and allows async
threads to finish their operations before exiting. To exit without such
flushing, pass options [flush: false] instead.
## Examples
halt(0)
halt(1, flush: false)
halt(:abort)
"""
def halt(status, options)
@doc """
Returns the head of a list, raises badarg if the list is empty.
"""
def hd(list)
@doc """
Returns a char list which corresponds to the text representation of the given integer.
## Examples
integer_to_list(7)
#=> '7'
"""
def integer_to_list(number)
@doc """
Returns a char list which corresponds to the text representation of the
given integer in the given case.
## Examples
integer_to_list(1023, 16).
#=> "3FF"
"""
def integer_to_list(number, base)
@doc """
Returns the size of an iolist.
## Examples
iolist_size([1,2|<<3,4>>])
#=> 4
"""
def iolist_size(item)
@doc """
Returns a binary which is made from the integers and binaries in iolist.
## Examples
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>>
"""
def iolist_to_binary(item)
@doc """
Returns `true` if `term` is an atom; otherwise returns `false`.
Allowed in guard tests.
"""
def is_atom(term)
@doc """
Returns `true` if `term` is a binary; otherwise returns `false`.
A binary always contains a complete number of bytes.
Allowed in guard tests.
"""
def is_binary(term)
@doc """
Returns `true` if `term` is a bitstring (including a binary); otherwise returns `false`.
Allowed in guard tests.
"""
def is_bitstring(term)
@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.
"""
def is_boolean(term)
@doc """
Returns `true` if `term` is a floating point number; otherwise returns `false`.
Allowed in guard tests.
"""
def is_float(term)
@doc """
Returns `true` if `term` is a function; otherwise returns `false`.
Allowed in guard tests.
"""
def is_function(term)
@doc """
Returns `true` if `term` is a function that can be applied with `arity` number of arguments;
otherwise returns `false`.
Allowed in guard tests.
"""
def is_function(term, arity)
@doc """
Returns `true` if `term` is an integer; otherwise returns `false`.
Allowed in guard tests.
"""
def is_integer(term)
@doc """
Returns `true` if `term` is a list with zero or more elements; otherwise returns `false`.
Allowed in guard tests.
"""
def is_list(term)
@doc """
Returns `true` if `term` is either an integer or a floating point number;
otherwise returns `false`.
Allowed in guard tests.
"""
def is_number(term)
@doc """
Returns `true` if `term` is a pid (process identifier); otherwise returns `false`.
Allowed in guard tests.
"""
def is_pid(term)
@doc """
Returns `true` if `term` is a port identifier; otherwise returns `false`.
Allowed in guard tests.
"""
def is_port(term)
@doc """
Returns `true` if `term` is a reference; otherwise returns `false`.
Allowed in guard tests.
"""
def is_reference(term)
@doc """
Returns `true` if `term` is a tuple; otherwise returns `false`.
Allowed in guard tests.
"""
def is_tuple(term)
@doc """
Returns the length of `list`.
Allowed in guard tests.
## Examples
length([1,2,3,4,5,6,7,8,9]) #=> 9
"""
def length(list)
@doc """
Returns the atom whose text representation is `char_list`.
## Examples
list_to_atom('elixir') #=> :elixir
"""
def list_to_atom(char_list)
@doc """
Returns a binary which is made from the content of `char_list`.
## Examples
list_to_binary('Elixir') #=> "Elixir"
"""
def list_to_binary(char_list)
@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
bin1 = <<1,2,3>>
bin2 = <<4,5>>
bin3 = <<6,7|4>>
list_to_binary([bin1,1,[2,3,bin2],4|bin3])
#=> <<1,2,3,1,2,3,4,5,4,6,7|4>>
"""
def list_to_bitstring(bitstring_list)
@doc """
Returns the atom whose text representation is `char_list`, but only if there already
exists such atom.
"""
def list_to_existing_atom(char_list)
@doc """
Returns the float whose text representation is `char_list`.
## Examples
list_to_float('2.2017764e+0') #=> 2.2017764
"""
def list_to_float(char_list)
@doc """
Returns an integer whose text representation is `char_list`.
## Examples
list_to_integer('123') #=> 123
"""
def list_to_integer(char_list)
@doc """
Returns an integer whose text representation in base `base` is `char_list`.
## Examples
> list_to_integer('3FF', 16) #=> 1023
"""
def list_to_integer(char_list, base)
@doc """
Returns a pid whose text representation is `char_list`.
## Examples
list_to_pid('<0.41>') #=> <0.4.1>
"""
def list_to_pid(char_list)
@doc """
Returns a tuple which corresponds to `list`. `list` can contain any Erlang terms.
## Examples
list_to_tuple([share, [:elixir, 163]]). #=> {share, [:elixir, 163]}
"""
def list_to_tuple(list)
@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()
#=> #Ref<0.0.0.135>
"""
def make_ref()
@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
max(1, 2) #=> 2
"""
def max(first, second)
@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
min(1, 2) #=> 1
"""
def min(first, second)
@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.
"""
def node
@doc """
Returns the node where the given argmuent 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.
"""
def node(arg)
@doc """
Returns a char list which corresponds to the text representation of pid.
This function is intended for debugging and for use in the Erlang operating
system. It should not be used in application programs.
"""
def pid_to_list(pid)
@doc """
Returns an integer by rounding the given number.
Allowed in guard tests.
## Examples
round(5.5) #=> 6
"""
def round(number)
@doc """
Returns the size of the given argument, which must be a tuple
or a binary. If possible, please use tuple_size or binary_size.
"""
def size(arg)
@doc """
Spawns the given function and returns its pid.
Check the module `Process` for other functions that handle
process, including spawning functions in nodes.
## Examples
current = Process.self
child = spawn(fn -> current <- { Process.self, 1 + 2 } end)
receive
{ ^child, 3 } -> IO.puts "Received 3 back"
end
"""
def spawn(fun)
@doc """
Spawns the given module and function passing the given args
and returns its pid.
Check the module `Process` for other functions that handle
process, including spawning functions in nodes.
## Examples
spawn(SomeModule, :function, [1,2,3])
"""
def spawn(module, args, fun)
@doc """
Spawns the given function, links it to the current process and returns its pid.
Check the module `Process` for other functions that handle
process, including spawning functions in nodes.
## Examples
current = Process.self
child = spawn_link(fn -> current <- { Process.self, 1 + 2 } end)
receive
{ ^child, 3 } ->
IO.puts "Received 3 back"
end
"""
def spawn_link(fun)
@doc """
Spawns the given module and function passing the given args,
links it to the current process and returns its pid.
Check the module `Process` for other functions that handle
process, including spawning functions in nodes.
## Examples
spawn_link(SomeModule, :function, [1,2,3])
"""
def spawn_link(module, args, fun)
@doc """
Returns a binary data 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 Erlang.
"""
def term_to_binary(term)
@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
"""
def term_to_binary(term, opts)
@doc """
A non-local return from a function. Check try/2 for more information.
"""
def throw(term)
@doc """
Returns the tail of a list. Raises ArgumentError if the list is empty.
"""
def tl(list)
@doc """
Returns an integer by the truncating the given number.
Allowed in guard clauses.
## Examples
trunc(5.5) #=> 5
"""
def trunc(number)
@doc """
Returns the size of a tuple.
"""
def tuple_size(tuple)
@doc """
Converts a tuple to a list.
"""
def tuple_to_list(tuple)
@doc """
Defines a module given by name with the given contents.
## Examples
defmodule Foo do
def bar, do: :baz
end
Foo.bar #=> :baz
## Nesting
Nesting a module inside the other affects its name:
defmodule Foo do
defmodule Bar do
end
end
In the example above, two modules `Foo` and `Foo.Bar`. The
second can be accessed as `Bar` inside `Foo` in the same
lexical scope. If the module Bar is moved away to another
file, it needs to be referenced via the full name or a
reference need to be set with the help of `refer/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}", :utf8) do
# contents ...
end
Elixir will accept any module name as long as the expression
returns an atom.
"""
defmacro defmodule(name, do: contents)
@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 sum them.
## Dynamic generation with atoms
Elixir follows the same rule as Erlang when it comes to
function invocations. Calling a function is the same thing
as "invoking at atom". That said, we could invoke a function
named sum in these two equivalent ways:
sum(1, 2)
:sum.(1, 2)
We can also use the atom format to define functions:
defmodule Foo do
def :sum.(a, b) do
a + b
end
end
In general, a developer never needs to use the format above
except when he wants to dynamically define functions with macros.
In such scenarios, the name needs to be given dynamically via
the unquoting mechanism.
Imagine a macro that receives keywords and defines a function
for each entry in the keyword, using the key as function name
and the value as the value returned by the function:
defmacro defkv(keywords) do
Enum.map keywords, fn {k,v} ->
quote do
def unquote(k).() do
unquote(v)
end
end
end
end
This macro could be invoked as:
defkv one: 1, two: 2
Notice in the example above, we define the function as `def unquote(k).()`
because each entry `k` is a an atom and invoking `def unquote(k)()`
would be invalid Elixir syntax.
"""
defmacro def(name, do: contents)
@doc """
This macro allows a function to be defined more explicitly
by accepting the name, args and guards as different entries.
Differently from `def/2`, the macro arguments are evaluated
and therefore requires quoting.
The `name` must be an atom, the `arguments` a list where each
element represents another argument and `guards` a list of
clauses, where each clause is disjunct.
## Examples
The most common mistake when using this macro is to pass the
arguments without quoting:
def :some_function, [first_arg, second_arg], is_list(first_arg) do
# ...
end
However, the example above will fail because it will attempt to
evaluate `[first_arg, second_arg]` and fail because the variable
`first_arg` is not defined. Therefore, we need to use quote:
name = :some_function
args = quote(do: [first_arg, second_arg])
guards = quote(do: is_list(first_arg))
def name, args, guards do
# ...
end
"""
defmacro def(name, args, guards, do: contents)
@doc """
Defines a function that is private. Private functions
can only be accessible from the same module it is 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(name, do: contents)
@doc """
The same as `def/4` but generates a private function.
"""
defmacro defp(name, args, guards, do: contents)
@doc """
Define a record given by name and values.
## Examples
defrecord FileInfo, atime: nil, mtime: nil
The line above will define a module named `FileInfo` which
contains a function named `new` that returns a new record
and other functions to read and set the values in the
record. Therefore, we can do:
file_info = FileInfo.new(atime: now())
file_info.atime #=> Returns the value of atime
file_info.atime(now()) #=> Updates the value of atime
Internally, a record is simply a tuple where the first element is
the record module name. This can be noticed if we print the record:
IO.puts FileInfo.new
{ FileInfo, nil, nil }
## Default based functions
Depending on the default value, Elixir will define helpers to interact
with the record. For example, ExUnit defines a record which keeps
track of how many tests were executed and the failures that happened
The record definition is similar to:
defrecord Config, counter: 0, failures: []
Since `counter` is an integer, Elixir automatically defines a helper
named `increment_counter` that will increase the counter value:
Config.new.increment_counter.counter #=> 1
`increment_counter` also accepts a number of increment as argument:
Config.new.increment_counter(10).counter #=> 10
Besides, if the default is a list, Elixir will define three helpers:
* `merge_field` - Receives keywords and merge it into the current value;
* `prepend_field` - Receives another list and prepend its values
## Documentation
By default records are not documented and have @moduledoc set to false.
This can be changed by passing a moduledoc option after values:
defrecord Config, [counter: 0, failures: []], moduledoc: "A simple record"
"""
defmacro defrecord(name, values, opts // [], do_block // []) do
Record.defrecord(name, values, Keyword.merge(opts, do_block))
end
@doc """
Defines an exception. It follows exactly the same API as record.
The defined record must implement `message/1` as API, otherwise
an error is raised. Check exception.ex for examples.
"""
defmacro defexception(name, values, opts // [], do_block // []) do
opts = Keyword.merge(opts, do_block)
opts = Keyword.put(opts, :do, quote do
unquote(Keyword.get opts, :do)
def exception(args), do: new(args)
def exception(args, self), do: self
end)
opts = case Keyword.key?(opts, :moduledoc) do
false ->
Keyword.put(opts, :moduledoc, nil)
_ ->
opts
end
values = [{ :__exception__, :__exception__ }|values]
record = Record.defrecord(name, values, opts)
check = quote do
name = Module.concat __MODULE__, unquote(name)
unless List.member?(name.__info__(:functions), { :message, 1 }),
do: raise "Expected #{name} to implement message/1"
end
[record, check]
end
@doc """
Check if the given structure is an exception.
## Examples
is_exception(Error.new) #=> true
is_exception(1) #=> false
"""
defmacro is_exception(thing) do
quote do
in_guard do
is_tuple(unquote(thing)) and :erlang.element(2, unquote(thing)) == :__exception__
else
result = unquote(thing)
is_tuple(result) and :erlang.element(2, result) == :__exception__
end
end
end
@doc """
Check 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
quote do
in_guard do
is_tuple(unquote(thing)) and :erlang.element(1, unquote(thing)) == unquote(kind)
else
result = unquote(thing)
is_tuple(result) and :erlang.element(1, result) == unquote(kind)
end
end
end
@doc """
Check if the given argument is a regex.
"""
defmacro is_regex(thing) do
quote do
is_record(unquote(thing), Regex)
end
end
@doc """
Defines the current module as a protocol and specifies the API
that should be implemented.
## 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:
# Numbers are never blank
defimpl Blank, for: Number 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 types available are:
* Record
* Tuple
* Atom
* List
* BitString
* Number
* Function
* PID
* Port
* Reference
* Any
## Selecting implementations
Implementing the protocol for all default types can be cumbersome.
Even more, if you consider that Number, Function, PID, Port and
Reference are never going to be blank, it would be easier if we
could simply provide a default implementation.
This can be achieved with Elixir as follows:
defprotocol Blank do
@only [Atom, Tuple, List, BitString, Any]
def blank?(data)
end
If the protocol is invoked with a data type that is not an Atom,
nor Tuple, nor List, nor BitString, Elixir will now dispatch to
Any. That said, the default behavior could be implemented as:
defimpl Blank, for: Any do
def blank?(_), do: false
end
Now, all data types that we have not specified will be
automatically considered non blank.
## Protocols + Records
The real benefit of protocols comes when mixed with records. For instance,
imagine we have a module called `RedBlack` that provides an API to create
and manipulate Red-Black trees. This module represents such trees via a
record named `RedBlack.Tree` and we want this tree to be considered blank
in case it has no items. To achieve this, the developer just needs to
implement the protocol for `RedBlack.Tree`:
defimpl Blank, for: RedBlack.Tree do
def blank?(tree), do: RedBlack.empty?(tree)
end
In the example above, we have implemented `blank?` for `RedBlack.Tree`
that simply delegates to `RedBlack.empty?` passing the tree as argument.
This implementation doesn't need to be defined inside the `RedBlack`
tree or inside the record, but anywhere in the code.
Finally, since records are simply tuples, one can add a default protocol
implementation to any record by defining a default implementation for tuples.
"""
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.
"""
defmacro defimpl(name, opts, do_block // []) do
Protocol.defimpl(name, Keyword.merge(opts, do_block))
end
@doc """
Defines the given functions in the current module that will
delegate to the given `target`. Functions defined with defdelegate
are public and should be purposedly 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.
## Examples
defmodule MyList do
defdelegate [reverse: 1], to: Erlang.lists
end
MyList.reverse([1,2,3])
#=> [3,2,1]
"""
defmacro defdelegate(tuples, to: target) do
lc { name, arity } in tuples do
args = lc i in :lists.seq(1, arity) do
{ binary_to_atom(<<?x, i + 64>>, :utf8), 0, :quoted }
end
quote do
def unquote(name).(unquote_splicing(args)) do
apply unquote(target), unquote(name), [unquote_splicing(args)]
end
end
end
end
@doc """
Makes the given functions in the current module overridable.
An overridable function is lazily defined, allowing a
developer to customize it.
"""
defmacro defoverridable(tuples) do
quote do
Module.make_overridable(__MODULE__, unquote(tuples))
end
end
@doc """
`use` is a simple mechanism for extending the current module with the
given module.
## Examples
defmodule AssertionTest do
use ExUnit.Case
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. In other words,
`use` is simply a translation to:
defmodule AssertionTest do
require ExUnit.Case
ExUnit.Case.__using__(AssertionTest)
def test_always_pass do
true = true
end
end
"""
defmacro use(module, args)
@doc """
Inspect the given arguments according to the Binary.Inspect protocol.
## Examples
inspect(:foo)
#=> ":foo"
"""
defmacro inspect(arg) do
quote do: __MAIN__.Binary.Inspect.inspect(unquote(arg))
end
@doc """
Access the given element according the qualifier according
to the `Access` protocol. Many types implement the access
protocol, so check the protocol documentation for more
information.
It is important to notice the access protocol is also
allowed in function signatures when applying to references.
This is useful when working with records to allow to match
against an specific part of a record:
def uri_parse(Uri.Config[schema: :http])
In the example above, the schema clause will only match if
the config schema is `:http`. Using the access protocol with
a reference that does not point to a record module will
generate a compilation exception.
## Examples
a = { :a, :b, :c }
a[1] #=> :a
access a, 1 #=> :a
"""
defmacro access(element, qualifier)
@doc """
Convert the argument to a string according to the Binary.Chars protocol.
This is the function invoked when there is string interpolation.
## Examples
to_binary(:foo)
#=> "foo"
"""
defmacro to_binary(arg) do
quote do: __MAIN__.Binary.Chars.to_binary(unquote(arg))
end
@doc """
Convert the argument to a list according to the List.Chars protocol.
## Examples
to_char_list(:foo)
#=> 'foo'
"""
defmacro to_char_list(arg) do
quote do: __MAIN__.List.Chars.to_char_list(unquote(arg))
end
@doc """
Define elem to get Tuple element according to Elixir conventions.
We need to implement it as a macro to it can be used in guards.
## Example
tuple = { :foo, :bar, 3 }
elem(tuple, 1) #=> :foo
"""
defmacro elem(tuple, index) do
quote do: :erlang.element(unquote(index), unquote(tuple))
end
@doc """
Define setelem to set Tuple element according to Elixir conventions.
We need to implement it as a macro to it can be used in guards.
## Example
tuple = { :foo, :bar, 3 }
setelem(tuple, 1, :baz) #=> { :baz, :bar, 3 }
"""
defmacro setelem(tuple, index, value) do
quote do: :erlang.setelement(unquote(index), unquote(tuple), unquote(value))
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
5 div 2 #=> 2
"""
defmacro div(left, right) do
quote do: __op__ :div, unquote(left), unquote(right)
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
5 rem 2 #=> 1
"""
defmacro rem(left, right) do
quote do: __op__ :rem, unquote(left), unquote(right)
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
match?(1, 1) #=> true
match?(1, 2) #=> false
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, _}, _)
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)
"""
defmacro match?({ :_, _, atom }, _right) when is_atom(atom) do
# Special case underscore since it always matches.
true
end
defmacro match?(left, right) do
quote do
case unquote(right) do
unquote(left) ->
true
_ ->
false
end
end
end
@doc """
Matches the given condition against the match clauses.
## Examples
case thing do
{ :selector, i, value } when is_integer(i) ->
value
value -> value
end
In the example above, we compare `thing` with each given
match clause and execute the first one that matches. If no
clause matches, an error is raised.
Since Elixir variables can be assigned more than once, variables
in a match clause will always be assigned instead of matching with
its previous values. For example:
i = 1
case 10 do
i -> i * 2
end
The example above will return 20, because `i` is assgined to 10
and then multiplied by 2. If you desire to match the value of `i`
against the given condition, you need to use the `^` operator:
i = 1
case 10 do
^i -> i * 2
end
The example above will actually fail because 10 does not match 1.
Finally, `case` accepts an `else:` branch as a fallback if none
of the clauses match:
case thing do
{ :selector, i, value } when is_integer(i) ->
value
_ ->
thing
end
"""
defmacro case(condition, blocks)
@doc """
Execute the given expressions and catch any error, exit
or throw that may have happened.
## Examples
try do
do_something_that_may_fail(some_arg)
rescue
ArgumentError ->
IO.puts "Invalid argument given"
catch
value ->
IO.puts "caught \#{value}"
after
IO.puts "This is printed regardless if it failed or succeed"
end
The rescue clause is used to handle errors, while the catch clause
can be used to catch throw values. Both catch and rescue clauses
accepts the same pattern matching rules as match.
Note that calls inside `try` are not tail recursive since the VM
needs to keep the stacktrace in case an exception happens.
## Rescue clauses
Besides accepting the same pattern matching rules as `match`
clauses, rescue provides some conveniences around exceptions
that allows one to rescue an exception by its name and not by
its internal contents. All the following formats are valid
rescue expressions:
try do
UndefinedModule.undefined_function
rescue
UndefinedFunctionError -> nil
end
try do
UndefinedModule.undefined_function
rescue
[UndefinedFunctionError] -> nil
end
# rescue and assign to x
try do
UndefinedModule.undefined_function
rescue
x in [UndefinedFunctionError] -> nil
end
# rescue all and assign to x
try do
UndefinedModule.undefined_function
rescue
x -> nil
end
## Variable visibility
Since an expression inside `try` may not have been evaluted
due to an exception, any variable created inside `try` cannot
be accessed externaly.
For instance:
try do
x = 1
do_something_that_may_fail(same_arg)
:ok
catch
_ | _ -> :failed
end
x #=> Cannot access `x`
In the example above, `x` cannot be accessed since it was defined
inside the `try` clause.
## Catching exits and Erlang errors
The catch clause works exactly the same as in Erlang. Therefore,
one can also handle exits/errors coming from Erlang as below:
try do
exit(1)
catch
:exit | 1 -> IO.puts "Exited with 1"
end
try do
error(:sample)
catch
:error | :sample ->
IO.puts "sample error"
end
Although the second form should be avoided in favor of raise/rescue
control mechanisms.
"""
defmacro try(args)
@doc """
The current process will hang until it receives a message
from other processes that matches the given clauses.
## Examples
receive do
{ :selector, i, value } when is_integer(i) ->
value
value when is_atom(value) ->
value
_ ->
IO.puts :standard_error, "Unexpected message received"
end
The match clauses above follows the same rules as `case/2`.
An optional after clause can be given in case the message was not
received after the specified period of time:
receive do
{ :selector, i, value } when is_integer(i) ->
value
value when is_atom(value) ->
value
_ ->
IO.puts :standard_error, "Unexpected message received"
after
5000 ->
IO.puts :standard_error, "No message in 5 seconds"
end
The `after` clause can be specified even if there are no match clauses.
There are two special cases for the timout value given to after
* `:infinity` - The process should wait indefinitely for a matching
message, this is the same as not using a timeout.
* 0 - if there is no matching message in the mailbox, the timeout
will occur immediately.
"""
defmacro receive(args)
@doc """
Invokes the given `fun` with the array of arguments `args`.
## Examples
apply fn x -> x * 2 end, [2]
#=> 4
"""
def apply(fun, args)
@doc """
Invokes the given `fun` from `module` with the array of arguments `args`.
## Examples
apply List, reverse, [[1,2,3]]
#=> [3,2,1]
"""
def apply(module, fun, args)
@doc """
Provides an `if` macro. This macro expects the first argument to
be a condition and the rest are keywords arguments.
## One-liner examples
if(foo, do: bar)
In the example above, bar will be returned if foo evalutes to
true (i.e. it is not false nor nil). Otherwise, nil will be returned.
An else option can be given to specify the opposite:
if(foo, do: bar, else: bar)
## Keyword blocks examples
When several expressions must be passed to if, the most appropriate
form is thorugh keywords blocks. The first example above would then
be translated to:
if foo do
bar
end
Notice that do/end becomes delimiters. The value given between
do/end becomes the expression given to as `do:`. The second example
would then translate do:
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, [{:do,do_clause}|tail]) do
else_clause = Keyword.get(tail, :else, nil)
quote do
case !unquote(condition) do
false -> unquote(do_clause)
true -> unquote(else_clause)
end
end
end
@doc """
Execute the first clause where the condition returns true,
raises an error otherwise.
## Examples
cond do
1 + 1 == 2 ->
"This will never match"
2 * 2 != 4 ->
"Nor this"
true ->
"This will"
end
"""
defmacro cond([do: { :"->", _, pairs }]) do
[{ condition, clause }|t] = List.reverse pairs
new_acc = quote do
case !unquote(condition) do
false -> unquote(clause)
end
end
build_cond_clauses(t, new_acc)
end
@doc """
Provides a unless macro that executes the expression
unless a value evalutes to true. Check `if` for examples
and documentation.
"""
defmacro unless(clause, options) do
quote do: if(!unquote(clause), unquote(options))
end
@doc """
Allows you to destructure two lists, assigning each
term in the right to the left. Differently from pattern
matching via `=`, if the sizes of the left and right
lists don't match,, structuring simply stops instead
of raising an error.
## Examples
destructure [x,y,z], [1,2,3,4,5]
x #=> 1
y #=> 2
z #=> 3
Notice in the example above, even though the right
size has more entries than the left, structuring works
fine. If the right size is smaller, the remaining items
are simply assigned to nil:
destructure [x,y,z], [1]
x #=> 1
y #=> nil
z #=> 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
List.foldl left, right, fn item, acc ->
quote do
case unquote(acc) do
[unquote(item)|t] ->
t
other when other == [] or other == nil ->
unquote(item) = nil
end
end
end
end
@doc """
Returns the atom whose text representation is
`some_binary` in UTF8 encoding.
Allowed in guard clauses.
## Examples
binary_to_atom "my_atom" #=> :my_atom
"""
defmacro binary_to_atom(some_binary) do
quote do
binary_to_atom(unquote(some_binary), :utf8)
end
end
@doc """
Works like `binary_to_atom` but the atom must exist.
Allowed in guard clauses.
## Examples
:my_atom #=> :my_atom
binary_to_existing_atom "my_atom" #=> :my_atom
"""
defmacro binary_to_existing_atom(some_binary) do
quote do
binary_to_existing_atom(unquote(some_binary), :utf8)
end
end
@doc """
Returns a binary which corresponds to the text representation
of `some_atom` in UTF8 encoding. Allowed in guard clauses.
## Examples
atom_to_binary :my_atom #=> "my_atom"
"""
defmacro atom_to_binary(some_atom) do
quote do
atom_to_binary(unquote(some_atom), :utf8)
end
end
@doc """
Concatenates two binaries. Allowed in guard clauses.
## Examples
"foo" <> "bar" #=> "foobar"
The `<>` operator can also be used in guard clauses as
long as the first part is a literal binary:
"foo" <> x = "foobar"
x #=> "bar"
"""
defmacro :<>.(left, right) do
concats = extract_concatenations({ :<>, 0, [left, right] })
quote do: << unquote_splicing(concats) >>
end
@doc """
Provides a short-circuit operator that executes the second
expression only if the first one evalutes to true (i.e. it is
not nil nor false). Returns the first expression otherwise.
## Examples
true && true #=> true
nil && true #=> nil
true && 1 #=> 1
false && error(:bad) #=> false
Notice that, differently from Erlang `and` operator,
this operator accepts any expression as arguments,
not only booleans, however it is not allowed in guards.
"""
defmacro :&&.(left, right) do
quote do
case unquote(left) do
andand in [false, nil] ->
andand
_ ->
unquote(right)
end
end
end
@doc """
Provides a short-circuit operator that executes the second
expression only if the first one does not evalute to true (i.e. it
is not nil nor false). Returns the first expression otherwise.
## Examples
false || false #=> false
nil || true #=> true
false || 1 #=> 1
true || error(:bad) #=> true
Notice that, differently from Erlang `or` operator,
this operator accepts any expression as arguments,
not only booleans, however it is not allowed in guards.
"""
defmacro :||.(left, right) do
quote do
case unquote(left) do
oror in [false, nil] ->
unquote(right)
oror ->
oror
end
end
end
@doc """
Returns true if the element on the left is equal (==) to
any of the items in the right. For now, it only accepts
a list as the right argument. Useful in guard clauses.
## Examples
x = 1
x in [1,2,3] #=> true
This macro simply translates the expression above to:
x == 1 or x == 2 or x == 3
"""
defmacro :in.(left, [h|t]) do
:lists.foldl fn x, acc ->
{ :or, 0, [acc, { :==, 0, [left, x] }] }
end, { :==, 0, [left, h] }, t
end
@doc """
Implements the unary operator ! as a macro. It receives any
argument and returns true if it is false or nil. Returns false
otherwise. Not allowed in guard clauses.
## Examples
!1 #=> false
![1,2,3] #=> false
!false #=> true
!nil #=> true
"""
# Optimizes !! to avoid generating case twice.
defmacro :!.({:!, _, [expr]}) do
quote do
case unquote(expr) do
false -> false
nil -> false
_ -> true
end
end
end
defmacro :!.(expr) do
quote do
case unquote(expr) do
false -> true
nil -> true
_ -> false
end
end
end
@doc """
Raises an error.
If the argument is a binary, it raises RuntimeError with the message.
If anything else, becomes a call to raise(argument, []).
## Examples
raise "Given values do not match"
try do
1 + :foo
rescue
x in [BadargError] ->
IO.puts "that was expected"
raise x
end
"""
def raise(msg) when is_binary(msg) do
:erlang.error RuntimeError.new(message: msg)
end
def raise(exception) do
raise(exception, [])
end
@doc """
Raises an error.
It calls `.exception` on the given argument passing
the args in order to retrieve the appropriate exception
structure.
Any module defined via `defexception` automatically
defines `exception(args)` that returns a new instance
of the record and a `exception(args, current)` that
works as no-op.
## Examples
raise ArgumentError, message: "Sample"
"""
def raise(exception, args) do
:erlang.error exception.exception(args)
end
@doc """
Handles the sigil %B. It simples returns a binary
without escaping characters and without interpolations.
## Examples
%B(foo) #=> "foo"
%B(f\#{o}o) #=> "f\\\#{o}o"
"""
defmacro __B__(string, []) do
string
end
@doc """
Handles the sigil %b. It returns a binary as if it was double quoted
string, unescaping characters and replacing interpolations.
## Examples
%b(foo) #=> "foo"
%b(f\#{:o}o) #=> "foo"
"""
defmacro __b__({ :<<>>, line, pieces }, []) do
{ :<<>>, line, Binary.unescape_tokens(pieces) }
end
@doc """
Handles the sigil %C. It simples returns a char list
without escaping characters and without interpolations.
## Examples
%C(foo) #=> 'foo'
%C(f\#{o}o) #=> 'f\\\#{o}o'
"""
defmacro __C__({ :<<>>, _line, [string] }, []) do
binary_to_list(string)
end
@doc """
Handles the sigil %c. It returns a char list as if it was a single
quoted string, unescaping characters and replacing interpolations.
## Examples
%c(foo) #=> 'foo'
%c(f\#{:o}o) #=> 'foo'
"""
# We can skip the runtime conversion if we are
# creating a binary made solely of series of chars.
defmacro __c__({ :<<>>, _line, [string] }, []) when is_list(string) do
Binary.unescape(string)
end
defmacro __c__({ :<<>>, line, pieces }, []) do
binary = { :<<>>, line, Binary.unescape_tokens(pieces) }
quote do: binary_to_list(unquote(binary))
end
@doc """
handles the sigil %r. It returns a Regex pattern.
## Examples
Regex.match? %r(foo), "foo" #=> true
"""
defmacro __r__({ :<<>>, line, pieces }, options) do
binary = { :<<>>, line, Binary.unescape_tokens(pieces, Regex.unescape_map(&1)) }
quote do: Regex.compile(unquote(binary), unquote(options))
end
## Private functions
# 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
# If it is a binary, we don't need to add the binary
# tag. This allows us to use <> function signatures.
defp wrap_concatenation(binary) when is_binary(binary) do
binary
end
defp wrap_concatenation(other) do
{ :|, 0, [other, :binary] }
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([{ condition, clause }|t], acc) do
new_acc = quote do
case !unquote(condition) do
false -> unquote(clause)
true -> unquote(acc)
end
end
build_cond_clauses(t, new_acc)
end
defp build_cond_clauses([], acc), do: acc
end