Permalink
Cannot retrieve contributors at this time
# 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` is Elixir's default environment. | |
It mainly consists of: | |
* basic language primitives, such as arithmetic operators, spawning of processes, | |
data type handling, and others | |
* macros for control-flow and defining new functionality (modules, functions, and the like) | |
* guard checks for augmenting pattern matching | |
You can invoke `Kernel` functions and macros anywhere in Elixir code | |
without the use of the `Kernel.` prefix since they have all been | |
automatically imported. For example, in IEx, you can call: | |
iex> is_number(13) | |
true | |
If you don't want to import a function or macro from `Kernel`, use the `:except` | |
option and then list the function/macro by arity: | |
import Kernel, except: [if: 2, unless: 2] | |
See `Kernel.SpecialForms.import/2` for more information on importing. | |
Elixir also has special forms that are always imported and | |
cannot be skipped. These are described in `Kernel.SpecialForms`. | |
## The standard library | |
`Kernel` provides the basic capabilities the Elixir standard library | |
is built on top of. It is recommended to explore the standard library | |
for advanced functionality. Here are the main groups of modules in the | |
standard library (this list is not a complete reference, see the | |
documentation sidebar for all entries). | |
### Built-in types | |
The following modules handle Elixir built-in data types: | |
* `Atom` - literal constants with a name (`true`, `false`, and `nil` are atoms) | |
* `Float` - numbers with floating point precision | |
* `Function` - a reference to code chunk, created with the `fn/1` special form | |
* `Integer` - whole numbers (not fractions) | |
* `List` - collections of a variable number of elements (linked lists) | |
* `Map` - collections of key-value pairs | |
* `Process` - light-weight threads of execution | |
* `Port` - mechanisms to interact with the external world | |
* `Tuple` - collections of a fixed number of elements | |
There are two data types without an accompanying module: | |
* Bitstring - a sequence of bits, created with `Kernel.SpecialForms.<<>>/1`. | |
When the number of bits is divisible by 8, they are called binaries and can | |
be manipulated with Erlang's `:binary` module | |
* Reference - a unique value in the runtime system, created with `make_ref/0` | |
### Data types | |
Elixir also provides other data types that are built on top of the types | |
listed above. Some of them are: | |
* `Date` - `year-month-day` structs in a given calendar | |
* `DateTime` - date and time with time zone in a given calendar | |
* `Exception` - data raised from errors and unexpected scenarios | |
* `MapSet` - unordered collections of unique elements | |
* `NaiveDateTime` - date and time without time zone in a given calendar | |
* `Keyword` - lists of two-element tuples, often representing optional values | |
* `Range` - inclusive ranges between two integers | |
* `Regex` - regular expressions | |
* `String` - UTF-8 encoded binaries representing characters | |
* `Time` - `hour:minute:second` structs in a given calendar | |
* `URI` - representation of URIs that identify resources | |
* `Version` - representation of versions and requirements | |
### System modules | |
Modules that interface with the underlying system, such as: | |
* `IO` - handles input and output | |
* `File` - interacts with the underlying file system | |
* `Path` - manipulates file system paths | |
* `System` - reads and writes system information | |
### Protocols | |
Protocols add polymorphic dispatch to Elixir. They are contracts | |
implementable by data types. See `Protocol` for more information on | |
protocols. Elixir provides the following protocols in the standard library: | |
* `Collectable` - collects data into a data type | |
* `Enumerable` - handles collections in Elixir. The `Enum` module | |
provides eager functions for working with collections, the `Stream` | |
module provides lazy functions | |
* `Inspect` - converts data types into their programming language | |
representation | |
* `List.Chars` - converts data types to their outside world | |
representation as charlists (non-programming based) | |
* `String.Chars` - converts data types to their outside world | |
representation as strings (non-programming based) | |
### Process-based and application-centric functionality | |
The following modules build on top of processes to provide concurrency, | |
fault-tolerance, and more. | |
* `Agent` - a process that encapsulates mutable state | |
* `Application` - functions for starting, stopping and configuring | |
applications | |
* `GenServer` - a generic client-server API | |
* `Registry` - a key-value process-based storage | |
* `Supervisor` - a process that is responsible for starting, | |
supervising and shutting down other processes | |
* `Task` - a process that performs computations | |
* `Task.Supervisor` - a supervisor for managing tasks exclusively | |
### Supporting documents | |
Elixir documentation also includes supporting documents under the | |
"Pages" section. Those are: | |
* [Compatibility and deprecations](compatibility-and-deprecations.md) - lists | |
compatibility between every Elixir version and Erlang/OTP, release schema; | |
lists all deprecated functions, when they were deprecated and alternatives | |
* [Library guidelines](library-guidelines.md) - general guidelines, anti-patterns, | |
and rules for those writing libraries | |
* [Naming conventions](naming-conventions.md) - naming conventions for Elixir code | |
* [Operators](operators.md) - lists all Elixir operators and their precedences | |
* [Patterns and guards](patterns-and-guards.md) - an introduction to patterns, | |
guards, and extensions | |
* [Syntax reference](syntax-reference.md) - the language syntax reference | |
* [Typespecs](typespecs.md)- types and function specifications, including list of types | |
* [Unicode syntax](unicode-syntax.md) - outlines Elixir support for Unicode | |
* [Writing documentation](writing-documentation.md) - guidelines for writing | |
documentation in Elixir | |
## Guards | |
This module includes the built-in guards used by Elixir developers. | |
They are a predefined set of functions and macros that augment pattern | |
matching, typically invoked after the `when` operator. For example: | |
def drive(%User{age: age}) when age >= 16 do | |
... | |
end | |
The clause above will only be invoked if the user's age is more than | |
or equal to 16. Guards also support joining multiple conditions with | |
`and` and `or`. The whole guard is true if all guard expressions will | |
evaluate to `true`. A more complete introduction to guards is available | |
in the [Patterns and guards](patterns-and-guards.md) page. | |
## Structural comparison | |
The comparison functions in this module perform structural comparison. | |
This means structures are compared based on their representation and | |
not on their semantic value. This is specially important for functions | |
that are meant to provide ordering, such as `>/2`, `</2`, `>=/2`, | |
`<=/2`, `min/2`, and `max/2`. For example: | |
~D[2017-03-31] > ~D[2017-04-01] | |
will return `true` because structural comparison compares the `:day` | |
field before `:month` or `:year`. Therefore, when comparing structs, | |
you often use the `compare/2` function made available by the structs | |
modules themselves: | |
iex> Date.compare(~D[2017-03-31], ~D[2017-04-01]) | |
:lt | |
Alternatively, you can use the functions in the `Enum` module to | |
sort or compute a maximum/minimum: | |
iex> Enum.sort([~D[2017-03-31], ~D[2017-04-01]], Date) | |
[~D[2017-03-31], ~D[2017-04-01]] | |
iex> Enum.max([~D[2017-03-31], ~D[2017-04-01]], Date) | |
~D[2017-04-01] | |
## Truthy and falsy values | |
Besides the booleans `true` and `false`, Elixir has the | |
concept of a "truthy" or "falsy" value. | |
* a value is truthy when it is neither `false` nor `nil` | |
* a value is falsy when it is either `false` or `nil` | |
Elixir has functions, like `and/2`, that *only* work with | |
booleans, but also functions that work with these | |
truthy/falsy values, like `&&/2` and `!/1`. | |
### Examples | |
We can check the truthiness of a value by using the `!/1` | |
function twice. | |
Truthy values: | |
iex> !!true | |
true | |
iex> !!5 | |
true | |
iex> !![1,2] | |
true | |
iex> !!"foo" | |
true | |
Falsy values (of which there are exactly two): | |
iex> !!false | |
false | |
iex> !!nil | |
false | |
## Inlining | |
Some of the functions described in this module are inlined by | |
the Elixir compiler into their Erlang counterparts in the | |
[`:erlang`](`:erlang`) module. | |
Those functions are called BIFs (built-in internal functions) | |
in Erlang-land and they exhibit interesting properties, as some | |
of them are allowed in guards and others are used for compiler | |
optimizations. | |
Most of the inlined functions can be seen in effect when | |
capturing the function: | |
iex> &Kernel.is_atom/1 | |
&:erlang.is_atom/1 | |
Those functions will be explicitly marked in their docs as | |
"inlined by the compiler". | |
""" | |
# We need this check only for bootstrap purposes. | |
# Once Kernel is loaded and we recompile, it is a no-op. | |
@compile {:inline, bootstrapped?: 1} | |
case :code.ensure_loaded(Kernel) do | |
{:module, _} -> | |
defp bootstrapped?(_), do: true | |
{:error, _} -> | |
defp bootstrapped?(module), do: :code.ensure_loaded(module) == {:module, module} | |
end | |
## Delegations to Erlang with inlining (macros) | |
@doc """ | |
Returns an integer or float which is the arithmetical absolute value of `number`. | |
Allowed in guard tests. Inlined by the compiler. | |
## Examples | |
iex> abs(-3.33) | |
3.33 | |
iex> abs(-3) | |
3 | |
""" | |
@doc guard: true | |
@spec abs(number) :: number | |
def abs(number) do | |
:erlang.abs(number) | |
end | |
@doc """ | |
Invokes the given anonymous function `fun` with the list of | |
arguments `args`. | |
If the number of arguments is known at compile time, prefer | |
`fun.(arg_1, arg_2, ..., arg_n)` as it is clearer than | |
`apply(fun, [arg_1, arg_2, ..., arg_n])`. | |
Inlined by the compiler. | |
## Examples | |
iex> apply(fn x -> x * 2 end, [2]) | |
4 | |
""" | |
@spec apply(fun, [any]) :: any | |
def apply(fun, args) do | |
:erlang.apply(fun, args) | |
end | |
@doc """ | |
Invokes the given function from `module` with the list of | |
arguments `args`. | |
`apply/3` is used to invoke functions where the module, function | |
name or arguments are defined dynamically at runtime. For this | |
reason, you can't invoke macros using `apply/3`, only functions. | |
If the number of arguments and the function name are known at compile time, | |
prefer `module.function(arg_1, arg_2, ..., arg_n)` as it is clearer than | |
`apply(module, :function, [arg_1, arg_2, ..., arg_n])`. | |
Inlined by the compiler. | |
## Examples | |
iex> apply(Enum, :reverse, [[1, 2, 3]]) | |
[3, 2, 1] | |
""" | |
@spec apply(module, function_name :: atom, [any]) :: any | |
def apply(module, function_name, args) do | |
:erlang.apply(module, function_name, args) | |
end | |
@doc """ | |
Extracts the part of the binary starting at `start` with length `length`. | |
Binaries are zero-indexed. | |
If `start` or `length` reference in any way outside the binary, an | |
`ArgumentError` exception is raised. | |
Allowed in guard tests. Inlined by the compiler. | |
## Examples | |
iex> binary_part("foo", 1, 2) | |
"oo" | |
A negative `length` can be used to extract bytes that come *before* the byte | |
at `start`: | |
iex> binary_part("Hello", 5, -3) | |
"llo" | |
An `ArgumentError` is raised when the length is outside of the binary: | |
binary_part("Hello", 0, 10) | |
** (ArgumentError) argument error | |
""" | |
@doc guard: true | |
@spec binary_part(binary, non_neg_integer, integer) :: binary | |
def binary_part(binary, start, length) do | |
:erlang.binary_part(binary, start, length) | |
end | |
@doc """ | |
Returns an integer which is the size in bits of `bitstring`. | |
Allowed in guard tests. Inlined by the compiler. | |
## Examples | |
iex> bit_size(<<433::16, 3::3>>) | |
19 | |
iex> bit_size(<<1, 2, 3>>) | |
24 | |
""" | |
@doc guard: true | |
@spec bit_size(bitstring) :: non_neg_integer | |
def bit_size(bitstring) do | |
:erlang.bit_size(bitstring) | |
end | |
@doc """ | |
Returns 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 (by excess). This operation | |
happens in constant time. | |
Allowed in guard tests. Inlined by the compiler. | |
## Examples | |
iex> byte_size(<<433::16, 3::3>>) | |
3 | |
iex> byte_size(<<1, 2, 3>>) | |
3 | |
""" | |
@doc guard: true | |
@spec byte_size(bitstring) :: non_neg_integer | |
def byte_size(bitstring) do | |
:erlang.byte_size(bitstring) | |
end | |
@doc """ | |
Returns the smallest integer greater than or equal to `number`. | |
If you want to perform ceil operation on other decimal places, | |
use `Float.ceil/2` instead. | |
Allowed in guard tests. Inlined by the compiler. | |
""" | |
@doc since: "1.8.0", guard: true | |
@spec ceil(number) :: integer | |
def ceil(number) do | |
:erlang.ceil(number) | |
end | |
@doc """ | |
Performs an integer division. | |
Raises an `ArithmeticError` exception if one of the arguments is not an | |
integer, or when the `divisor` is `0`. | |
`div/2` performs *truncated* integer division. This means that | |
the result is always rounded towards zero. | |
If you want to perform floored integer division (rounding towards negative infinity), | |
use `Integer.floor_div/2` instead. | |
Allowed in guard tests. Inlined by the compiler. | |
## Examples | |
div(5, 2) | |
#=> 2 | |
div(6, -4) | |
#=> -1 | |
div(-99, 2) | |
#=> -49 | |
div(100, 0) | |
** (ArithmeticError) bad argument in arithmetic expression | |
""" | |
@doc guard: true | |
@spec div(integer, neg_integer | pos_integer) :: integer | |
def div(dividend, divisor) do | |
:erlang.div(dividend, divisor) | |
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. | |
Inlined by the compiler. | |
## Examples | |
When a process reaches its end, by default it exits with | |
reason `:normal`. You can also call `exit/1` explicitly if you | |
want to terminate a process but not signal any failure: | |
exit(:normal) | |
In case something goes wrong, you can also use `exit/1` with | |
a different reason: | |
exit(:seems_bad) | |
If the exit reason is not `:normal`, all the processes linked to the process | |
that exited will crash (unless they are trapping exits). | |
## OTP exits | |
Exits are used by the OTP to determine if a process exited abnormally | |
or not. The following exits are considered "normal": | |
* `exit(:normal)` | |
* `exit(:shutdown)` | |
* `exit({:shutdown, term})` | |
Exiting with any other reason is considered abnormal and treated | |
as a crash. This means the default supervisor behaviour kicks in, | |
error reports are emitted, and so forth. | |
This behaviour is relied on in many different places. For example, | |
`ExUnit` uses `exit(:shutdown)` when exiting the test process to | |
signal linked processes, supervision trees and so on to politely | |
shut down too. | |
## CLI exits | |
Building on top of the exit signals mentioned above, if the | |
process started by the command line exits with any of the three | |
reasons above, its exit is considered normal and the Operating | |
System process will exit with status 0. | |
It is, however, possible to customize the operating system exit | |
signal by invoking: | |
exit({:shutdown, integer}) | |
This will cause the operating system process to exit with the status given by | |
`integer` while signaling all linked Erlang processes to politely | |
shut down. | |
Any other exit reason will cause the operating system process to exit with | |
status `1` and linked Erlang processes to crash. | |
""" | |
@spec exit(term) :: no_return | |
def exit(reason) do | |
:erlang.exit(reason) | |
end | |
@doc """ | |
Returns the largest integer smaller than or equal to `number`. | |
If you want to perform floor operation on other decimal places, | |
use `Float.floor/2` instead. | |
Allowed in guard tests. Inlined by the compiler. | |
""" | |
@doc since: "1.8.0", guard: true | |
@spec floor(number) :: integer | |
def floor(number) do | |
:erlang.floor(number) | |
end | |
@doc """ | |
Returns the head of a list. Raises `ArgumentError` if the list is empty. | |
It works with improper lists. | |
Allowed in guard tests. Inlined by the compiler. | |
## Examples | |
hd([1, 2, 3, 4]) | |
#=> 1 | |
hd([1 | 2]) | |
#=> 1 | |
Giving it an empty list raises: | |
tl([]) | |
#=> ** (ArgumentError) argument error | |
""" | |
@doc guard: true | |
@spec hd(nonempty_maybe_improper_list(elem, any)) :: elem when elem: term | |
def hd(list) do | |
:erlang.hd(list) | |
end | |
@doc """ | |
Returns `true` if `term` is an atom; otherwise returns `false`. | |
Allowed in guard tests. Inlined by the compiler. | |
""" | |
@doc guard: true | |
@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. Inlined by the compiler. | |
## Examples | |
iex> is_binary("foo") | |
true | |
iex> is_binary(<<1::3>>) | |
false | |
""" | |
@doc guard: true | |
@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. Inlined by the compiler. | |
## Examples | |
iex> is_bitstring("foo") | |
true | |
iex> is_bitstring(<<1::3>>) | |
true | |
""" | |
@doc guard: true | |
@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. Inlined by the compiler. | |
""" | |
@doc guard: true | |
@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. Inlined by the compiler. | |
""" | |
@doc guard: true | |
@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. Inlined by the compiler. | |
""" | |
@doc guard: true | |
@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. Inlined by the compiler. | |
## Examples | |
iex> is_function(fn x -> x * 2 end, 1) | |
true | |
iex> is_function(fn x -> x * 2 end, 2) | |
false | |
""" | |
@doc guard: true | |
@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. Inlined by the compiler. | |
""" | |
@doc guard: true | |
@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. Inlined by the compiler. | |
""" | |
@doc guard: true | |
@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. Inlined by the compiler. | |
""" | |
@doc guard: true | |
@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. Inlined by the compiler. | |
""" | |
@doc guard: true | |
@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. Inlined by the compiler. | |
""" | |
@doc guard: true | |
@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. Inlined by the compiler. | |
""" | |
@doc guard: true | |
@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. Inlined by the compiler. | |
""" | |
@doc guard: true | |
@spec is_tuple(term) :: boolean | |
def is_tuple(term) do | |
:erlang.is_tuple(term) | |
end | |
@doc """ | |
Returns `true` if `term` is a map; otherwise returns `false`. | |
Allowed in guard tests. Inlined by the compiler. | |
""" | |
@doc guard: true | |
@spec is_map(term) :: boolean | |
def is_map(term) do | |
:erlang.is_map(term) | |
end | |
@doc """ | |
Returns `true` if `key` is a key in `map`; otherwise returns `false`. | |
It raises `BadMapError` if the first element is not a map. | |
Allowed in guard tests. Inlined by the compiler. | |
""" | |
@doc guard: true, since: "1.10.0" | |
@spec is_map_key(map, term) :: boolean | |
def is_map_key(map, key) do | |
:erlang.is_map_key(key, map) | |
end | |
@doc """ | |
Returns the length of `list`. | |
Allowed in guard tests. Inlined by the compiler. | |
## Examples | |
iex> length([1, 2, 3, 4, 5, 6, 7, 8, 9]) | |
9 | |
""" | |
@doc guard: true | |
@spec length(list) :: non_neg_integer | |
def length(list) do | |
:erlang.length(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. | |
Inlined by the compiler. | |
## Examples | |
make_ref() | |
#=> #Reference<0.0.0.135> | |
""" | |
@spec make_ref() :: reference | |
def make_ref() do | |
:erlang.make_ref() | |
end | |
@doc """ | |
Returns the size of a map. | |
The size of a map is the number of key-value pairs that the map contains. | |
This operation happens in constant time. | |
Allowed in guard tests. Inlined by the compiler. | |
## Examples | |
iex> map_size(%{a: "foo", b: "bar"}) | |
2 | |
""" | |
@doc guard: true | |
@spec map_size(map) :: non_neg_integer | |
def map_size(map) do | |
:erlang.map_size(map) | |
end | |
@doc """ | |
Returns the biggest of the two given terms according to | |
their structural comparison. | |
If the terms compare equal, the first one is returned. | |
This performs a structural comparison where all Elixir | |
terms can be compared with each other. See the ["Structural | |
comparison" section](#module-structural-comparison) section | |
for more information. | |
Inlined by the compiler. | |
## Examples | |
iex> max(1, 2) | |
2 | |
iex> max(:a, :b) | |
:b | |
""" | |
@spec max(first, second) :: first | second when first: term, second: term | |
def max(first, second) do | |
:erlang.max(first, second) | |
end | |
@doc """ | |
Returns the smallest of the two given terms according to | |
their structural comparison. | |
If the terms compare equal, the first one is returned. | |
This performs a structural comparison where all Elixir | |
terms can be compared with each other. See the ["Structural | |
comparison" section](#module-structural-comparison) section | |
for more information. | |
Inlined by the compiler. | |
## Examples | |
iex> min(1, 2) | |
1 | |
iex> min("foo", "bar") | |
"bar" | |
""" | |
@spec min(first, second) :: first | second when first: term, second: 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. Inlined by the compiler. | |
""" | |
@doc guard: true | |
@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. Inlined by the compiler. | |
""" | |
@doc guard: true | |
@spec node(pid | reference | port) :: node | |
def node(arg) do | |
:erlang.node(arg) | |
end | |
@doc """ | |
Computes the remainder of an integer division. | |
`rem/2` uses truncated division, which means that | |
the result will always have the sign of the `dividend`. | |
Raises an `ArithmeticError` exception if one of the arguments is not an | |
integer, or when the `divisor` is `0`. | |
Allowed in guard tests. Inlined by the compiler. | |
## Examples | |
iex> rem(5, 2) | |
1 | |
iex> rem(6, -4) | |
2 | |
""" | |
@doc guard: true | |
@spec rem(integer, neg_integer | pos_integer) :: integer | |
def rem(dividend, divisor) do | |
:erlang.rem(dividend, divisor) | |
end | |
@doc """ | |
Rounds a number to the nearest integer. | |
If the number is equidistant to the two nearest integers, rounds away from zero. | |
Allowed in guard tests. Inlined by the compiler. | |
## Examples | |
iex> round(5.6) | |
6 | |
iex> round(5.2) | |
5 | |
iex> round(-9.9) | |
-10 | |
iex> round(-9) | |
-9 | |
iex> round(2.5) | |
3 | |
iex> round(-2.5) | |
-3 | |
""" | |
@doc guard: true | |
@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 in the form of `{registered_name, node}` for a | |
registered name at another node. | |
Inlined by the compiler. | |
## Examples | |
iex> send(self(), :hello) | |
:hello | |
""" | |
@spec send(dest :: Process.dest(), message) :: message when message: any | |
def send(dest, message) do | |
:erlang.send(dest, message) | |
end | |
@doc """ | |
Returns the PID (process identifier) of the calling process. | |
Allowed in guard clauses. Inlined by the compiler. | |
""" | |
@doc guard: true | |
@spec self() :: pid | |
def self() do | |
:erlang.self() | |
end | |
@doc """ | |
Spawns the given function and returns its PID. | |
Typically developers do not use the `spawn` functions, instead they use | |
abstractions such as `Task`, `GenServer` and `Agent`, built on top of | |
`spawn`, that spawns processes with more conveniences in terms of | |
introspection and debugging. | |
Check the `Process` module for more process-related functions. | |
The anonymous function receives 0 arguments, and may return any value. | |
Inlined by the compiler. | |
## Examples | |
current = self() | |
child = spawn(fn -> send(current, {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 function `fun` from the given `module` passing it the given | |
`args` and returns its PID. | |
Typically developers do not use the `spawn` functions, instead they use | |
abstractions such as `Task`, `GenServer` and `Agent`, built on top of | |
`spawn`, that spawns processes with more conveniences in terms of | |
introspection and debugging. | |
Check the `Process` module for more process-related functions. | |
Inlined by the compiler. | |
## 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. | |
Typically developers do not use the `spawn` functions, instead they use | |
abstractions such as `Task`, `GenServer` and `Agent`, built on top of | |
`spawn`, that spawns processes with more conveniences in terms of | |
introspection and debugging. | |
Check the `Process` module for more process-related functions. For more | |
information on linking, check `Process.link/1`. | |
The anonymous function receives 0 arguments, and may return any value. | |
Inlined by the compiler. | |
## Examples | |
current = self() | |
child = spawn_link(fn -> send(current, {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 function `fun` from the given `module` passing it the given | |
`args`, links it to the current process, and returns its PID. | |
Typically developers do not use the `spawn` functions, instead they use | |
abstractions such as `Task`, `GenServer` and `Agent`, built on top of | |
`spawn`, that spawns processes with more conveniences in terms of | |
introspection and debugging. | |
Check the `Process` module for more process-related functions. For more | |
information on linking, check `Process.link/1`. | |
Inlined by the compiler. | |
## 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 """ | |
Spawns the given function, monitors it and returns its PID | |
and monitoring reference. | |
Typically developers do not use the `spawn` functions, instead they use | |
abstractions such as `Task`, `GenServer` and `Agent`, built on top of | |
`spawn`, that spawns processes with more conveniences in terms of | |
introspection and debugging. | |
Check the `Process` module for more process-related functions. | |
The anonymous function receives 0 arguments, and may return any value. | |
Inlined by the compiler. | |
## Examples | |
current = self() | |
spawn_monitor(fn -> send(current, {self(), 1 + 2}) end) | |
""" | |
@spec spawn_monitor((() -> any)) :: {pid, reference} | |
def spawn_monitor(fun) do | |
:erlang.spawn_monitor(fun) | |
end | |
@doc """ | |
Spawns the given module and function passing the given args, | |
monitors it and returns its PID and monitoring reference. | |
Typically developers do not use the `spawn` functions, instead they use | |
abstractions such as `Task`, `GenServer` and `Agent`, built on top of | |
`spawn`, that spawns processes with more conveniences in terms of | |
introspection and debugging. | |
Check the `Process` module for more process-related functions. | |
Inlined by the compiler. | |
## Examples | |
spawn_monitor(SomeModule, :function, [1, 2, 3]) | |
""" | |
@spec spawn_monitor(module, atom, list) :: {pid, reference} | |
def spawn_monitor(module, fun, args) do | |
:erlang.spawn_monitor(module, fun, args) | |
end | |
@doc """ | |
Pipes `value` to the given `fun` and returns the `value` itself. | |
Useful for running synchronous side effects in a pipeline. | |
## Examples | |
iex> tap(1, fn x -> x + 1 end) | |
1 | |
Most commonly, this is used in pipelines. For example, | |
let's suppose you want to inspect part of a data structure. | |
You could write: | |
%{a: 1} | |
|> Map.update!(:a, & &1 + 2) | |
|> tap(&IO.inspect(&1.a)) | |
|> Map.update!(:a, & &1 * 2) | |
""" | |
@doc since: "1.12.0" | |
defmacro tap(value, fun) do | |
quote bind_quoted: [fun: fun, value: value] do | |
fun.(value) | |
value | |
end | |
end | |
@doc """ | |
A non-local return from a function. | |
Check `Kernel.SpecialForms.try/1` for more information. | |
Inlined by the compiler. | |
""" | |
@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. | |
It works with improper lists. | |
Allowed in guard tests. Inlined by the compiler. | |
## Examples | |
tl([1, 2, 3, :go]) | |
#=> [2, 3, :go] | |
tl([:one]) | |
#=> [] | |
tl([:a, :b | :c]) | |
#=> [:b | :c] | |
tl([:a | %{b: 1}]) | |
#=> %{b: 1} | |
Giving it an empty list raises: | |
tl([]) | |
#=> ** (ArgumentError) argument error | |
""" | |
@doc guard: true | |
@spec tl(nonempty_maybe_improper_list(elem, tail)) :: maybe_improper_list(elem, tail) | tail | |
when elem: term, tail: term | |
def tl(list) do | |
:erlang.tl(list) | |
end | |
@doc """ | |
Returns the integer part of `number`. | |
Allowed in guard tests. Inlined by the compiler. | |
## Examples | |
iex> trunc(5.4) | |
5 | |
iex> trunc(-5.99) | |
-5 | |
iex> trunc(-5) | |
-5 | |
""" | |
@doc guard: true | |
@spec trunc(number) :: integer | |
def trunc(number) do | |
:erlang.trunc(number) | |
end | |
@doc """ | |
Returns the size of a tuple. | |
This operation happens in constant time. | |
Allowed in guard tests. Inlined by the compiler. | |
## Examples | |
iex> tuple_size({:a, :b, :c}) | |
3 | |
""" | |
@doc guard: true | |
@spec tuple_size(tuple) :: non_neg_integer | |
def tuple_size(tuple) do | |
:erlang.tuple_size(tuple) | |
end | |
@doc """ | |
Arithmetic addition operator. | |
Allowed in guard tests. Inlined by the compiler. | |
## Examples | |
iex> 1 + 2 | |
3 | |
""" | |
@doc guard: true | |
@spec integer + integer :: integer | |
@spec float + float :: float | |
@spec integer + float :: float | |
@spec float + integer :: float | |
def left + right do | |
:erlang.+(left, right) | |
end | |
@doc """ | |
Arithmetic subtraction operator. | |
Allowed in guard tests. Inlined by the compiler. | |
## Examples | |
iex> 1 - 2 | |
-1 | |
""" | |
@doc guard: true | |
@spec integer - integer :: integer | |
@spec float - float :: float | |
@spec integer - float :: float | |
@spec float - integer :: float | |
def left - right do | |
:erlang.-(left, right) | |
end | |
@doc """ | |
Arithmetic positive unary operator. | |
Allowed in guard tests. Inlined by the compiler. | |
## Examples | |
iex> +1 | |
1 | |
""" | |
@doc guard: true | |
@spec +integer :: integer | |
@spec +float :: float | |
def +value do | |
:erlang.+(value) | |
end | |
@doc """ | |
Arithmetic negative unary operator. | |
Allowed in guard tests. Inlined by the compiler. | |
## Examples | |
iex> -2 | |
-2 | |
""" | |
@doc guard: true | |
@spec -0 :: 0 | |
@spec -pos_integer :: neg_integer | |
@spec -neg_integer :: pos_integer | |
@spec -float :: float | |
def -value do | |
:erlang.-(value) | |
end | |
@doc """ | |
Arithmetic multiplication operator. | |
Allowed in guard tests. Inlined by the compiler. | |
## Examples | |
iex> 1 * 2 | |
2 | |
""" | |
@doc guard: true | |
@spec integer * integer :: integer | |
@spec float * float :: float | |
@spec integer * float :: float | |
@spec float * integer :: float | |
def left * right do | |
:erlang.*(left, right) | |
end | |
@doc """ | |
Arithmetic division operator. | |
The result is always a float. Use `div/2` and `rem/2` if you want | |
an integer division or the remainder. | |
Raises `ArithmeticError` if `right` is 0 or 0.0. | |
Allowed in guard tests. Inlined by the compiler. | |
## Examples | |
1 / 2 | |
#=> 0.5 | |
-3.0 / 2.0 | |
#=> -1.5 | |
5 / 1 | |
#=> 5.0 | |
7 / 0 | |
** (ArithmeticError) bad argument in arithmetic expression | |
""" | |
@doc guard: true | |
@spec number / number :: float | |
def left / right do | |
:erlang./(left, right) | |
end | |
@doc """ | |
List concatenation operator. Concatenates a proper list and a term, returning a list. | |
The complexity of `a ++ b` is proportional to `length(a)`, so avoid repeatedly | |
appending to lists of arbitrary length, for example, `list ++ [element]`. | |
Instead, consider prepending via `[element | rest]` and then reversing. | |
If the `right` operand is not a proper list, it returns an improper list. | |
If the `left` operand is not a proper list, it raises `ArgumentError`. | |
Inlined by the compiler. | |
## Examples | |
iex> [1] ++ [2, 3] | |
[1, 2, 3] | |
iex> 'foo' ++ 'bar' | |
'foobar' | |
# returns an improper list | |
iex> [1] ++ 2 | |
[1 | 2] | |
# returns a proper list | |
iex> [1] ++ [2] | |
[1, 2] | |
# improper list on the right will return an improper list | |
iex> [1] ++ [2 | 3] | |
[1, 2 | 3] | |
""" | |
@spec list ++ term :: maybe_improper_list | |
def left ++ right do | |
:erlang.++(left, right) | |
end | |
@doc """ | |
List subtraction operator. Removes the first occurrence of an element | |
on the left list for each element on the right. | |
This function is optimized so the complexity of `a -- b` is proportional | |
to `length(a) * log(length(b))`. See also the [Erlang efficiency | |
guide](https://erlang.org/doc/efficiency_guide/retired_myths.html). | |
Inlined by the compiler. | |
## Examples | |
iex> [1, 2, 3] -- [1, 2] | |
[3] | |
iex> [1, 2, 3, 2, 1] -- [1, 2, 2] | |
[3, 1] | |
The `--/2` operator is right associative, meaning: | |
iex> [1, 2, 3] -- [2] -- [3] | |
[1, 3] | |
As it is equivalent to: | |
iex> [1, 2, 3] -- ([2] -- [3]) | |
[1, 3] | |
""" | |
@spec list -- list :: list | |
def left -- right do | |
:erlang.--(left, right) | |
end | |
@doc """ | |
Strictly boolean "not" operator. | |
`value` must be a boolean; if it's not, an `ArgumentError` exception is raised. | |
Allowed in guard tests. Inlined by the compiler. | |
## Examples | |
iex> not false | |
true | |
""" | |
@doc guard: true | |
@spec not true :: false | |
@spec not false :: true | |
def not value do | |
:erlang.not(value) | |
end | |
@doc """ | |
Less-than operator. | |
Returns `true` if `left` is less than `right`. | |
This performs a structural comparison where all Elixir | |
terms can be compared with each other. See the ["Structural | |
comparison" section](#module-structural-comparison) section | |
for more information. | |
Allowed in guard tests. Inlined by the compiler. | |
## Examples | |
iex> 1 < 2 | |
true | |
""" | |
@doc guard: true | |
@spec term < term :: boolean | |
def left < right do | |
:erlang.<(left, right) | |
end | |
@doc """ | |
Greater-than operator. | |
Returns `true` if `left` is more than `right`. | |
This performs a structural comparison where all Elixir | |
terms can be compared with each other. See the ["Structural | |
comparison" section](#module-structural-comparison) section | |
for more information. | |
Allowed in guard tests. Inlined by the compiler. | |
## Examples | |
iex> 1 > 2 | |
false | |
""" | |
@doc guard: true | |
@spec term > term :: boolean | |
def left > right do | |
:erlang.>(left, right) | |
end | |
@doc """ | |
Less-than or equal to operator. | |
Returns `true` if `left` is less than or equal to `right`. | |
This performs a structural comparison where all Elixir | |
terms can be compared with each other. See the ["Structural | |
comparison" section](#module-structural-comparison) section | |
for more information. | |
Allowed in guard tests. Inlined by the compiler. | |
## Examples | |
iex> 1 <= 2 | |
true | |
""" | |
@doc guard: true | |
@spec term <= term :: boolean | |
def left <= right do | |
:erlang."=<"(left, right) | |
end | |
@doc """ | |
Greater-than or equal to operator. | |
Returns `true` if `left` is more than or equal to `right`. | |
This performs a structural comparison where all Elixir | |
terms can be compared with each other. See the ["Structural | |
comparison" section](#module-structural-comparison) section | |
for more information. | |
Allowed in guard tests. Inlined by the compiler. | |
## Examples | |
iex> 1 >= 2 | |
false | |
""" | |
@doc guard: true | |
@spec term >= term :: boolean | |
def left >= right do | |
:erlang.>=(left, right) | |
end | |
@doc """ | |
Equal to operator. Returns `true` if the two terms are equal. | |
This operator considers 1 and 1.0 to be equal. For stricter | |
semantics, use `===/2` instead. | |
All terms in Elixir can be compared with each other. | |
Allowed in guard tests. Inlined by the compiler. | |
## Examples | |
iex> 1 == 2 | |
false | |
iex> 1 == 1.0 | |
true | |
""" | |
@doc guard: true | |
@spec term == term :: boolean | |
def left == right do | |
:erlang.==(left, right) | |
end | |
@doc """ | |
Not equal to operator. | |
Returns `true` if the two terms are not equal. | |
This operator considers 1 and 1.0 to be equal. For match | |
comparison, use `!==/2` instead. | |
All terms in Elixir can be compared with each other. | |
Allowed in guard tests. Inlined by the compiler. | |
## Examples | |
iex> 1 != 2 | |
true | |
iex> 1 != 1.0 | |
false | |
""" | |
@doc guard: true | |
@spec term != term :: boolean | |
def left != right do | |
:erlang."/="(left, right) | |
end | |
@doc """ | |
Strictly equal to operator. | |
Returns `true` if the two terms are exactly equal. | |
The terms are only considered to be exactly equal if they | |
have the same value and are of the same type. For example, | |
`1 == 1.0` returns `true`, but since they are of different | |
types, `1 === 1.0` returns `false`. | |
All terms in Elixir can be compared with each other. | |
Allowed in guard tests. Inlined by the compiler. | |
## Examples | |
iex> 1 === 2 | |
false | |
iex> 1 === 1.0 | |
false | |
""" | |
@doc guard: true | |
@spec term === term :: boolean | |
def left === right do | |
:erlang."=:="(left, right) | |
end | |
@doc """ | |
Strictly not equal to operator. | |
Returns `true` if the two terms are not exactly equal. | |
See `===/2` for a definition of what is considered "exactly equal". | |
All terms in Elixir can be compared with each other. | |
Allowed in guard tests. Inlined by the compiler. | |
## Examples | |
iex> 1 !== 2 | |
true | |
iex> 1 !== 1.0 | |
true | |
""" | |
@doc guard: true | |
@spec term !== term :: boolean | |
def left !== right do | |
:erlang."=/="(left, right) | |
end | |
@doc """ | |
Gets the element at the zero-based `index` in `tuple`. | |
It raises `ArgumentError` when index is negative or it is out of range of the tuple elements. | |
Allowed in guard tests. Inlined by the compiler. | |
## Examples | |
tuple = {:foo, :bar, 3} | |
elem(tuple, 1) | |
#=> :bar | |
elem({}, 0) | |
** (ArgumentError) argument error | |
elem({:foo, :bar}, 2) | |
** (ArgumentError) argument error | |
""" | |
@doc guard: true | |
@spec elem(tuple, non_neg_integer) :: term | |
def elem(tuple, index) do | |
:erlang.element(index + 1, tuple) | |
end | |
@doc """ | |
Puts `value` at the given zero-based `index` in `tuple`. | |
Inlined by the compiler. | |
## Examples | |
iex> tuple = {:foo, :bar, 3} | |
iex> put_elem(tuple, 0, :baz) | |
{:baz, :bar, 3} | |
""" | |
@spec put_elem(tuple, non_neg_integer, term) :: tuple | |
def put_elem(tuple, index, value) do | |
:erlang.setelement(index + 1, tuple, value) | |
end | |
## Implemented in Elixir | |
defp optimize_boolean({:case, meta, args}) do | |
{:case, [{:optimize_boolean, true} | meta], args} | |
end | |
@doc """ | |
Strictly boolean "or" operator. | |
If `left` is `true`, returns `true`; otherwise returns `right`. | |
Requires only the `left` operand to be a boolean since it short-circuits. | |
If the `left` operand is not a boolean, a `BadBooleanError` exception is | |
raised. | |
Allowed in guard tests. | |
## Examples | |
iex> true or false | |
true | |
iex> false or 42 | |
42 | |
iex> 42 or false | |
** (BadBooleanError) expected a boolean on left-side of "or", got: 42 | |
""" | |
@doc guard: true | |
defmacro left or right do | |
case __CALLER__.context do | |
nil -> build_boolean_check(:or, left, true, right) | |
:match -> invalid_match!(:or) | |
:guard -> quote(do: :erlang.orelse(unquote(left), unquote(right))) | |
end | |
end | |
@doc """ | |
Strictly boolean "and" operator. | |
If `left` is `false`, returns `false`; otherwise returns `right`. | |
Requires only the `left` operand to be a boolean since it short-circuits. If | |
the `left` operand is not a boolean, a `BadBooleanError` exception is raised. | |
Allowed in guard tests. | |
## Examples | |
iex> true and false | |
false | |
iex> true and "yay!" | |
"yay!" | |
iex> "yay!" and true | |
** (BadBooleanError) expected a boolean on left-side of "and", got: "yay!" | |
""" | |
@doc guard: true | |
defmacro left and right do | |
case __CALLER__.context do | |
nil -> build_boolean_check(:and, left, right, false) | |
:match -> invalid_match!(:and) | |
:guard -> quote(do: :erlang.andalso(unquote(left), unquote(right))) | |
end | |
end | |
defp build_boolean_check(operator, check, true_clause, false_clause) do | |
optimize_boolean( | |
quote do | |
case unquote(check) do | |
false -> unquote(false_clause) | |
true -> unquote(true_clause) | |
other -> :erlang.error({:badbool, unquote(operator), other}) | |
end | |
end | |
) | |
end | |
@doc """ | |
Boolean "not" operator. | |
Receives any value (not just booleans) and returns `true` if `value` | |
is `false` or `nil`; returns `false` otherwise. | |
Not allowed in guard clauses. | |
## Examples | |
iex> !Enum.empty?([]) | |
false | |
iex> !List.first([]) | |
true | |
""" | |
defmacro !value | |
defmacro !{:!, _, [value]} do | |
assert_no_match_or_guard_scope(__CALLER__.context, "!") | |
optimize_boolean( | |
quote do | |
case unquote(value) do | |
x when :"Elixir.Kernel".in(x, [false, nil]) -> false | |
_ -> true | |
end | |
end | |
) | |
end | |
defmacro !value do | |
assert_no_match_or_guard_scope(__CALLER__.context, "!") | |
optimize_boolean( | |
quote do | |
case unquote(value) do | |
x when :"Elixir.Kernel".in(x, [false, nil]) -> true | |
_ -> false | |
end | |
end | |
) | |
end | |
@doc """ | |
Binary concatenation operator. Concatenates two binaries. | |
## Examples | |
iex> "foo" <> "bar" | |
"foobar" | |
The `<>/2` operator can also be used in pattern matching (and guard clauses) as | |
long as the left argument is a literal binary: | |
iex> "foo" <> x = "foobar" | |
iex> x | |
"bar" | |
`x <> "bar" = "foobar"` would have resulted in a `CompileError` exception. | |
""" | |
defmacro left <> right do | |
concats = extract_concatenations({:<>, [], [left, right]}, __CALLER__) | |
quote(do: <<unquote_splicing(concats)>>) | |
end | |
# Extracts concatenations in order to optimize many | |
# concatenations into one single clause. | |
defp extract_concatenations({:<>, _, [left, right]}, caller) do | |
[wrap_concatenation(left, :left, caller) | extract_concatenations(right, caller)] | |
end | |
defp extract_concatenations(other, caller) do | |
[wrap_concatenation(other, :right, caller)] | |
end | |
defp wrap_concatenation(binary, _side, _caller) when is_binary(binary) do | |
binary | |
end | |
defp wrap_concatenation(literal, _side, _caller) | |
when is_list(literal) or is_atom(literal) or is_integer(literal) or is_float(literal) do | |
:erlang.error( | |
ArgumentError.exception( | |
"expected binary argument in <> operator but got: #{Macro.to_string(literal)}" | |
) | |
) | |
end | |
defp wrap_concatenation(other, side, caller) do | |
expanded = expand_concat_argument(other, side, caller) | |
{:"::", [], [expanded, {:binary, [], nil}]} | |
end | |
defp expand_concat_argument(arg, :left, %{context: :match} = caller) do | |
expanded_arg = | |
case bootstrapped?(Macro) do | |
true -> Macro.expand(arg, caller) | |
false -> arg | |
end | |
case expanded_arg do | |
{var, _, nil} when is_atom(var) -> | |
invalid_concat_left_argument_error(Atom.to_string(var)) | |
{:^, _, [{var, _, nil}]} when is_atom(var) -> | |
invalid_concat_left_argument_error("^#{Atom.to_string(var)}") | |
_ -> | |
expanded_arg | |
end | |
end | |
defp expand_concat_argument(arg, _, _) do | |
arg | |
end | |
defp invalid_concat_left_argument_error(arg) do | |
:erlang.error( | |
ArgumentError.exception( | |
"the left argument of <> operator inside a match should always be a literal " <> | |
"binary because its size can't be verified. Got: #{arg}" | |
) | |
) | |
end | |
@doc """ | |
Raises an exception. | |
If `message` is a string, it raises a `RuntimeError` exception with it. | |
If `message` is an atom, it just calls `raise/2` with the atom as the first | |
argument and `[]` as the second one. | |
If `message` is an exception struct, it is raised as is. | |
If `message` is anything else, `raise` will fail with an `ArgumentError` | |
exception. | |
## Examples | |
iex> raise "oops" | |
** (RuntimeError) oops | |
try do | |
1 + :foo | |
rescue | |
x in [ArithmeticError] -> | |
IO.puts("that was expected") | |
raise x | |
end | |
""" | |
defmacro raise(message) do | |
# Try to figure out the type at compilation time | |
# to avoid dead code and make Dialyzer happy. | |
message = | |
case not is_binary(message) and bootstrapped?(Macro) do | |
true -> Macro.expand(message, __CALLER__) | |
false -> message | |
end | |
case message do | |
message when is_binary(message) -> | |
quote do | |
:erlang.error(RuntimeError.exception(unquote(message))) | |
end | |
{:<<>>, _, _} = message -> | |
quote do | |
:erlang.error(RuntimeError.exception(unquote(message))) | |
end | |
alias when is_atom(alias) -> | |
quote do | |
:erlang.error(unquote(alias).exception([])) | |
end | |
_ -> | |
quote do | |
:erlang.error(Kernel.Utils.raise(unquote(message))) | |
end | |
end | |
end | |
@doc """ | |
Raises an exception. | |
Calls the `exception/1` function on the given argument (which has to be a | |
module name like `ArgumentError` or `RuntimeError`) passing `attributes` | |
in order to retrieve the exception struct. | |
Any module that contains a call to the `defexception/1` macro automatically | |
implements the `c:Exception.exception/1` callback expected by `raise/2`. | |
For more information, see `defexception/1`. | |
## Examples | |
iex> raise(ArgumentError, "Sample") | |
** (ArgumentError) Sample | |
""" | |
defmacro raise(exception, attributes) do | |
quote do | |
:erlang.error(unquote(exception).exception(unquote(attributes))) | |
end | |
end | |
@doc """ | |
Raises an exception preserving a previous stacktrace. | |
Works like `raise/1` but does not generate a new stacktrace. | |
Note that `__STACKTRACE__` can be used inside catch/rescue | |
to retrieve the current stacktrace. | |
## Examples | |
try do | |
raise "oops" | |
rescue | |
exception -> | |
reraise exception, __STACKTRACE__ | |
end | |
""" | |
defmacro reraise(message, stacktrace) do | |
# Try to figure out the type at compilation time | |
# to avoid dead code and make Dialyzer happy. | |
case Macro.expand(message, __CALLER__) do | |
message when is_binary(message) -> | |
quote do | |
:erlang.error( | |
:erlang.raise(:error, RuntimeError.exception(unquote(message)), unquote(stacktrace)) | |
) | |
end | |
{:<<>>, _, _} = message -> | |
quote do | |
:erlang.error( | |
:erlang.raise(:error, RuntimeError.exception(unquote(message)), unquote(stacktrace)) | |
) | |
end | |
alias when is_atom(alias) -> | |
quote do | |
:erlang.error(:erlang.raise(:error, unquote(alias).exception([]), unquote(stacktrace))) | |
end | |
message -> | |
quote do | |
:erlang.error( | |
:erlang.raise(:error, Kernel.Utils.raise(unquote(message)), unquote(stacktrace)) | |
) | |
end | |
end | |
end | |
@doc """ | |
Raises an exception preserving a previous stacktrace. | |
`reraise/3` works like `reraise/2`, except it passes arguments to the | |
`exception/1` function as explained in `raise/2`. | |
## Examples | |
try do | |
raise "oops" | |
rescue | |
exception -> | |
reraise WrapperError, [exception: exception], __STACKTRACE__ | |
end | |
""" | |
defmacro reraise(exception, attributes, stacktrace) do | |
quote do | |
:erlang.raise( | |
:error, | |
unquote(exception).exception(unquote(attributes)), | |
unquote(stacktrace) | |
) | |
end | |
end | |
@doc """ | |
Text-based match operator. Matches the term on the `left` | |
against the regular expression or string on the `right`. | |
If `right` is a regular expression, returns `true` if `left` matches right. | |
If `right` is a string, returns `true` if `left` contains `right`. | |
## Examples | |
iex> "abcd" =~ ~r/c(d)/ | |
true | |
iex> "abcd" =~ ~r/e/ | |
false | |
iex> "abcd" =~ ~r// | |
true | |
iex> "abcd" =~ "bc" | |
true | |
iex> "abcd" =~ "ad" | |
false | |
iex> "abcd" =~ "abcd" | |
true | |
iex> "abcd" =~ "" | |
true | |
""" | |
@spec String.t() =~ (String.t() | Regex.t()) :: boolean | |
def left =~ "" when is_binary(left), do: true | |
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) do | |
Regex.match?(right, left) | |
end | |
@doc ~S""" | |
Inspects the given argument according to the `Inspect` protocol. | |
The second argument is a keyword list with options to control | |
inspection. | |
## Options | |
`inspect/2` accepts a list of options that are internally | |
translated to an `Inspect.Opts` struct. Check the docs for | |
`Inspect.Opts` to see the supported options. | |
## Examples | |
iex> inspect(:foo) | |
":foo" | |
iex> inspect([1, 2, 3, 4, 5], limit: 3) | |
"[1, 2, 3, ...]" | |
iex> inspect([1, 2, 3], pretty: true, width: 0) | |
"[1,\n 2,\n 3]" | |
iex> inspect("olá" <> <<0>>) | |
"<<111, 108, 195, 161, 0>>" | |
iex> inspect("olá" <> <<0>>, binaries: :as_strings) | |
"\"olá\\0\"" | |
iex> inspect("olá", binaries: :as_binaries) | |
"<<111, 108, 195, 161>>" | |
iex> inspect('bar') | |
"'bar'" | |
iex> inspect([0 | 'bar']) | |
"[0, 98, 97, 114]" | |
iex> inspect(100, base: :octal) | |
"0o144" | |
iex> inspect(100, base: :hex) | |
"0x64" | |
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(fn a, b -> a + b end) | |
#=> #Function<...> | |
The `Inspect` protocol can be derived to hide certain fields | |
from structs, so they don't show up in logs, inspects and similar. | |
See the "Deriving" section of the documentation of the `Inspect` | |
protocol for more information. | |
""" | |
@spec inspect(Inspect.t(), keyword) :: String.t() | |
def inspect(term, opts \\ []) when is_list(opts) do | |
opts = Inspect.Opts.new(opts) | |
limit = | |
case opts.pretty do | |
true -> opts.width | |
false -> :infinity | |
end | |
doc = Inspect.Algebra.group(Inspect.Algebra.to_doc(term, opts)) | |
IO.iodata_to_binary(Inspect.Algebra.format(doc, limit)) | |
end | |
@doc """ | |
Creates and updates a struct. | |
The `struct` argument may be an atom (which defines `defstruct`) | |
or a `struct` itself. The second argument is any `Enumerable` that | |
emits two-element tuples (key-value pairs) during enumeration. | |
Keys in the `Enumerable` that don't exist in the struct are automatically | |
discarded. Note that keys must be atoms, as only atoms are allowed when | |
defining a struct. If keys in the `Enumerable` are duplicated, the last | |
entry will be taken (same behaviour as `Map.new/1`). | |
This function is useful for dynamically creating and updating structs, as | |
well as for converting maps to structs; in the latter case, just inserting | |
the appropriate `:__struct__` field into the map may not be enough and | |
`struct/2` should be used instead. | |
## Examples | |
defmodule User do | |
defstruct name: "john" | |
end | |
struct(User) | |
#=> %User{name: "john"} | |
opts = [name: "meg"] | |
user = struct(User, opts) | |
#=> %User{name: "meg"} | |
struct(user, unknown: "value") | |
#=> %User{name: "meg"} | |
struct(User, %{name: "meg"}) | |
#=> %User{name: "meg"} | |
# String keys are ignored | |
struct(User, %{"name" => "meg"}) | |
#=> %User{name: "john"} | |
""" | |
@spec struct(module | struct, Enum.t()) :: struct | |
def struct(struct, fields \\ []) do | |
struct(struct, fields, fn | |
{:__struct__, _val}, acc -> | |
acc | |
{key, val}, acc -> | |
case acc do | |
%{^key => _} -> %{acc | key => val} | |
_ -> acc | |
end | |
end) | |
end | |
@doc """ | |
Similar to `struct/2` but checks for key validity. | |
The function `struct!/2` emulates the compile time behaviour | |
of structs. This means that: | |
* when building a struct, as in `struct!(SomeStruct, key: :value)`, | |
it is equivalent to `%SomeStruct{key: :value}` and therefore this | |
function will check if every given key-value belongs to the struct. | |
If the struct is enforcing any key via `@enforce_keys`, those will | |
be enforced as well; | |
* when updating a struct, as in `struct!(%SomeStruct{}, key: :value)`, | |
it is equivalent to `%SomeStruct{struct | key: :value}` and therefore this | |
function will check if every given key-value belongs to the struct. | |
However, updating structs does not enforce keys, as keys are enforced | |
only when building; | |
""" | |
@spec struct!(module | struct, Enum.t()) :: struct | |
def struct!(struct, fields \\ []) | |
def struct!(struct, fields) when is_atom(struct) do | |
validate_struct!(struct.__struct__(fields), struct, 1) | |
end | |
def struct!(struct, fields) when is_map(struct) do | |
struct(struct, fields, fn | |
{:__struct__, _}, acc -> | |
acc | |
{key, val}, acc -> | |
Map.replace!(acc, key, val) | |
end) | |
end | |
defp struct(struct, [], _fun) when is_atom(struct) do | |
validate_struct!(struct.__struct__(), struct, 0) | |
end | |
defp struct(struct, fields, fun) when is_atom(struct) do | |
struct(validate_struct!(struct.__struct__(), struct, 0), fields, fun) | |
end | |
defp struct(%_{} = struct, [], _fun) do | |
struct | |
end | |
defp struct(%_{} = struct, fields, fun) do | |
Enum.reduce(fields, struct, fun) | |
end | |
defp validate_struct!(%{__struct__: module} = struct, module, _arity) do | |
struct | |
end | |
defp validate_struct!(%{__struct__: struct_name}, module, arity) when is_atom(struct_name) do | |
error_message = | |
"expected struct name returned by #{inspect(module)}.__struct__/#{arity} to be " <> | |
"#{inspect(module)}, got: #{inspect(struct_name)}" | |
:erlang.error(ArgumentError.exception(error_message)) | |
end | |
defp validate_struct!(expr, module, arity) do | |
error_message = | |
"expected #{inspect(module)}.__struct__/#{arity} to return a map with a :__struct__ " <> | |
"key that holds the name of the struct (atom), got: #{inspect(expr)}" | |
:erlang.error(ArgumentError.exception(error_message)) | |
end | |
@doc """ | |
Returns true if `term` is a struct; otherwise returns `false`. | |
Allowed in guard tests. | |
## Examples | |
iex> is_struct(URI.parse("/")) | |
true | |
iex> is_struct(%{}) | |
false | |
""" | |
@doc since: "1.10.0", guard: true | |
defmacro is_struct(term) do | |
case __CALLER__.context do | |
nil -> | |
quote do | |
case unquote(term) do | |
%_{} -> true | |
_ -> false | |
end | |
end | |
:match -> | |
invalid_match!(:is_struct) | |
:guard -> | |
quote do | |
is_map(unquote(term)) and :erlang.is_map_key(:__struct__, unquote(term)) and | |
is_atom(:erlang.map_get(:__struct__, unquote(term))) | |
end | |
end | |
end | |
@doc """ | |
Returns true if `term` is a struct of `name`; otherwise returns `false`. | |
Allowed in guard tests. | |
## Examples | |
iex> is_struct(URI.parse("/"), URI) | |
true | |
iex> is_struct(URI.parse("/"), Macro.Env) | |
false | |
""" | |
@doc since: "1.11.0", guard: true | |
defmacro is_struct(term, name) do | |
case __CALLER__.context do | |
nil -> | |
quote generated: true do | |
case unquote(name) do | |
name when is_atom(name) -> | |
case unquote(term) do | |
%{__struct__: ^name} -> true | |
_ -> false | |
end | |
_ -> | |
raise ArgumentError | |
end | |
end | |
:match -> | |
invalid_match!(:is_struct) | |
:guard -> | |
quote do | |
is_map(unquote(term)) and | |
(is_atom(unquote(name)) or :fail) and | |
:erlang.is_map_key(:__struct__, unquote(term)) and | |
:erlang.map_get(:__struct__, unquote(term)) == unquote(name) | |
end | |
end | |
end | |
@doc """ | |
Returns true if `term` is an exception; otherwise returns `false`. | |
Allowed in guard tests. | |
## Examples | |
iex> is_exception(%RuntimeError{}) | |
true | |
iex> is_exception(%{}) | |
false | |
""" | |
@doc since: "1.11.0", guard: true | |
defmacro is_exception(term) do | |
case __CALLER__.context do | |
nil -> | |
quote do | |
case unquote(term) do | |
%_{__exception__: true} -> true | |
_ -> false | |
end | |
end | |
:match -> | |
invalid_match!(:is_exception) | |
:guard -> | |
quote do | |
is_map(unquote(term)) and :erlang.is_map_key(:__struct__, unquote(term)) and | |
is_atom(:erlang.map_get(:__struct__, unquote(term))) and | |
:erlang.is_map_key(:__exception__, unquote(term)) and | |
:erlang.map_get(:__exception__, unquote(term)) == true | |
end | |
end | |
end | |
@doc """ | |
Returns true if `term` is an exception of `name`; otherwise returns `false`. | |
Allowed in guard tests. | |
## Examples | |
iex> is_exception(%RuntimeError{}, RuntimeError) | |
true | |
iex> is_exception(%RuntimeError{}, Macro.Env) | |
false | |
""" | |
@doc since: "1.11.0", guard: true | |
defmacro is_exception(term, name) do | |
case __CALLER__.context do | |
nil -> | |
quote do | |
case unquote(name) do | |
name when is_atom(name) -> | |
case unquote(term) do | |
%{__struct__: ^name, __exception__: true} -> true | |
_ -> false | |
end | |
_ -> | |
raise ArgumentError | |
end | |
end | |
:match -> | |
invalid_match!(:is_exception) | |
:guard -> | |
quote do | |
is_map(unquote(term)) and | |
(is_atom(unquote(name)) or :fail) and | |
:erlang.is_map_key(:__struct__, unquote(term)) and | |
:erlang.map_get(:__struct__, unquote(term)) == unquote(name) and | |
:erlang.is_map_key(:__exception__, unquote(term)) and | |
:erlang.map_get(:__exception__, unquote(term)) == true | |
end | |
end | |
end | |
@doc """ | |
Pipes `value` into the given `fun`. | |
In other words, it invokes `fun` with `value` as argument. | |
This is most commonly used in pipelines, allowing you | |
to pipe a value to a function outside of its first argument. | |
### Examples | |
iex> 1 |> then(fn x -> x * 2 end) | |
2 | |
""" | |
@doc since: "1.12.0" | |
defmacro then(value, fun) do | |
quote do | |
unquote(fun).(unquote(value)) | |
end | |
end | |
@doc """ | |
Gets a value from a nested structure. | |
Uses the `Access` module to traverse the structures | |
according to the given `keys`, unless the `key` is a | |
function, which is detailed in a later section. | |
## Examples | |
iex> users = %{"john" => %{age: 27}, "meg" => %{age: 23}} | |
iex> get_in(users, ["john", :age]) | |
27 | |
`get_in/2` can also use the accessors in the `Access` module | |
to traverse more complex data structures. For example, here we | |
use `Access.all/0` to traverse a list: | |
iex> users = [%{name: "john", age: 27}, %{name: "meg", age: 23}] | |
iex> get_in(users, [Access.all(), :age]) | |
[27, 23] | |
In case any of the components returns `nil`, `nil` will be returned | |
and `get_in/2` won't traverse any futher: | |
iex> users = %{"john" => %{age: 27}, "meg" => %{age: 23}} | |
iex> get_in(users, ["unknown", :age]) | |
nil | |
iex> users = nil | |
iex> get_in(users, [Access.all(), :age]) | |
nil | |
The main feature of `get_in/2` is precisely that it aborts traversal | |
when a `nil` value is found. Unless you need nil-safety, you are likely | |
better off by writing "regular" Elixir code: | |
iex> users = %{"john" => %{age: 27}, "meg" => %{age: 23}} | |
iex> users["john"][:age] | |
27 | |
iex> users = [%{name: "john", age: 27}, %{name: "meg", age: 23}] | |
iex> Enum.map(users, fn user -> user[:age] end) | |
[27, 23] | |
Alternatively, if you need to access complex data-structures, you can | |
use pattern matching: | |
case users do | |
%{"john" => %{age: age}} -> age | |
_ -> default_value | |
end | |
## Functions as keys | |
If a key given to `get_in/2` is a function, the function will be invoked | |
passing three arguments: | |
* the operation (`:get`) | |
* the data to be accessed | |
* a function to be invoked next | |
This means `get_in/2` can be extended to provide custom lookups. | |
That's precisely how the `Access.all/0` key in the previous section | |
behaves. For example, we can manually implement such traversal as | |
follows: | |
iex> users = [%{name: "john", age: 27}, %{name: "meg", age: 23}] | |
iex> all = fn :get, data, next -> Enum.map(data, next) end | |
iex> get_in(users, [all, :age]) | |
[27, 23] | |
The `Access` module ships with many convenience accessor functions. | |
See `Access.all/0`, `Access.key/2`, and others as examples. | |
## Working with structs | |
By default, structs do not implement the `Access` behaviour required | |
by this function. Therefore, you can't do this: | |
get_in(some_struct, [:some_key, :nested_key]) | |
The good news is that structs have predefined shape. Therefore, | |
you can write instead: | |
some_struct.some_key.nested_key | |
If, by any chance, `some_key` can return nil, you can always | |
fallback to pattern matching to provide nested struct handling: | |
case some_struct do | |
%{some_key: %{nested_key: value}} -> value | |
%{} -> nil | |
end | |
""" | |
@spec get_in(Access.t(), nonempty_list(term)) :: term | |
def get_in(data, keys) | |
def get_in(nil, [_ | _]), do: nil | |
def get_in(data, [h]) when is_function(h), do: h.(:get, data, & &1) | |
def get_in(data, [h | t]) when is_function(h), do: h.(:get, data, &get_in(&1, t)) | |
def get_in(data, [h]), do: Access.get(data, h) | |
def get_in(data, [h | t]), do: get_in(Access.get(data, h), t) | |
@doc """ | |
Puts a value in a nested structure. | |
Uses the `Access` module to traverse the structures | |
according to the given `keys`, unless the `key` is a | |
function. If the key is a function, it will be invoked | |
as specified in `get_and_update_in/3`. | |
## Examples | |
iex> users = %{"john" => %{age: 27}, "meg" => %{age: 23}} | |
iex> put_in(users, ["john", :age], 28) | |
%{"john" => %{age: 28}, "meg" => %{age: 23}} | |
If any of the intermediate values are nil, it will raise: | |
iex> users = %{"john" => %{age: 27}, "meg" => %{age: 23}} | |
iex> put_in(users, ["jane", :age], "oops") | |
** (ArgumentError) could not put/update key :age on a nil value | |
""" | |
@spec put_in(Access.t(), nonempty_list(term), term) :: Access.t() | |
def put_in(data, [_ | _] = keys, value) do | |
elem(get_and_update_in(data, keys, fn _ -> {nil, value} end), 1) | |
end | |
@doc """ | |
Updates a key in a nested structure. | |
Uses the `Access` module to traverse the structures | |
according to the given `keys`, unless the `key` is a | |
function. If the key is a function, it will be invoked | |
as specified in `get_and_update_in/3`. | |
`data` is a nested structure (that is, a map, keyword | |
list, or struct that implements the `Access` behaviour). | |
The `fun` argument receives the value of `key` (or `nil` | |
if `key` is not present) and the result replaces the value | |
in the structure. | |
## Examples | |
iex> users = %{"john" => %{age: 27}, "meg" => %{age: 23}} | |
iex> update_in(users, ["john", :age], &(&1 + 1)) | |
%{"john" => %{age: 28}, "meg" => %{age: 23}} | |
Note the current value given to the anonymous function may be `nil`. | |
If any of the intermediate values are nil, it will raise: | |
iex> users = %{"john" => %{age: 27}, "meg" => %{age: 23}} | |
iex> update_in(users, ["jane", :age], & &1 + 1) | |
** (ArgumentError) could not put/update key :age on a nil value | |
""" | |
@spec update_in(Access.t(), nonempty_list(term), (term -> term)) :: Access.t() | |
def update_in(data, [_ | _] = keys, fun) when is_function(fun) do | |
elem(get_and_update_in(data, keys, fn x -> {nil, fun.(x)} end), 1) | |
end | |
@doc """ | |
Gets a value and updates a nested structure. | |
`data` is a nested structure (that is, a map, keyword | |
list, or struct that implements the `Access` behaviour). | |
The `fun` argument receives the value of `key` (or `nil` if `key` | |
is not present) and must return one of the following values: | |
* a two-element tuple `{current_value, new_value}`. In this case, | |
`current_value` is the retrieved value which can possibly be operated on before | |
being returned. `new_value` is the new value to be stored under `key`. | |
* `:pop`, which implies that the current value under `key` | |
should be removed from the structure and returned. | |
This function uses the `Access` module to traverse the structures | |
according to the given `keys`, unless the `key` is a function, | |
which is detailed in a later section. | |
## Examples | |
This function is useful when there is a need to retrieve the current | |
value (or something calculated in function of the current value) and | |
update it at the same time. For example, it could be used to read the | |
current age of a user while increasing it by one in one pass: | |
iex> users = %{"john" => %{age: 27}, "meg" => %{age: 23}} | |
iex> get_and_update_in(users, ["john", :age], &{&1, &1 + 1}) | |
{27, %{"john" => %{age: 28}, "meg" => %{age: 23}}} | |
Note the current value given to the anonymous function may be `nil`. | |
If any of the intermediate values are nil, it will raise: | |
iex> users = %{"john" => %{age: 27}, "meg" => %{age: 23}} | |
iex> get_and_update_in(users, ["jane", :age], &{&1, &1 + 1}) | |
** (ArgumentError) could not put/update key :age on a nil value | |
## Functions as keys | |
If a key is a function, the function will be invoked passing three | |
arguments: | |
* the operation (`:get_and_update`) | |
* the data to be accessed | |
* a function to be invoked next | |
This means `get_and_update_in/3` can be extended to provide custom | |
lookups. The downside is that functions cannot be stored as keys | |
in the accessed data structures. | |
When one of the keys is a function, the function is invoked. | |
In the example below, we use a function to get and increment all | |
ages inside a list: | |
iex> users = [%{name: "john", age: 27}, %{name: "meg", age: 23}] | |
iex> all = fn :get_and_update, data, next -> | |
...> data |> Enum.map(next) |> Enum.unzip() | |
...> end | |
iex> get_and_update_in(users, [all, :age], &{&1, &1 + 1}) | |
{[27, 23], [%{name: "john", age: 28}, %{name: "meg", age: 24}]} | |
If the previous value before invoking the function is `nil`, | |
the function *will* receive `nil` as a value and must handle it | |
accordingly (be it by failing or providing a sane default). | |
The `Access` module ships with many convenience accessor functions, | |
like the `all` anonymous function defined above. See `Access.all/0`, | |
`Access.key/2`, and others as examples. | |
""" | |
@spec get_and_update_in( | |
structure, | |
keys, | |
(term | nil -> {current_value, new_value} | :pop) | |
) :: {current_value, new_structure :: structure} | |
when structure: Access.t(), | |
keys: nonempty_list(any), | |
current_value: Access.value(), | |
new_value: Access.value() | |
def get_and_update_in(data, keys, fun) | |
def get_and_update_in(data, [head], fun) when is_function(head, 3), | |
do: head.(:get_and_update, data, fun) | |
def get_and_update_in(data, [head | tail], fun) when is_function(head, 3), | |
do: head.(:get_and_update, data, &get_and_update_in(&1, tail, fun)) | |
def get_and_update_in(data, [head], fun) when is_function(fun, 1), | |
do: Access.get_and_update(data, head, fun) | |
def get_and_update_in(data, [head | tail], fun) when is_function(fun, 1), | |
do: Access.get_and_update(data, head, &get_and_update_in(&1, tail, fun)) | |
@doc """ | |
Pops a key from the given nested structure. | |
Uses the `Access` protocol to traverse the structures | |
according to the given `keys`, unless the `key` is a | |
function. If the key is a function, it will be invoked | |
as specified in `get_and_update_in/3`. | |
## Examples | |
iex> users = %{"john" => %{age: 27}, "meg" => %{age: 23}} | |
iex> pop_in(users, ["john", :age]) | |
{27, %{"john" => %{}, "meg" => %{age: 23}}} | |
In case any entry returns `nil`, its key will be removed | |
and the deletion will be considered a success. | |
iex> users = %{"john" => %{age: 27}, "meg" => %{age: 23}} | |
iex> pop_in(users, ["jane", :age]) | |
{nil, %{"john" => %{age: 27}, "meg" => %{age: 23}}} | |
""" | |
@spec pop_in(data, nonempty_list(Access.get_and_update_fun(term, data) | term)) :: {term, data} | |
when data: Access.container() | |
def pop_in(data, keys) | |
def pop_in(nil, [key | _]) do | |
raise ArgumentError, "could not pop key #{inspect(key)} on a nil value" | |
end | |
def pop_in(data, [_ | _] = keys) do | |
pop_in_data(data, keys) | |
end | |
defp pop_in_data(nil, [_ | _]), do: :pop | |
defp pop_in_data(data, [fun]) when is_function(fun), | |
do: fun.(:get_and_update, data, fn _ -> :pop end) | |
defp pop_in_data(data, [fun | tail]) when is_function(fun), | |
do: fun.(:get_and_update, data, &pop_in_data(&1, tail)) | |
defp pop_in_data(data, [key]), do: Access.pop(data, key) | |
defp pop_in_data(data, [key | tail]), | |
do: Access.get_and_update(data, key, &pop_in_data(&1, tail)) | |
@doc """ | |
Puts a value in a nested structure via the given `path`. | |
This is similar to `put_in/3`, except the path is extracted via | |
a macro rather than passing a list. For example: | |
put_in(opts[:foo][:bar], :baz) | |
Is equivalent to: | |
put_in(opts, [:foo, :bar], :baz) | |
This also works with nested structs and the `struct.path.to.value` way to specify | |
paths: | |
put_in(struct.foo.bar, :baz) | |
Note that in order for this macro to work, the complete path must always | |
be visible by this macro. For more information about the supported path | |
expressions, please check `get_and_update_in/2` docs. | |
## Examples | |
iex> users = %{"john" => %{age: 27}, "meg" => %{age: 23}} | |
iex> put_in(users["john"][:age], 28) | |
%{"john" => %{age: 28}, "meg" => %{age: 23}} | |
iex> users = %{"john" => %{age: 27}, "meg" => %{age: 23}} | |
iex> put_in(users["john"].age, 28) | |
%{"john" => %{age: 28}, "meg" => %{age: 23}} | |
""" | |
defmacro put_in(path, value) do | |
case unnest(path, [], true, "put_in/2") do | |
{[h | t], true} -> | |
nest_update_in(h, t, quote(do: fn _ -> unquote(value) end)) | |
{[h | t], false} -> | |
expr = nest_get_and_update_in(h, t, quote(do: fn _ -> {nil, unquote(value)} end)) | |
quote(do: :erlang.element(2, unquote(expr))) | |
end | |
end | |
@doc """ | |
Pops a key from the nested structure via the given `path`. | |
This is similar to `pop_in/2`, except the path is extracted via | |
a macro rather than passing a list. For example: | |
pop_in(opts[:foo][:bar]) | |
Is equivalent to: | |
pop_in(opts, [:foo, :bar]) | |
Note that in order for this macro to work, the complete path must always | |
be visible by this macro. For more information about the supported path | |
expressions, please check `get_and_update_in/2` docs. | |
## Examples | |
iex> users = %{"john" => %{age: 27}, "meg" => %{age: 23}} | |
iex> pop_in(users["john"][:age]) | |
{27, %{"john" => %{}, "meg" => %{age: 23}}} | |
iex> users = %{john: %{age: 27}, meg: %{age: 23}} | |
iex> pop_in(users.john[:age]) | |
{27, %{john: %{}, meg: %{age: 23}}} | |
In case any entry returns `nil`, its key will be removed | |
and the deletion will be considered a success. | |
""" | |
defmacro pop_in(path) do | |
{[h | t], _} = unnest(path, [], true, "pop_in/1") | |
nest_pop_in(:map, h, t) | |
end | |
@doc """ | |
Updates a nested structure via the given `path`. | |
This is similar to `update_in/3`, except the path is extracted via | |
a macro rather than passing a list. For example: | |
update_in(opts[:foo][:bar], &(&1 + 1)) | |
Is equivalent to: | |
update_in(opts, [:foo, :bar], &(&1 + 1)) | |
This also works with nested structs and the `struct.path.to.value` way to specify | |
paths: | |
update_in(struct.foo.bar, &(&1 + 1)) | |
Note that in order for this macro to work, the complete path must always | |
be visible by this macro. For more information about the supported path | |
expressions, please check `get_and_update_in/2` docs. | |
## Examples | |
iex> users = %{"john" => %{age: 27}, "meg" => %{age: 23}} | |
iex> update_in(users["john"][:age], &(&1 + 1)) | |
%{"john" => %{age: 28}, "meg" => %{age: 23}} | |
iex> users = %{"john" => %{age: 27}, "meg" => %{age: 23}} | |
iex> update_in(users["john"].age, &(&1 + 1)) | |
%{"john" => %{age: 28}, "meg" => %{age: 23}} | |
""" | |
defmacro update_in(path, fun) do | |
case unnest(path, [], true, "update_in/2") do | |
{[h | t], true} -> | |
nest_update_in(h, t, fun) | |
{[h | t], false} -> | |
expr = nest_get_and_update_in(h, t, quote(do: fn x -> {nil, unquote(fun).(x)} end)) | |
quote(do: :erlang.element(2, unquote(expr))) | |
end | |
end | |
@doc """ | |
Gets a value and updates a nested data structure via the given `path`. | |
This is similar to `get_and_update_in/3`, except the path is extracted | |
via a macro rather than passing a list. For example: | |
get_and_update_in(opts[:foo][:bar], &{&1, &1 + 1}) | |
Is equivalent to: | |
get_and_update_in(opts, [:foo, :bar], &{&1, &1 + 1}) | |
This also works with nested structs and the `struct.path.to.value` way to specify | |
paths: | |
get_and_update_in(struct.foo.bar, &{&1, &1 + 1}) | |
Note that in order for this macro to work, the complete path must always | |
be visible by this macro. See the "Paths" section below. | |
## Examples | |
iex> users = %{"john" => %{age: 27}, "meg" => %{age: 23}} | |
iex> get_and_update_in(users["john"].age, &{&1, &1 + 1}) | |
{27, %{"john" => %{age: 28}, "meg" => %{age: 23}}} | |
## Paths | |
A path may start with a variable, local or remote call, and must be | |
followed by one or more: | |
* `foo[bar]` - accesses the key `bar` in `foo`; in case `foo` is nil, | |
`nil` is returned | |
* `foo.bar` - accesses a map/struct field; in case the field is not | |
present, an error is raised | |
Here are some valid paths: | |
users["john"][:age] | |
users["john"].age | |
User.all()["john"].age | |
all_users()["john"].age | |
Here are some invalid ones: | |
# Does a remote call after the initial value | |
users["john"].do_something(arg1, arg2) | |
# Does not access any key or field | |
users | |
""" | |
defmacro get_and_update_in(path, fun) do | |
{[h | t], _} = unnest(path, [], true, "get_and_update_in/2") | |
nest_get_and_update_in(h, t, fun) | |
end | |
defp nest_update_in([], fun), do: fun | |
defp nest_update_in(list, fun) do | |
quote do | |
fn x -> unquote(nest_update_in(quote(do: x), list, fun)) end | |
end | |
end | |
defp nest_update_in(h, [{:map, key} | t], fun) do | |
quote do | |
Map.update!(unquote(h), unquote(key), unquote(nest_update_in(t, fun))) | |
end | |
end | |
defp nest_get_and_update_in([], fun), do: fun | |
defp nest_get_and_update_in(list, fun) do | |
quote do | |
fn x -> unquote(nest_get_and_update_in(quote(do: x), list, fun)) end | |
end | |
end | |
defp nest_get_and_update_in(h, [{:access, key} | t], fun) do | |
quote do | |
Access.get_and_update(unquote(h), unquote(key), unquote(nest_get_and_update_in(t, fun))) | |
end | |
end | |
defp nest_get_and_update_in(h, [{:map, key} | t], fun) do | |
quote do | |
Map.get_and_update!(unquote(h), unquote(key), unquote(nest_get_and_update_in(t, fun))) | |
end | |
end | |
defp nest_pop_in(kind, list) do | |
quote do | |
fn x -> unquote(nest_pop_in(kind, quote(do: x), list)) end | |
end | |
end | |
defp nest_pop_in(:map, h, [{:access, key}]) do | |
quote do | |
case unquote(h) do | |
nil -> {nil, nil} | |
h -> Access.pop(h, unquote(key)) | |
end | |
end | |
end | |
defp nest_pop_in(_, _, [{:map, key}]) do | |
raise ArgumentError, | |
"cannot use pop_in when the last segment is a map/struct field. " <> | |
"This would effectively remove the field #{inspect(key)} from the map/struct" | |
end | |
defp nest_pop_in(_, h, [{:map, key} | t]) do | |
quote do | |
Map.get_and_update!(unquote(h), unquote(key), unquote(nest_pop_in(:map, t))) | |
end | |
end | |
defp nest_pop_in(_, h, [{:access, key}]) do | |
quote do | |
case unquote(h) do | |
nil -> :pop | |
h -> Access.pop(h, unquote(key)) | |
end | |
end | |
end | |
defp nest_pop_in(_, h, [{:access, key} | t]) do | |
quote do | |
Access.get_and_update(unquote(h), unquote(key), unquote(nest_pop_in(:access, t))) | |
end | |
end | |
defp unnest({{:., _, [Access, :get]}, _, [expr, key]}, acc, _all_map?, kind) do | |
unnest(expr, [{:access, key} | acc], false, kind) | |
end | |
defp unnest({{:., _, [expr, key]}, _, []}, acc, all_map?, kind) | |
when is_tuple(expr) and :erlang.element(1, expr) != :__aliases__ and | |
:erlang.element(1, expr) != :__MODULE__ do | |
unnest(expr, [{:map, key} | acc], all_map?, kind) | |
end | |
defp unnest(other, [], _all_map?, kind) do | |
raise ArgumentError, | |
"expected expression given to #{kind} to access at least one element, " <> | |
"got: #{Macro.to_string(other)}" | |
end | |
defp unnest(other, acc, all_map?, kind) do | |
case proper_start?(other) do | |
true -> | |
{[other | acc], all_map?} | |
false -> | |
raise ArgumentError, | |
"expression given to #{kind} must start with a variable, local or remote call " <> | |
"and be followed by an element access, got: #{Macro.to_string(other)}" | |
end | |
end | |
defp proper_start?({{:., _, [expr, _]}, _, _args}) | |
when is_atom(expr) | |
when :erlang.element(1, expr) == :__aliases__ | |
when :erlang.element(1, expr) == :__MODULE__, | |
do: true | |
defp proper_start?({atom, _, _args}) | |
when is_atom(atom), | |
do: true | |
defp proper_start?(other), do: not is_tuple(other) | |
@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" | |
""" | |
defmacro to_string(term) do | |
quote(do: :"Elixir.String.Chars".to_string(unquote(term))) | |
end | |
@doc """ | |
Converts the given term to a charlist according to the `List.Chars` protocol. | |
## Examples | |
iex> to_charlist(:foo) | |
'foo' | |
""" | |
defmacro to_charlist(term) do | |
quote(do: List.Chars.to_charlist(unquote(term))) | |
end | |
@doc """ | |
Returns `true` if `term` is `nil`, `false` otherwise. | |
Allowed in guard clauses. | |
## Examples | |
iex> is_nil(1) | |
false | |
iex> is_nil(nil) | |
true | |
""" | |
@doc guard: true | |
defmacro is_nil(term) do | |
quote(do: unquote(term) == nil) | |
end | |
@doc """ | |
A convenience macro that checks if the right side (an expression) matches the | |
left side (a pattern). | |
## Examples | |
iex> match?(1, 1) | |
true | |
iex> match?({1, _}, {1, 2}) | |
true | |
iex> map = %{a: 1, b: 2} | |
iex> match?(%{a: _}, map) | |
true | |
iex> a = 1 | |
iex> match?(^a, 1) | |
true | |
`match?/2` is very useful when filtering or finding a value in an enumerable: | |
iex> list = [a: 1, b: 2, a: 3] | |
iex> Enum.filter(list, &match?({:a, _}, &1)) | |
[a: 1, a: 3] | |
Guard clauses can also be given to the match: | |
iex> list = [a: 1, b: 2, a: 3] | |
iex> Enum.filter(list, &match?({:a, x} when x < 2, &1)) | |
[a: 1] | |
However, variables assigned in the match will not be available | |
outside of the function call (unlike regular pattern matching with the `=` | |
operator): | |
iex> match?(_x, 1) | |
true | |
iex> binding() | |
[] | |
""" | |
defmacro match?(pattern, expr) do | |
success = | |
quote do | |
unquote(pattern) -> true | |
end | |
failure = | |
quote generated: true do | |
_ -> false | |
end | |
{:case, [], [expr, [do: success ++ failure]]} | |
end | |
@doc """ | |
Module attribute unary operator. | |
Reads and writes attributes in the current module. | |
The canonical example for attributes is annotating that a module | |
implements an OTP behaviour, such as `GenServer`: | |
defmodule MyServer do | |
@behaviour GenServer | |
# ... callbacks ... | |
end | |
By default Elixir supports all the module attributes supported by Erlang, but | |
custom attributes can be used as well: | |
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 custom attributes to store temporary data that | |
will be available at compile-time. Custom attributes may be configured to | |
behave closer to Erlang by using `Module.register_attribute/3`. | |
Finally, note 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. | |
## Compile-time considerations | |
One thing to keep in mind is that references to other modules, even | |
in module attributes, generate compile-time dependencies to said | |
modules. | |
For example, take this common pattern: | |
@values [:foo, :bar, :baz] | |
def handle_arg(arg) when arg in @values do | |
... | |
end | |
While the above is fine, imagine if instead you have actual | |
module names in the module attribute, like this: | |
@values [Foo, Bar, Baz] | |
def handle_arg(arg) when arg in @values do | |
... | |
end | |
The code above will define a compile-time dependency on the modules | |
`Foo`, `Bar`, and `Baz`, in a way that, if any of them change, the | |
current module will have to recompile. In such cases, it may be | |
preferred to avoid the module attribute altogether: | |
def handle_arg(arg) when arg in [Foo, Bar, Baz] do | |
... | |
end | |
""" | |
defmacro @expr | |
defmacro @{:__aliases__, _meta, _args} do | |
raise ArgumentError, "module attributes set via @ cannot start with an uppercase letter" | |
end | |
defmacro @{name, meta, args} do | |
assert_module_scope(__CALLER__, :@, 1) | |
function? = __CALLER__.function != nil | |
cond do | |
# Check for Macro as it is compiled later than Kernel | |
not bootstrapped?(Macro) -> | |
nil | |
not function? and __CALLER__.context == :match -> | |
raise ArgumentError, | |
""" | |
invalid write attribute syntax. If you want to define an attribute, don't do this: | |
@foo = :value | |
Instead, do this: | |
@foo :value | |
""" | |
# Typespecs attributes are currently special cased by the compiler | |
is_list(args) and typespec?(name) -> | |
case bootstrapped?(Kernel.Typespec) do | |
false -> | |
:ok | |
true -> | |
pos = :elixir_locals.cache_env(__CALLER__) | |
%{line: line, file: file, module: module} = __CALLER__ | |
quote do | |
Kernel.Typespec.deftypespec( | |
unquote(name), | |
unquote(Macro.escape(hd(args), unquote: true)), | |
unquote(line), | |
unquote(file), | |
unquote(module), | |
unquote(pos) | |
) | |
end | |
end | |
true -> | |
do_at(args, meta, name, function?, __CALLER__) | |
end | |
end | |
# @attribute(value) | |
defp do_at([arg], meta, name, function?, env) do | |
line = | |
case :lists.keymember(:context, 1, meta) do | |
true -> nil | |
false -> env.line | |
end | |
cond do | |
function? -> | |
raise ArgumentError, "cannot set attribute @#{name} inside function/macro" | |
name == :behavior -> | |
warn_message = "@behavior attribute is not supported, please use @behaviour instead" | |
IO.warn(warn_message, Macro.Env.stacktrace(env)) | |
:lists.member(name, [:moduledoc, :typedoc, :doc]) -> | |
arg = {env.line, arg} | |
quote do | |
Module.__put_attribute__(__MODULE__, unquote(name), unquote(arg), unquote(line)) | |
end | |
true -> | |
arg = expand_attribute(name, arg, env) | |
quote do | |
Module.__put_attribute__(__MODULE__, unquote(name), unquote(arg), unquote(line)) | |
end | |
end | |
end | |
# @attribute() | |
defp do_at([], meta, name, function?, env) do | |
IO.warn( | |
"the @#{name}() notation (with parenthesis) is deprecated, please use @#{name} (without parenthesis) instead", | |
Macro.Env.stacktrace(env) | |
) | |
do_at(nil, meta, name, function?, env) | |
end | |
# @attribute | |
defp do_at(args, _meta, name, function?, env) when is_atom(args) do | |
line = env.line | |
doc_attr? = :lists.member(name, [:moduledoc, :typedoc, :doc]) | |
case function? do | |
true -> | |
value = | |
case Module.__get_attribute__(env.module, name, line) do | |
{_, doc} when doc_attr? -> doc | |
other -> other | |
end | |
try do | |
:elixir_quote.escape(value, :default, false) | |
rescue | |
ex in [ArgumentError] -> | |
raise ArgumentError, | |
"cannot inject attribute @#{name} into function/macro because " <> | |
Exception.message(ex) | |
end | |
false when doc_attr? -> | |
quote do | |
case Module.__get_attribute__(__MODULE__, unquote(name), unquote(line)) do | |
{_, doc} -> doc | |
other -> other | |
end | |
end | |
false -> | |
quote do | |
Module.__get_attribute__(__MODULE__, unquote(name), unquote(line)) | |
end | |
end | |
end | |
# Error cases | |
defp do_at([{call, meta, ctx_or_args}, [{:do, _} | _] = kw], _meta, name, _function?, _env) do | |
args = | |
case is_atom(ctx_or_args) do | |
true -> [] | |
false -> ctx_or_args | |
end | |
code = "\n@#{name} (#{Macro.to_string({call, meta, args ++ [kw]})})" | |
raise ArgumentError, """ | |
expected 0 or 1 argument for @#{name}, got 2. | |
It seems you are trying to use the do-syntax with @module attributes \ | |
but the do-block is binding to the attribute name. You probably want \ | |
to wrap the argument value in parentheses, like this: | |
#{String.replace(code, "\n", "\n ")} | |
""" | |
end | |
defp do_at(args, _meta, name, _function?, _env) do | |
raise ArgumentError, "expected 0 or 1 argument for @#{name}, got: #{length(args)}" | |
end | |
defp expand_attribute(:compile, arg, env) do | |
Macro.prewalk(arg, fn | |
# {:no_warn_undefined, alias} | |
{elem, {:__aliases__, _, _} = alias} -> | |
{elem, Macro.expand(alias, %{env | function: {:__info__, 1}})} | |
# {alias, fun, arity} | |
{:{}, meta, [{:__aliases__, _, _} = alias, fun, arity]} -> | |
{:{}, meta, [Macro.expand(alias, %{env | function: {:__info__, 1}}), fun, arity]} | |
node -> | |
node | |
end) | |
end | |
defp expand_attribute(_, arg, _), do: arg | |
defp typespec?(:type), do: true | |
defp typespec?(:typep), do: true | |
defp typespec?(:opaque), do: true | |
defp typespec?(:spec), do: true | |
defp typespec?(:callback), do: true | |
defp typespec?(:macrocallback), do: true | |
defp typespec?(_), do: false | |
@doc """ | |
Returns the binding for the given context as a keyword list. | |
In the returned result, keys are variable names and values are the | |
corresponding variable values. | |
If the given `context` is `nil` (by default it is), the binding for the | |
current context is returned. | |
## Examples | |
iex> x = 1 | |
iex> binding() | |
[x: 1] | |
iex> x = 2 | |
iex> binding() | |
[x: 2] | |
iex> binding(:foo) | |
[] | |
iex> var!(x, :foo) = 1 | |
1 | |
iex> binding(:foo) | |
[x: 1] | |
""" | |
defmacro binding(context \\ nil) do | |
in_match? = Macro.Env.in_match?(__CALLER__) | |
bindings = | |
for {v, c} <- Macro.Env.vars(__CALLER__), c == context do | |
{v, wrap_binding(in_match?, {v, [generated: true], c})} | |
end | |
:lists.sort(bindings) | |
end | |
defp wrap_binding(true, var) do | |
quote(do: ^unquote(var)) | |
end | |
defp wrap_binding(_, var) do | |
var | |
end | |
@doc """ | |
Provides an `if/2` macro. | |
This macro expects the first argument to be a condition and the second | |
argument to be a keyword list. | |
## One-liner examples | |
if(foo, do: bar) | |
In the example above, `bar` will be returned if `foo` evaluates to | |
a truthy value (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 | |
It's also possible to pass a block to the `if/2` macro. The first | |
example above would be translated to: | |
if foo do | |
bar | |
end | |
Note that `do/end` become delimiters. The second example would | |
translate to: | |
if foo do | |
bar | |
else | |
baz | |
end | |
In order to compare more than two clauses, the `cond/1` macro has to be used. | |
""" | |
defmacro if(condition, clauses) do | |
build_if(condition, clauses) | |
end | |
defp build_if(condition, do: do_clause) do | |
build_if(condition, do: do_clause, else: nil) | |
end | |
defp build_if(condition, do: do_clause, else: else_clause) do | |
optimize_boolean( | |
quote do | |
case unquote(condition) do | |
x when :"Elixir.Kernel".in(x, [false, nil]) -> unquote(else_clause) | |
_ -> unquote(do_clause) | |
end | |
end | |
) | |
end | |
defp build_if(_condition, _arguments) do | |
raise ArgumentError, | |
"invalid or duplicate keys for if, only \"do\" and an optional \"else\" are permitted" | |
end | |
@doc """ | |
Provides an `unless` macro. | |
This macro evaluates and returns the `do` block passed in as the second | |
argument if `condition` evaluates to a falsy value (`false` or `nil`). | |
Otherwise, it returns the value of the `else` block if present or `nil` if not. | |
See also `if/2`. | |
## Examples | |
iex> unless(Enum.empty?([]), do: "Hello") | |
nil | |
iex> unless(Enum.empty?([1, 2, 3]), do: "Hello") | |
"Hello" | |
iex> unless Enum.sum([2, 2]) == 5 do | |
...> "Math still works" | |
...> else | |
...> "Math is broken" | |
...> end | |
"Math still works" | |
""" | |
defmacro unless(condition, clauses) do | |
build_unless(condition, clauses) | |
end | |
defp build_unless(condition, do: do_clause) do | |
build_unless(condition, do: do_clause, else: nil) | |
end | |
defp build_unless(condition, do: do_clause, else: else_clause) do | |
quote do | |
if(unquote(condition), do: unquote(else_clause), else: unquote(do_clause)) | |
end | |
end | |
defp build_unless(_condition, _arguments) do | |
raise ArgumentError, | |
"invalid or duplicate keys for unless, " <> | |
"only \"do\" and an optional \"else\" are permitted" | |
end | |
@doc """ | |
Destructures two lists, assigning each term in the | |
right one to the matching term in the left one. | |
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]) | |
iex> {x, y, z} | |
{1, 2, 3} | |
In the example above, even though the right list has more entries than the | |
left one, destructuring works fine. If the right list is smaller, the | |
remaining elements are simply set to `nil`: | |
iex> destructure([x, y, z], [1]) | |
iex> {x, y, z} | |
{1, nil, nil} | |
The left-hand side supports any expression you would use | |
on the left-hand 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 in the right | |
list. Otherwise, it will raise a `MatchError` (like the `=` operator would | |
do). | |
""" | |
defmacro destructure(left, right) when is_list(left) do | |
quote do | |
unquote(left) = Kernel.Utils.destructure(unquote(right), unquote(length(left))) | |
end | |
end | |
@doc """ | |
Creates a range from `first` to `last`. | |
If first is less than last, the range will be increasing from | |
first to last. If first is equal to last, the range will contain | |
one element, which is the number itself. | |
If first is more than last, the range will be decreasing from first | |
to last, albeit this behaviour is deprecated. Instead prefer to | |
explicitly list the step with `first..last//-1`. | |
See the `Range` module for more information. | |
## Examples | |
iex> 0 in 1..3 | |
false | |
iex> 2 in 1..3 | |
true | |
iex> Enum.to_list(1..3) | |
[1, 2, 3] | |
""" | |
defmacro first..last do | |
case bootstrapped?(Macro) do | |
true -> | |
first = Macro.expand(first, __CALLER__) | |
last = Macro.expand(last, __CALLER__) | |
validate_range!(first, last) | |
range(__CALLER__.context, first, last) | |
false -> | |
range(__CALLER__.context, first, last) | |
end | |
end | |
defp range(_context, first, last) when is_integer(first) and is_integer(last) do | |
# TODO: Deprecate inferring a range with a step of -1 on Elixir v1.17 | |
step = if first <= last, do: 1, else: -1 | |
{:%{}, [], [__struct__: Elixir.Range, first: first, last: last, step: step]} | |
end | |
defp range(nil, first, last) do | |
quote(do: Elixir.Range.new(unquote(first), unquote(last))) | |
end | |
defp range(:guard, first, last) do | |
# TODO: Deprecate me inside guard when sides are not integers on Elixir v1.17 | |
{:%{}, [], [__struct__: Elixir.Range, first: first, last: last, step: nil]} | |
end | |
defp range(:match, first, last) do | |
# TODO: Deprecate me inside match in all occasions (including literals) on Elixir v1.17 | |
{:%{}, [], [__struct__: Elixir.Range, first: first, last: last]} | |
end | |
@doc """ | |
Creates a range from `first` to `last` with `step`. | |
See the `Range` module for more information. | |
## Examples | |
iex> 0 in 1..3//1 | |
false | |
iex> 2 in 1..3//1 | |
true | |
iex> 2 in 1..3//2 | |
false | |
iex> Enum.to_list(1..3//1) | |
[1, 2, 3] | |
iex> Enum.to_list(1..3//2) | |
[1, 3] | |
iex> Enum.to_list(3..1//-1) | |
[3, 2, 1] | |
iex> Enum.to_list(1..0//1) | |
[] | |
""" | |
@doc since: "1.12.0" | |
defmacro first..last//step do | |
case bootstrapped?(Macro) do | |
true -> | |
first = Macro.expand(first, __CALLER__) | |
last = Macro.expand(last, __CALLER__) | |
step = Macro.expand(step, __CALLER__) | |
validate_range!(first, last) | |
validate_step!(step) | |
range(__CALLER__.context, first, last, step) | |
false -> | |
range(__CALLER__.context, first, last, step) | |
end | |
end | |
defp range(context, first, last, step) | |
when is_integer(first) and is_integer(last) and is_integer(step) | |
when context != nil do | |
{:%{}, [], [__struct__: Elixir.Range, first: first, last: last, step: step]} | |
end | |
defp range(nil, first, last, step) do | |
quote(do: Elixir.Range.new(unquote(first), unquote(last), unquote(step))) | |
end |