File: operators.md

package info (click to toggle)
elixir-lang 1.14.0.dfsg-2
  • links: PTS, VCS
  • area: main
  • in suites: bookworm
  • size: 12,204 kB
  • sloc: erlang: 9,877; sh: 311; makefile: 276
file content (164 lines) | stat: -rw-r--r-- 7,229 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
# Operators

This document covers operators in Elixir, how they are parsed, how they can be defined, and how they can be overridden.

## Operator precedence and associativity

The following is a list of all operators that Elixir is capable of parsing, ordered from higher to lower precedence, alongside their associativity:

Operator                                       | Associativity
---------------------------------------------- | -------------
`@`                                            | Unary
`.`                                            | Left
`+` `-` `!` `^` `not`                          | Unary
`**`                                           | Left
`*` `/`                                        | Left
`+` `-`                                        | Left
`++` `--` `+++` `---` `..` `<>`                | Right
`in` `not in`                                  | Left
`\|>` `<<<` `>>>` `<<~` `~>>` `<~` `~>` `<~>`  | Left
`<` `>` `<=` `>=`                              | Left
`==` `!=` `=~` `===` `!==`                     | Left
`&&` `&&&` `and`                               | Left
`\|\|` `\|\|\|` `or`                           | Left
`=`                                            | Right
`&`                                            | Unary
`=>` (valid only inside `%{}`)                 | Right
`\|`                                           | Right
`::`                                           | Right
`when`                                         | Right
`<-` `\\`                                      | Left

## General operators

Elixir provides the following built-in operators:

  * [`+`](`+/1`) and [`-`](`-/1`) - unary positive/negative
  * [`+`](`+/2`), [`-`](`-/2`), [`*`](`*/2`), and [`/`](`//2`) - basic arithmetic operations
  * [`++`](`++/2`) and [`--`](`--/2`) - list concatenation and subtraction
  * [`and`](`and/2`) and [`&&`](`&&/2`) - strict and relaxed boolean "and"
  * [`or`](`or/2`) and [`||`](`||/2`) - strict and relaxed boolean "or"
  * [`not`](`not/1`) and [`!`](`!/1`) - strict and relaxed boolean "not"
  * [`in`](`in/2`) and [`not in`](`in/2`) - membership
  * [`@`](`@/1`) - module attribute
  * [`..`](`../2`) - range creation
  * [`<>`](`<>/2`) - binary concatenation
  * [`|>`](`|>/2`) - pipeline
  * [`=~`](`=~/2`) - text-based match

Many of those can be used in guards; consult the [list of allowed guard functions and operators](patterns-and-guards.md#list-of-allowed-functions-and-operators).

Additionally, there are a few other operators that Elixir parses but doesn't actually use.
See [Custom and overridden operators](#custom-and-overridden-operators) below for a list and for guidelines about their use.

Some other operators are special forms and cannot be overridden:

  * [`^`](`^/1`) - pin operator
  * [`.`](`./2`) - dot operator
  * [`=`](`=/2`) - match operator
  * [`&`](`&/1`) - capture operator
  * [`::`](`::/2`) - type operator

Finally, these operators appear in the precedence table above but are only meaningful within certain constructs:

  * `=>` - see [`%{}`](`%{}/1`)
  * `when` - see [Guards](patterns-and-guards.md#guards)
  * `<-` - see [`for`](`for/1`) and [`with`](`with/1`)
  * `\\` - see [Default arguments](Kernel.html#def/2-default-arguments)

## Comparison operators

Elixir provides the following built-in comparison operators (all of which can be used in guards):

  * [`==`](`==/2`) - equal to
  * [`===`](`===/2`) - strictly equal to
  * [`!=`](`!=/2`) - inequal to
  * [`!==`](`!==/2`) - strictly inequal to
  * [`<`](`</2`) - less-than
  * [`>`](`>/2`) - greater-than
  * [`<=`](`<=/2`) - less-than or equal to
  * [`>=`](`>=/2`) - greater-than or equal to

The only difference between [`==`](`==/2`) and [`===`](`===/2`) is that [`===`](`===/2`) is strict when it comes to comparing integers and floats:

```elixir
iex> 1 == 1.0
true
iex> 1 === 1.0
false
```

[`!=`](`!=/2`) and [`!==`](`!==/2`) act as the negation of [`==`](`==/2`) and [`===`](`===/2`), respectively.

### Term ordering

In Elixir, different data types can be compared using comparison operators:

```elixir
iex> 1 < :an_atom
true
```

The reason we can compare different data types is pragmatism. Sorting algorithms don't need to worry about different data types in order to sort. For reference, the overall sorting order is defined below:

```
number < atom < reference < function < port < pid < tuple < map < list < bitstring
```

When comparing two numbers of different types (a number being either an integer or a float), a conversion to the type with greater precision will always occur, unless the comparison operator used is either [`===`](`===/2`) or [`!==`](`!==/2`). A float will be considered more precise than an integer, unless the float is greater/less than +/-9007199254740992.0 respectively, at which point all the significant figures of the float are to the left of the decimal point. This behavior exists so that the comparison of large numbers remains transitive.

The collection types are compared using the following rules:

* Tuples are compared by size, then element by element.
* Maps are compared by size, then by keys in ascending term order, then by values in key order. In the specific case of maps' key ordering, integers are always considered to be less than floats.
* Lists are compared element by element.
* Bitstrings are compared byte by byte, incomplete bytes are compared bit by bit.
* Atoms are compared using their string value, codepoint by codepoint.

## Custom and overridden operators

### Defining custom operators

Elixir is capable of parsing a predefined set of operators. It's not possible to define new operators (as supported by some languages). However, not all operators that Elixir can parse are *used* by Elixir: for example, `+` and `||` are used by Elixir for addition and boolean *or*, but `<~>` is not used (but valid).

To define an operator, you can use the usual `def*` constructs (`def`, `defp`, `defmacro`, and so on) but with a syntax similar to how the operator is used:

```elixir
defmodule MyOperators do
  # We define ~> to return the maximum of the given two numbers,
  # and <~ to return the minimum.

  def a ~> b, do: max(a, b)
  def a <~ b, do: min(a, b)
end
```

To use the newly defined operators, you **have to** import the module that defines them:

```elixir
iex> import MyOperators
iex> 1 ~> 2
2
iex> 1 <~ 2
1
```

The following is a table of all the operators that Elixir is capable of parsing, but that are not used by default:

  * `|||`
  * `&&&`
  * `<<<`
  * `>>>`
  * `<<~`
  * `~>>`
  * `<~`
  * `~>`
  * `<~>`
  * `+++`
  * `---`

The following operators are used by the `Bitwise` module when imported: [`&&&`](`Bitwise.&&&/2`), [`<<<`](`Bitwise.<<</2`), [`>>>`](`Bitwise.>>>/2`), and [`|||`](`Bitwise.|||/2`). See the documentation for `Bitwise` for more information.

Note that the Elixir community generally discourages custom operators. They can be hard to read and even more to understand, as they don't have a descriptive name like functions do. That said, some specific cases or custom domain specific languages (DSLs) may justify these practices.

It is also possible to replace predefined operators, such as `+`, but doing so is extremely discouraged.