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
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
|
---
language: elixir
author: Joao Marques
author_url: http://github.com/mrshankly
filename: learnelixir.ex
---
```elixir
# Single line comments start with a hash.
## --------------------
## -- Basic types
## --------------------
# There are numbers
3 # integer
0x1F # integer
3.0 # float
# Atoms, that are literals, a constant with name. They start with `:`.
:hello # atom
# Tuples that are stored contigously in memory.
{1,2,3} # tuple
# We can access a tuple element with the `elem` function:
elem({1, 2, 3}, 0) # => 1
# Lists that are implemented as linked lists.
[1,2,3] # list
# We can access the head and tail of a list as follows:
[head | tail] = [1,2,3]
head # => 1
tail # => [2,3]
# In elixir, just like in erlang, the `=` denotes pattern matching and
# not an assignment.
#
# This means that the left-hand side (pattern) is matched against a
# right-hand side.
#
# This is how the above example of accessing the head and tail of a list works.
# A pattern match will error when the sides don't match, in this example
# the tuples have different sizes.
{a, b, c} = {1, 2} # => ** (MatchError) no match of right hand side value: {1,2}
# There's also binaries
<<1,2,3>> # binary
# Strings and char lists
"hello" # string
'hello' # char list
# Strings are all encoded in UTF-8:
"héllò" # => "héllò"
# Strings are really just binaries, and char lists are just lists.
<<?a, ?b, ?c>> # => "abc"
[?a, ?b, ?c] # => 'abc'
# `?a` in elixir returns the ASCII integer for the letter `a`
?a # => 97
## TODO:
######################################################
## JOIN BINARIES AND LISTS
######################################################
## --------------------
## -- Operators
## --------------------
# Some math
1 + 1 # => 2
10 - 5 # => 5
5 * 2 # => 10
10 / 2 # => 5.0
# In elixir the operator `/` always returns a float.
# To do integer division use `div`
div(10, 2) # => 5
# To get the division remainder use `rem`
rem(10, 3) # => 1
# There's also boolean operators: `or`, `and` and `not`.
# These operators expect a boolean as their first argument.
true and true # => true
false or true # => true
1 and true # => ** (ArgumentError) argument error
# Elixir also provides `||`, `&&` and `!` which accept arguments of any type.
# All values except `false` and `nil` will evaluate to true.
1 || true # => 1
false && 1 # => false
nil && 20 # => nil
!true # => false
# For comparisons we have: `==`, `!=`, `===`, `!==`, `<=`, `>=`, `<` and `>`
1 == 1 # => true
1 != 1 # => false
1 < 2 # => true
# `===` and `!==` are more strict when comparing integers and floats:
1 == 1.0 # => true
1 === 1.0 # => false
# We can also compare two different data types:
1 < :hello # => true
# The overall sorting order is defined below:
number < atom < reference < functions < port < pid < tuple < list < bit string
# To quote Joe Armstrong on this: "The actual order is not important,
# but that a total ordering is well defined is important."
## --------------------
## -- Control Flow
## --------------------
# `if` expression
if false do
"This will never be seen"
else
"This will"
end
# There's also `unless`
unless true do
"This will never be seen"
else
"This will"
end
# Remember pattern matching? Many control-flow structures in elixir rely on it.
# `case` allows us to compare a value against many patterns:
case {:one, :two} do
{:four, :five} ->
"This won't match"
{:one, x} ->
"This will match and assign `x` to `:two`"
_ ->
"This will match any value"
end
# It's common practive to assign a value to `_` if we don't need it.
# For example, if only the head of a list matters to us:
[head | _] = [1,2,3]
head # => 1
# For better readability we can do the following:
[head | _tail] = [:a, :b, :c]
head # => :a
# `cond` lets us check for many conditions at the same time.
# Use `cond` instead of nesting many `if` expressions.
cond do
1 + 1 == 3 ->
"I will never be seen"
2 * 5 == 12 ->
"Me neither"
1 + 2 == 3 ->
"But I will"
end
# It is common to see a last condition equal to `true`, which will always match.
cond do
1 + 1 == 3 ->
"I will never be seen"
2 * 5 == 12 ->
"Me neither"
true ->
"But I will (this is essentially an else)"
end
# `try/catch` is used to catch values that are thrown, it also supports an
# `after` clause that is invoked whether or not a value is catched.
try do
throw(:hello)
catch
message -> "Got #{message}."
after
IO.puts("I'm the after clause.")
end
# => I'm the after clause
# "Got :hello"
## TODO:
######################################################
## GUARDS
######################################################
## ---------------------------
## -- Modules and Functions
## ---------------------------
```
|