Elixir identifiers must start with a letter or underscore, optionally followed by letters, digits, and underscores. Here letter means any UTF-8 letter character (optionally with a combining mark) and digit means a UTF-8 decimal-digit character. If you’re using ASCII, this does what you’d expect. The identifiers may end with a question mark or an exclamation mark.
Here are some examples of valid variables:
| name josé _age まつもと _42 адрес! |
And some examples of invalid variables:
| name• a±2 42 |
Module, record, protocol, and behavior names start with an uppercase letter and are BumpyCase. All other identifiers start with a lowercase letter or an underscore, and by convention use underscores between words. If the first character is an underscore, Elixir doesn’t report a warning if the variable is unused in a pattern match or function parameter list.
By convention, source files use two-character indentation for nesting—and they use spaces, not tabs, to achieve this.
Comments start with a hash sign (#) and run to the end of the line.
The Elixir distribution comes with a code formatter, which can be used to convert a source file into the “approved” representation. We’ll look at this here. Most examples in this book follow this format (except where I think it is particularly ugly).
Elixir has three special values related to Boolean operations: true, false, and nil. nil is treated as false in Boolean contexts.
(A bit of trivia: all three of these values are aliases for atoms of the same name, so true is the same as the atom :true.)
In most contexts, any value other than false or nil is treated as true. We sometimes refer to this as truthy as opposed to true.
Elixir has a very rich set of operators. Here’s a subset we’ll use in this book:
| a === b # strict equality (so 1 === 1.0 is false) |
| a !== b # strict inequality (so 1 !== 1.0 is true) |
| a == b # value equality (so 1 == 1.0 is true) |
| a != b # value inequality (so 1 != 1.0 is false) |
| a > b # normal comparison |
| a >= b # : |
| a < b # : |
| a <= b # : |
The ordering comparisons in Elixir are less strict than in many languages, as you can compare values of different types. If the types are the same or are compatible (for example, 3 > 2 or 3.0 < 5), the comparison uses natural ordering. Otherwise comparison is based on type according to this rule:
number < atom < reference < function < port < pid < tuple < map < list < binary
(These operators expect true or false as their first argument.)
| a or b # true if a is true; otherwise b |
| a and b # false if a is false; otherwise b |
| not a # false if a is true; true otherwise |
These operators take arguments of any type. Any value apart from nil or false is interpreted as true.
| a || b # a if a is truthy; otherwise b |
| a && b # b if a is truthy; otherwise a |
| !a # false if a is truthy; otherwise true |
+ - * / div rem
Integer division yields a floating-point result. Use div(a,b) to get an integer.
rem is the remainder operator. It is called as a function (rem(11, 3) => 2). It differs from normal modulo operations in that the result will have the same sign as the function’s first argument.
| binary1 <> binary2 # concatenates two binaries (Later we'll |
| # see that binaries include strings.) |
| list1 ++ list2 # concatenates two lists |
| list1 -- list2 # removes elements of list 2 from a copy of list 1 |
| a in enum # tests if a is included in enum (for example, |
| # a list, a range, or a map). For maps, a should |
| # be a {key, value} tuple. |