├── examples
├── elixir
├── fahren.exs
└── fahren_with_error_handling.exs
├── .gitattributes
├── Readme.md
├── script.ex
├── index.livemd
└── chapters
├── files
├── if.svg
├── if-else.svg
├── elsif.svg
├── pda.svg
├── pda2.svg
├── cond.svg
├── nested.svg
├── arch.svg
└── arch2.svg
├── chapter_4.livemd
├── chapter_3.livemd
├── chapter_2.livemd
└── chapter_1.livemd
/examples/elixir:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/.gitattributes:
--------------------------------------------------------------------------------
1 | # Auto detect text files and perform LF normalization
2 | * text=auto
3 |
--------------------------------------------------------------------------------
/examples/fahren.exs:
--------------------------------------------------------------------------------
1 | input = IO.gets("Enter Fahrenheit Temperature:") |> String.trim
2 | fahr = String.to_float(input)
3 | cel = (fahr - 32.0) * 5.0 / 9.0
4 | IO.puts cel
5 |
--------------------------------------------------------------------------------
/Readme.md:
--------------------------------------------------------------------------------
1 | # Elixir for Everybody
2 |
3 | Exploring Data Using Elixir based on
4 | Python for Everybody
5 | Exploring Data Using Python 3
6 | Dr. Charles R. Severance
7 |
8 | Elixir is a dynamic programming language.
--------------------------------------------------------------------------------
/examples/fahren_with_error_handling.exs:
--------------------------------------------------------------------------------
1 | inp = IO.gets("Enter Fahrenheit Temperature:") |> String.trim()
2 |
3 | try do
4 | fahr = String.to_float(inp)
5 | cel = (fahr - 32.0) * 5.0 / 9.0
6 | IO.puts(cel)
7 | rescue
8 | _error ->
9 | try do
10 | fahr = String.to_integer(inp)
11 | cel = (fahr - 32.0) * 5.0 / 9.0
12 | IO.puts(cel)
13 | rescue
14 | _error -> IO.puts("Please enter a number:\n")
15 | end
16 | end
17 |
--------------------------------------------------------------------------------
/script.ex:
--------------------------------------------------------------------------------
1 | # Livebook Blogpost
2 | # https://www.thestackcanary.com/elixirs-secret-weapon/
3 | # Livebook working with files
4 | # https://peterullrich.com/work-with-files-in-livebook
5 | # Read the file name from the user
6 | IO.puts("Enter file: ")
7 | name = String.trim(IO.gets(""))
8 |
9 | {word, count}=File.stream!(name)
10 | |> Stream.map(&String.split(&1, ~r{\s+}, trim: true))
11 | |> Stream.filter(&Enum.empty?(&1) == false)
12 | |> Enum.to_list() |> List.flatten()
13 | |> Enum.frequencies()
14 | |> Enum.max_by(fn {_, value} -> value end)
15 | |> IO.inspect
16 |
17 | IO.puts("The most used word is `#{word}` with #{count} appearences")
18 |
--------------------------------------------------------------------------------
/index.livemd:
--------------------------------------------------------------------------------
1 | # Elixir for Everybody
2 |
3 | ## About the book
4 |
5 | The goal of this book is to provide an Informatics-oriented introduction to programming. The primary difference between a computer science approach and the Informatics approach taken in this book is a greater focus on using Elixir to solve data analysis problems common in the world of Informatics.
6 |
7 | This book is based on [Python for Everybody](https://www.py4e.com/book) by Dr. Charles R. Severance
8 |
9 | ### Contents
10 |
11 | * [Chapter 1: Introduction](chapters/chapter_1.livemd)
12 | * [Chapter 2: Variables](chapters/chapter_2.livemd)
13 | * [Chapter 3: Conditionals](chapters/chapter_3.livemd)
14 | * [Chapter 4: Functions](chapters/chapter_4.livemd)
15 |
--------------------------------------------------------------------------------
/chapters/files/if.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/chapters/files/if-else.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/chapters/files/elsif.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/chapters/files/pda.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/chapters/files/pda2.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/chapters/files/cond.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
101 |
--------------------------------------------------------------------------------
/chapters/files/nested.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/chapters/files/arch.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/chapters/chapter_4.livemd:
--------------------------------------------------------------------------------
1 | # Functions
2 |
3 | ```elixir
4 | Mix.install([
5 | {:kino, "~> 0.12.0"}
6 | ])
7 |
8 | import IEx.Helpers
9 | ```
10 |
11 | ## Function calls
12 |
13 | In the context of programming, a function is a named sequence of expressions that performs a computation. When you define a function, you specify the name and the sequence of expressions. Later, you can invoke the function by name. We have already seen one example of a function invocation:
14 |
15 |
16 |
17 | ```elixir
18 | iex(1)> is_integer(3)
19 | true
20 | ```
21 |
22 | The name of the function is `is_integer`. The expression in parentheses is called the argument of the function. The argument is a value or variable (term) that we are passing into the function as input to the function. The result, for the `is_integer` function, is a boolean indicating whether the argument is an integer or not.
23 |
24 | It is common to say that a function "takes" an argument and "returns" a result. The result is called the _return value_.
25 |
26 | ## Built-in functions
27 |
28 | In Elixir, there are also built-in functions that can be used without providing a function definition. These functions are part of the [Elixir standard library](https://hexdocs.pm/elixir/Kernel.html#content) and are designed to solve common problems.
29 |
30 | The `List.first/1` and `List.last/1` functions give us the first and last value in a list, respectively:
31 |
32 |
33 |
34 | ```elixir
35 | iex(1)> List.first([1, 2, 3, 4, 5])
36 | 1
37 | iex(2)> List.last([1, 2, 3, 4, 5])
38 | 5
39 | ```
40 |
41 | The `List.first/1` function tells us the "first element" in the List (which turn out to be the integer "1") and the `List.last/1` shows the "last element" in the List (which turn out to be the integer "2")
42 |
43 | Another common built-in function is the `String.length/1` function, which can be used to find the length of a string:
44 |
45 |
46 |
47 | ```elixir
48 | iex(5)> String.length("Hello world")
49 | 11
50 | ```
51 |
52 | The `String.length/1` function returns the number of characters in the string (which is 11 in this case).
53 |
54 | ## Type conversion functions
55 |
56 | Elixir also provides built-in functions that convert values from one type to another. The `String.to_integer/1` function takes string representation and converts it to an integer, if it can, or complains otherwise:
57 |
58 |
59 |
60 | ```elixir
61 | iex(10)> String.to_integer("32")
62 | 32
63 | iex(11)> String.to_integer("Hello")
64 | ** (ArgumentError) errors were found at the given arguments:
65 |
66 | * 1st argument: not a textual representation of an integer
67 |
68 | erlang.erl:4719: :erlang.binary_to_integer("Hello")
69 | iex:11: (file)
70 | iex(12)> String.to_integer("3.99")
71 | ** (ArgumentError) errors were found at the given arguments:
72 |
73 | * 1st argument: not a textual representation of an integer
74 |
75 | erlang.erl:4719: :erlang.binary_to_integer("3.99")
76 | iex:12: (file)
77 |
78 | ```
79 |
80 | The `String.to_float/1` function takes string representation and converts it to an integer, if it can, or complains otherwise:
81 |
82 |
83 |
84 | ```elixir
85 | iex(1)> String.to_float("3.999")
86 | 3.999
87 | iex(2)> String.to_float("Hello")
88 | ** (ArgumentError) errors were found at the given arguments:
89 |
90 | * 1st argument: not a textual representation of a float
91 |
92 | :erlang.binary_to_float("Hello")
93 | iex:2: (file)
94 | iex(2)> String.to_float("3")
95 | ** (ArgumentError) errors were found at the given arguments:
96 |
97 | * 1st argument: not a textual representation of a float
98 |
99 | :erlang.binary_to_float("3")
100 | iex:2: (file)
101 | ```
102 |
103 | Finally `Integer.to_string/1` takes an `integer` as its argument and convert to a String, in the same way `Float.to_string/1` takes a `float` as its argument and convert to a String. In both cases if it can't it will complains.
104 |
105 |
106 |
107 | ```elixir
108 | iex(2)> Integer.to_string(3)
109 | "3"
110 | iex(3)> Integer.to_string(3.3)
111 | ** (ArgumentError) errors were found at the given arguments:
112 |
113 | * 1st argument: not an integer
114 |
115 | :erlang.integer_to_binary(3.3)
116 | iex:3: (file)
117 | iex(3)> Float.to_string(3.3)
118 | "3.3"
119 | iex(4)> Float.to_string(3)
120 | ** (FunctionClauseError) no function clause matching in Float.to_string/1
121 |
122 | The following arguments were given to Float.to_string/1:
123 |
124 | # 1
125 | 3
126 |
127 | Attempted function clauses (showing 1 out of 1):
128 |
129 | def to_string(float) when is_float(float)
130 |
131 | (elixir 1.16.2) lib/float.ex:624: Float.to_string/1
132 | iex:4: (file)
133 |
134 | ```
135 |
136 | **Exercise 1**: Using IEx or here in the following Elixir cell explore the the [Elixir Standard Library](https://hexdocs.pm/elixir/1.17.0/Kernel.html).
137 |
138 | * `exports Kernel`
139 | * `exports String`
140 | * `exports String`
141 |
142 | ```elixir
143 | exports(Kernel)
144 | h(String.to_integer())
145 | ```
146 |
147 | ## Math functions
148 |
149 | In Elixir, the `:math` module offers various mathematical functions that you can use in your programs. This module is part of the Erlang standard library. Elixir has seamless interoperability with Erlang libraries, and it encourages direct interaction with Erlang code. Keep in mind that Erlang conventions differ slightly: variables start with an uppercase letter, and atoms (simple lowercase names) are used. So, to access the Erlang `math` module in Elixir, you simply write `:math`.
150 |
151 | When using the `i` helper in the IEx (Interactive Elixir) shell, you can retrieve detailed information about an Erlang module. It provides a concise summary of the module, including its data type, bytecode location, source file, version, compile options, and implemented protocols.
152 |
153 | **Exercise 2** : Try to use `i (:math)` and `h :math` to access its documentation.
154 |
155 | To use a function from the `:math` module, you can use the dot notation to specify the name of the module and the name of the function, separated by a dot. For example:
156 |
157 |
158 |
159 | ```elixir
160 | iex> ratio = 10.0 / 1.0
161 | 10.0
162 | iex> decibels = 10 * :math.log10(ratio)
163 | 10.0
164 | ```
165 |
166 | The first example computes the logarithm base 10 of the signal-to-noise ratio using the `:math.log10/1` function. The `:math` module also provides a function called `:math.log/1` that computes logarithms base e.
167 |
168 | The second example finds the sine of a value in radians using the `:math.sin/1` function:
169 |
170 |
171 |
172 | ```elixir
173 | iex> radians = 0.7
174 | 0.7
175 | iex> height = :math.sin(radians)
176 | 0.644217687237691
177 | ```
178 |
179 | The `:math.sin/1` function, as well as the other trigonometric functions (`:math.cos/1`, `:math.tan/1`, etc.), take arguments in radians. To convert from degrees to radians, you can use the following formula:
180 |
181 |
182 |
183 | ```elixir
184 | iex> degrees = 45
185 | 45
186 | iex> radians = degrees * :math.pi / 180
187 | 0.7853981633974483
188 | iex> :math.sin(radians)
189 | 0.7071067811865476
190 | ```
191 |
192 | The expression `:math.pi` gets the value of π from the `:math` module. The value of this variable is an approximation of π, accurate to about 15 digits.
193 |
194 | If you know your trigonometry, you can check the previous result by comparing it to the square root of two divided by two:
195 |
196 |
197 |
198 | ```elixir
199 | iex> :math.sqrt(2) / 2.0
200 | 0.7071067811865476
201 | ```
202 |
203 | It is a good practice to familiarize yourself with the functions available in the `:math` module and to use them in your programs when needed. This can save you time and help you avoid errors in your mathematical
204 |
205 | To learn more about [math module](https://hexdocs.pm/elixir/main/erlang-libraries.html#the-math-module)
206 |
207 | ```elixir
208 | i(:math)
209 |
210 | h(:math)
211 | ```
212 |
213 | ## Random numbers
214 |
215 | ## Adding new functions
216 |
217 | ## Definitions and uses
218 |
219 | ## Flow of execution
220 |
221 | ## Parameters and arguments
222 |
223 | ## Fruitful functions and void functions
224 |
225 | ## Why functions?
226 |
227 | ## Debugging
228 |
229 | ## Glossary
230 |
231 | ## Exercises
232 |
--------------------------------------------------------------------------------
/chapters/files/arch2.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/chapters/chapter_3.livemd:
--------------------------------------------------------------------------------
1 |
2 |
3 | # Conditional execution
4 |
5 | ```elixir
6 | Mix.install([
7 | {:kino, "~> 0.12.0"}
8 | ])
9 |
10 | import IEx.Helpers
11 | ```
12 |
13 | ## Note
14 |
15 | In Elixir, while the language is inherently functional, we also have control flow constructs like `if`, `cond`, and `case` that allow us to solve problems in a more imperative manner when needed.
16 |
17 | But remember Elixir code tried to be declarative, no imperative.
18 |
19 | ## Boolean Expressions
20 |
21 | A boolean expression is an expression that is either `true` or `false`. Elixir supports true and false as booleans. The following examples use the operator `==`, which compares two operands and produces `true` if they are equal and `false` otherwise:
22 |
23 |
24 |
25 | ```elixir
26 | iex(1)> 5 == 5
27 | true
28 | iex(2)> 5 == 6
29 | false
30 | ```
31 |
32 | The `true` and `false` values are aliases for atoms of the same name,
33 | so `true` is the same as the atom `:true` and `false` as the atom `:false`
34 |
35 | These values can be combined with the strict boolean operators (`and/2`, `or/2`, `not/1`),
36 | they require their first argument to be a boolean.
37 |
38 |
39 |
40 | ```elixir
41 | iex(3)> true and (5 == 5)
42 | true
43 | iex(4)> 6 and true
44 | ** (BadBooleanError) expected a boolean on left-side of "and", got: 6
45 | iex:4: (file)
46 | ```
47 |
48 | Learn more about [Booleans](https://hexdocs.pm/elixir/basic-types.html#booleans-and-nil)
49 |
50 | The `==` operator is one of the comparison operators; the others are:
51 |
52 |
53 |
54 | ```elixir
55 | x === y # x is strict equal to y (value and type equality)
56 | X !== y # x is strict inequal to y (value and type equality)
57 | x != y # x is not equal to y (value equality)
58 | x == y # x is equal to y (value equality)
59 | x > y # x is greater than y
60 | x < y # x is less than y
61 | x >= y # x is greater than or equal to y
62 | x <= y # x is less than or equal to y
63 | ```
64 |
65 | ---
66 |
67 |
68 |
69 | ```elixir
70 | iex(6)> 5 === 5.0
71 | false
72 | iex(7)> 5 !== 5.0
73 | true
74 | ```
75 |
76 | Although these operations are probably familiar to you, the Elixir symbols are different from the mathematical symbols for the same operations. A common error is to use a single equal sign `(=)` instead of a double equal sign `(==)`. Remember that `=` is the binding operator and `==` is a comparison operator. There is no such thing as `=<` or `=>`.
77 |
78 | To learn more about [comparison operators](https://hexdocs.pm/elixir/operators.html#comparison-operators)
79 |
80 | ## Logical Operators
81 |
82 | There are three logical operators: `and`, `or`, and `not`. The semantics (meaning) of these operators is similar to their meaning in English. For example,
83 |
84 | `x > 0 and x < 10`
85 |
86 | is true only if `x` is greater than `0` and less than `10`.
87 |
88 | `rem(n, 2) == 0 or rem(n, 3) == 0` is `true` if either of the conditions is `true`, that is, if the number is divisible by 2 or 3.
89 |
90 | Finally, the not operator negates a boolean expression, so `not (x > y)` is true if `x > y` is false.
91 |
92 |
93 |
94 | ```elixir
95 | iex(1)> x = 1
96 | 1
97 | iex(2)> y = 2
98 | 2
99 | iex(3)> x > y
100 | false
101 | iex(4)> not (x > y)
102 | true
103 | ```
104 |
105 | Strictly speaking, the operands of the logical operators should be boolean expressions.
106 |
107 | ## Short-Cirtcuit Operators
108 |
109 | There are also equivalent boolean operators that work with any type of arguments.
110 | These operators (`&&/2`, `||/2`, and `!/1`) accept arguments of any type. Any value other than `nil` or `false` is considered `true`. Here we have introduced `nil`, representing there is no value.
111 |
112 |
113 |
114 | ```elixir
115 | x || y # x if x is truthy; otherwise y
116 | x && y # x if x is truthy; otherwise y
117 | !x # false if x is truthy; otherwise true
118 | ```
119 |
120 | ---
121 |
122 |
123 |
124 | ```elixir
125 | iex(5)> true || false
126 | true
127 | iex(6)> true && false
128 | false
129 | iex(7)> !true
130 | false
131 | ```
132 |
133 | ## Conditional execution
134 |
135 | In order to write useful programs, we almost always need the ability to check conditions and change the behavior of the program accordingly. `Conditional expressions` give us this ability. The simplest form is the `if` control flow structure :
136 |
137 |
138 |
139 | ```elixir
140 | iex(1)> x = 1
141 | 1
142 | iex(2)> if x > 0 do
143 | ...(2)> IO.puts("X is positive")
144 | ...(2)> end
145 | X is positive
146 | :ok
147 | ```
148 |
149 | ```elixir
150 | # Experiment rebingind the value x to
151 | # x = 0, x = -1
152 | x = 1
153 |
154 | if x > 0 do
155 | IO.puts("X is positive")
156 | end
157 | ```
158 |
159 | The boolean expression given to `if/2` is called the condition. Then we write the body between `do-end`.
160 |
161 | To learn more about [if control flow](https://hexdocs.pm/elixir/case-cond-and-if.html#if-unless)
162 |
163 |
164 |
165 | 
166 |
167 |
168 |
169 | If the condition given to `if/2` returns true the body given between `do-end` is executed. If not instead it returns nil.
170 |
171 | For example, if you want to print the values of `x` and `y` when `x` is greater than `y`, you can write it like this:
172 |
173 |
174 |
175 | ```elixir
176 | if x > y do
177 | IO.puts(x)
178 | IO.puts(y)
179 | end
180 | ```
181 |
182 | There is no limit on the number of expressions that can appear in the body `do-end`. Occasionally, it is useful to have a body with no statements (usually as a place holder for code you haven't written yet). In that case, you can use a comment, which does nothing.
183 |
184 |
185 |
186 | `````elixir
187 | if x < 0 do
188 | # Need to handle negative values, but we do nothing for now
189 | end
190 | ````
191 | If you enter an `if` expression in the IEx interpreter, the prompt will change from (iex(2))>) to three dots (…(2)) to indicate you are in the middle of an expression, as shown below:
192 |
193 | `````
194 |
195 | iex(1)> x = 3
196 | 3
197 | iex(2)> if x < 10 do
198 | ...(2)> IO.puts("small")
199 | ...(2)> end
200 | small
201 | :ok
202 | iex(3)>
203 |
204 | ```
205 |
206 | ```
207 |
208 | ## Alternative execution
209 |
210 | A second form of the `if/2` expression is alternative execution, in which there are two possibilities and the condition determines which one gets executed. The syntax looks like this:
211 |
212 |
213 |
214 | ```elixir
215 | if rem(x,2) == 0 do
216 | IO.puts("#{x} is even")
217 | else
218 | IO.puts("#{x} is odd")
219 | end
220 | ```
221 |
222 | If the remainder when x is divided by 2 is 0, then we know that x is even, and the program displays a message to that effect. If the condition is false, the `else` branch is executed
223 |
224 |
225 |
226 | 
227 |
228 |
229 |
230 | Since the condition must either be `true` or `false`, exactly one of the alternatives will be executed. The alternatives are called branches, because they are branches in the flow of execution.
231 |
232 | ## Chained Conditionals
233 |
234 | Sometimes there are more than two possibilities and we need more than two branches. One way to express a computation like that is a chained conditional, in Elixir we use `cond`:
235 |
236 |
237 |
238 | ```elixir
239 | cond do
240 | x < y ->
241 | IO.puts("#{x} is less than #{y}")
242 | x > y ->
243 | IO.puts("#{x} is greater than #{y}")
244 | true ->
245 | IO.puts("#{x} and #{y} are equal") # default clause or catch all.
246 | end
247 |
248 | ```
249 |
250 | There is no limit in the number of conditions.
251 | Each condition is checked in order. If the first is `false` or `nil`, the next is checked, and so on. If one of them is `true`, the corresponding branch is executed. Even if more than one condition is `true`, only the first `true` branch executes.
252 |
253 | If none of the conditions are satisfied, `cond` raises an error if and only if there is no a default or final condition, equal to `true`, which will always match.
254 |
255 | 
256 |
257 | In other languages `cond` is similar to the pattern `if-elseif`
258 | To learn more about [cond](https://hexdocs.pm/elixir/case-cond-and-if.html#cond)
259 |
260 | ## Nested conditionals
261 |
262 | One conditional can also be nested within another. We could have written the three-branch example like this:
263 |
264 |
265 |
266 | ```elixir
267 | if x == y do
268 | IO.puts("#{x} and #{y} are equal")
269 | else
270 | if x < y do
271 | IO.puts("#{x} is less than #{y}")
272 | else
273 | IO.puts("#{x} is greater than #{y}")
274 | end
275 | end
276 | ```
277 |
278 | The outer conditional contains two branches. The first branch contains a simple expression. The second branch contains another `if/2`, which has two branches of its own. Those two branches are both simple expressions, although they could have been conditional expressions as well.
279 |
280 | 
281 |
282 | Although the indentation of the code makes the structure apparent, nested conditionals become difficult to read very quickly. In general, it is a good idea to avoid them when you can.
283 |
284 | Logical operators often provide a way to simplify nested conditional statements. For example, we can rewrite the following code using a single conditional:
285 |
286 |
287 |
288 | ```elixir
289 | if 0 < x do
290 | if x < 10 do
291 | IO.puts("x is a positive single-digit number.")
292 | end
293 | end
294 | ```
295 |
296 | The `IO.puts` function is executed only when we pass both conditionals. We can get the same effect with the `and` operator:
297 |
298 |
299 |
300 | `````elixir
301 | if 0 < x and x < 10 do
302 | IO.puts("x is a positive single-digit number.")
303 | end
304 | ````
305 | `````
306 |
307 | ## Case: flow control through pattern matching
308 |
309 | Think of a `case` structure like a series of doors with labels. Each door has a condition written on it. When you encounter a situation, you start checking the doors one by one. As soon as you find a door with a matching condition, you open it and follow the instructions behind it. It's like solving a puzzle where you explore the doors in order until you find the right one.
310 |
311 | In programming, the `case` structure works similarly: it evaluates conditions in order and executes the code associated with the first matching condition. But what if none of the doors match?. An error is raised, "Oops, something unexpected happened!". That's where our special door labeled "Fallback" or "Catch-All" comes in. If all else fails, you automatically go to the "Fallback" door. It's like having a safety net for situations we didn't anticipate.
312 |
313 | In programming terms, that "Fallback" door corresponds to the default condition `_ -> something` in a case structure. If none of the specific conditions match, this catch-all condition handles unexpected cases and prevents errors from crashing our program.
314 |
315 |
316 |
317 | ```elixir
318 | case expression do
319 | pattern_1 -> something_1
320 | ...
321 | pattern_n -> something_n
322 |
323 | _ -> something # default condition
324 |
325 | end
326 | ```
327 |
328 | Example
329 |
330 |
331 |
332 | ```elixir
333 | case rem(value, 2) do
334 | 0 -> "The number is even."
335 | _ -> "The number is odd."
336 | end
337 | ```
338 |
339 | Play with the example in the following cell, update the value of `x`
340 |
341 | ```elixir
342 | # add different values
343 | x = 2
344 |
345 | case rem(x, 2) do
346 | 0 -> "The number is even."
347 | _ -> "The number is odd."
348 | end
349 | ```
350 |
351 | ## Catching exceptions using try and rescue
352 |
353 | In a previous section, we saw a code snippet where we used the `IO.gets` and `String.to_integer` functions to read and parse an integer number entered by the user. We also saw how error-prone this could be:
354 |
355 |
356 |
357 | ```elixir
358 | iex(1)> prompt = "What is the air velocity of an unladen swallow?\n"
359 | "What is the air velocity of an unladen swallow?\n"
360 | iex(2)> speed = IO.gets(prompt)
361 | What is the air velocity of an unladen swallow?
362 | What do you mean, an African or a European swallow?
363 | "What do you mean, an African or a European swallow?\n"
364 | iex(3)> String.to_integer(speed)
365 | ** (ArgumentError) errors were found at the given arguments:
366 |
367 | * 1st argument: not a textual representation of an integer
368 |
369 | erlang.erl:4716: :erlang.binary_to_integer("What do you mean, an African or a European swallow?\n")
370 | iex:3: (file)
371 | iex(3)>
372 | ```
373 |
374 | When we are executing these expressions in the Elixir interpreter (iex), we get a new prompt from the interpreter, think "oops", and move on to our next expression.
375 |
376 | However, if you place this code in an Elixir script or programs and this error occurs, your script or program immediately stops in its tracks with an error message. It does not execute the following expressions.
377 |
378 | Here is a sample program to convert a Fahrenheit temperature to a Celsius temperature:
379 |
380 |
381 |
382 | ```elixir
383 | input = IO.gets("Enter Fahrenheit Temperature:") |> String.trim
384 | fahr = String.to_float(input)
385 | cel = (fahr - 32.0) * 5.0 / 9.0
386 | IO.puts cel
387 |
388 | ```
389 |
390 | If we execute this code and give it invalid input, it simply fails with an unfriendly error message:
391 |
392 | ```cmd
393 | elixir fahren.exs
394 | Enter Fahrenheit Temperature:72.0
395 | 22.22222222222222
396 |
397 | elixir fahren.exs
398 | Enter Fahrenheit Temperature: 72
399 | ** (ArgumentError) argument error
400 | ```
401 |
402 | There is a conditional execution structure built into Elixir to handle these types of expected and unexpected errors called `try / rescue`. The purpose of `try` and `rescue` is that you know that some sequence of instruction(s) may have a problem and you want to add some expressions to be executed if an error occurs. These extra expressions (the rescue block) are ignored if there is no error.
403 |
404 | You can think of the `try` and `rescue` feature in Elixir as an "insurance policy" on a sequence of expressions.
405 |
406 | We can rewrite our temperature converter as follows:
407 |
408 |
409 |
410 | ```elixir
411 | inp = IO.gets("Enter Fahrenheit Temperature:") |> String.trim()
412 |
413 | try do
414 | fahr = String.to_float(inp)
415 | cel = (fahr - 32.0) * 5.0 / 9.0
416 | IO.puts(cel)
417 | rescue
418 | _error ->IO.puts("Please enter a number:\n")
419 | end
420 | ```
421 |
422 | Elixir starts by executing the sequence of expressions in the `try` block. If all goes well, it skips the `rescue` block and proceeds. If an exception occurs in the `try` block, Elixir jumps out of the `try` block and executes the sequence of expressions in the `rescue` block.
423 |
424 | ```cmd
425 | elixir fahren_with_error_handling.exs
426 | Enter Fahrenheit Temperature:72.0
427 | 22.22222222222222
428 |
429 | elixir fahren_with_error_handling.exs
430 | Enter Fahrenheit Temperature:fred
431 | Please enter a number:
432 | ```
433 |
434 | Handling an exception with a `try` block is called catching an exception. In this example, the `rescue` clause prints an error message. In general, catching an exception gives you a chance to fix the problem, or try again, or at least end the program gracefully.
435 |
436 | To learn more about [exceptions](https://hexdocs.pm/elixir/try-catch-and-rescue.html)
437 |
438 | ## Short-circuit evaluation of logical expressions
439 |
440 | When Elixir is processing a logical expression such as `x >= 2 && (x/y) > 2`, it evaluates the expression from `left to right`. Because of the definition of `&&`, if `x` is less than `2`, the expression `x >= 2` is `False` and so the whole expression is `False` regardless of whether `(x/y) > 2` evaluates to `True` or `False`.
441 |
442 | When Elixir detects that there is nothing to be gained by evaluating the rest of a logical expression, it stops its evaluation and does not do the computations in the rest of the logical expression. When the evaluation of a logical expression stops because the overall value is already known, it is called `short-circuiting` the evaluation.
443 |
444 | While this may seem like a fine point, the short-circuit behavior leads to a clever technique called the guardian pattern. Consider the following code sequence in the Elixir.
445 |
446 |
447 |
448 | ```elixir
449 | iex(1)> x = 6
450 | 6
451 | iex(2)> y = 2
452 | 2
453 | iex(3)> x >= 2 && (x/y) > 2
454 | true
455 | iex(4)> x = 1
456 | 1
457 | iex(5)> y = 0
458 | 0
459 | iex(6)> x >= 2 && (x/y) > 2
460 | false
461 | iex(7)> x = 6
462 | 6
463 | iex(8)> y = 0
464 | 0
465 | iex(9)> x >= 2 && (x/y) > 2
466 | ** (ArithmeticError) bad argument in arithmetic expression: 6 / 0
467 | :erlang./(6, 0)
468 | iex:9: (file)
469 | ```
470 |
471 | The third calculation failed because Elixir was evaluating `(x/y)` and `y` was zero, which causes a runtime error. But the first and the second examples did not fail because in the first calculation `y` was non zero and in the second one the first part of these expressions `x >= 2` evaluated to False so the `(x/y)` was not ever executed due to the `short-circuit` rule and there was no error.
472 |
473 | We can construct the logical expression to strategically place a `guard` evaluation just before the evaluation that might cause an error as follows:
474 |
475 |
476 |
477 | ```elixir
478 | iex(1)> x = 1
479 | 1
480 | iex(2)> y = 0
481 | 0
482 | iex(3)> x >= 2 && y != 0 && (x/y) > 2
483 | false
484 | iex(4)> x = 6
485 | 6
486 | iex(5)> y = 0
487 | 0
488 | iex(6)> x >= 2 && y != 0 && (x/y) > 2
489 | false
490 | iex(7)> x >= 2 && (x/y) > 2 && y != 0
491 | ** (ArithmeticError) bad argument in arithmetic expression: 6 / 0
492 | :erlang./(6, 0)
493 | iex:7: (file)
494 | ```
495 |
496 | In the first logical expression, `x >= 2` is `False` so the evaluation stops at the `&&`. In the second logical expression, `x >= 2` is `True` but `y != 0` is `False` so we never reach `(x/y)`.
497 |
498 | In the third logical expression, the `y != 0` is after the `(x/y)` calculation so the expression fails with an error.
499 |
500 | In the second expression, we say that `y != 0` acts as a guard to ensure that we only execute ``(x/y)` if `y` is non-zero.
501 |
502 | You can experiment with the examples in the followings cells.
503 |
504 | ```elixir
505 | x = 6
506 | y = 2
507 | x >= 2 && x / y > 2
508 | ```
509 |
510 | ```elixir
511 | x = 1
512 | y = 0
513 | x >= 2 && x / y > 2
514 | ```
515 |
516 | ```elixir
517 | x = 1
518 | y = 0
519 | x >= 2 && y != 0 && x / y > 2
520 | ```
521 |
522 | ```elixir
523 | x = 6
524 | y = 0
525 | x >= 2 && y != 0 && x / y > 2
526 | ```
527 |
528 | ```elixir
529 | x >= 2 && x / y > 2 && y != 0
530 | ```
531 |
532 | ## Debugging
533 |
534 | The error message Elixir displays when an error occurs contains a lot of information, but it can be overwhelming. The most useful parts are usually:
535 |
536 | * What kind of error it was, and
537 | * Where it occurred.
538 |
539 | Syntax errors are usually easy to find.
540 | For example
541 |
542 | ```
543 | iex(4)> x = 5
544 | 5
545 | iex(5)> end = 6
546 | ** (SyntaxError) invalid syntax found on iex:5:1:
547 | error: unexpected reserved word: end
548 | │
549 | 5 │ end = 6
550 | │ ^
551 | │
552 | └─ iex:5:1
553 | (iex 1.16.2) lib/iex/evaluator.ex:295: IEx.Evaluator.parse_eval_inspect/4
554 | (iex 1.16.2) lib/iex/evaluator.ex:187: IEx.Evaluator.loop/1
555 | (iex 1.16.2) lib/iex/evaluator.ex:32: IEx.Evaluator.init/5
556 | (stdlib 5.2.3) proc_lib.erl:241: :proc_lib.init_p_do_apply/3
557 | ```
558 |
559 | In this example, the problem is that the second line is using a reserved word. In general, error messages indicate where the problem was discovered, but the actual error might be earlier in the code, sometimes on a previous line.
560 |
561 | In general, error messages tell you where the problem was discovered, but that is often not where it was caused.
562 |
563 | To learn more about [debugging](https://hexdocs.pm/elixir/main/debugging.html)
564 |
565 | ## Glossary
566 |
567 | * **body**
568 | The sequence of expressions.
569 | * **boolean expression**
570 | An expression whose value is either `True` or `False`.
571 | * **branch**
572 | One of the alternative sequences of statements in a conditional statement.
573 | * **chained conditional**
574 | A conditional expression with a series of alternative branches.
575 | * **comparison operator**
576 | One of the operators that compares its operands: `===`, `==`, `!==`, `!=`, `>`, `<`, `>=`, and `<=`.
577 | * **conditional expression**
578 | An expression that controls the flow of execution depending on some condition.
579 | * **condition**
580 | The boolean expression in a conditional expression that determines which branch is executed.
581 | * **guardian pattern**
582 | Where we construct a logical expression with additional comparisons to take advantage of the short-circuit behavior.
583 | * **short-circuit operators**
584 | One of the following operators `||`, `&&`, and `!`. They first operand doesn't need to be boolean. They work with Truthy values, nil and false and treated as `flasy`, everything else is a `truthy` value.
585 | * **logical operator**
586 | One of the operators that combines boolean expressions: `and`, `or`, and `not`.
587 | * **nested conditional**
588 | A conditional expression that appears in one of the branches of another conditional expressopm.
589 | * **stack-trace**
590 | A list of the functions that are executing, printed when an exception occurs.
591 | * **short circuit**
592 | When Elixir is part-way through evaluating a logical expression and stops the evaluation because Elixir knows the final value for the expression without needing to evaluate the rest of the expression.
593 |
594 | ## Exercises
595 |
596 | * **Exercise 1**: Rewrite your pay computation to give the employee 1.5 times the hourly rate for hours worked above 40 hours.
597 |
598 | ```
599 | Enter Hours: 45
600 | Enter Rate: 10
601 | Pay: 475.0
602 | ```
603 |
604 | * **Exercise 2**: Rewrite your pay program using `try` and `rescue` so that your program handles non-numeric (only integers are handled for now) input gracefully by printing a message and exiting the program. The following shows two executions of the program:
605 |
606 | ```
607 | Enter Hours: 20
608 | Enter Rate: nine
609 | ```
610 |
611 | ---
612 |
613 | ```
614 | Error, please enter numeric input
615 | Enter Hours: forty
616 | Error, please enter numeric input
617 | ```
618 |
619 | * **Exercise 3**: Write a program to prompt for a score between `0.0` and `1.0`. If the score is out of range, print an error message. If the score is between `0.0` and `1.0`, print a grade using the following table:
620 |
621 | ```
622 | Score Grade
623 | >= 0.9 A
624 | >= 0.8 B
625 | >= 0.7 C
626 | >= 0.6 D
627 | < 0.6 F
628 | ```
629 |
630 | ---
631 |
632 | ```
633 | Enter score: 0.95
634 | A
635 | ```
636 |
637 | ---
638 |
639 | ```
640 | Enter score: perfect
641 | Bad score
642 | ```
643 |
644 | ---
645 |
646 | ```
647 | Enter score: 10.0
648 | Bad score
649 | ```
650 |
651 | ---
652 |
653 | ```
654 | Enter score: 0.75
655 | C
656 | ```
657 |
658 | ---
659 |
660 | ```
661 | Enter score: 0.5
662 | F
663 | ```
664 |
665 | Run the program repeatedly as shown above to test the various different values for input.
666 |
--------------------------------------------------------------------------------
/chapters/chapter_2.livemd:
--------------------------------------------------------------------------------
1 | # Chapter 2: Variables
2 |
3 | ```elixir
4 | Mix.install([
5 | {:kino, "~> 0.12.0"}
6 | ])
7 |
8 | import IEx.Helpers
9 | ```
10 |
11 | ## Values and types
12 |
13 | A value is one of the basic things a program works with, like a letter or a number. The values we have seen so far are `1`, `2`, and `Hello, World!`
14 |
15 | These values belong to different types: `2` is an `integer`, and `Hello, World!` is an `string`, so called because it contains a `string` of letters. You (and the interpreter/compiler) can identify strings because they are enclosed in quotation marks.
16 |
17 | ```
18 | Note: While Elixir doesn't have a specific `string` type, binaries can efficiently store
19 | and manipulate text data. They offer additional functionalities compared to traditional
20 | strings in other languages.
21 | ```
22 |
23 | ##### Printing in Elixir
24 |
25 | If you want to play in your local box, use `iex` command to start the Elixir REPL
26 |
27 | ```cmd
28 | $>iex
29 | Erlang/OTP 26 [erts-14.2.5] [source] [64-bit] [smp:20:20] [ds:20:20:10] [async-threads:1] [jit:ns]
30 |
31 | Interactive Elixir (1.16.2) - press Ctrl+C to exit (type h() ENTER for help)
32 | iex(1)>
33 | ```
34 |
35 | Unlike some languages, Elixir doesn't have a built-in print statement. However, you can achieve the same functionality using the IO (Input/Output) module.
36 |
37 |
38 |
39 | ```elixir
40 | iex(1)> IO.puts(4)
41 | 4
42 | :ok
43 | ```
44 |
45 | You can try it here
46 |
47 | ```elixir
48 | IO.puts(4)
49 | ```
50 |
51 | If you are not sure what type a value has, you can use `i` helper in IEx
52 |
53 | * i/0 - prints information about the last value
54 | * i/1 - prints information about the given term
55 |
56 | `i/n` Indicates the arity, in this case we can call `i` so it will show the information of the last value or we can call `i term` so it will show the information about `term`.
57 |
58 | A `term` refers to any data value or `expression`. As you will see, everything in Elixir is an expression.
59 |
60 |
61 |
62 | ```elixir
63 | iex(2)> a = 1
64 | 1
65 | iex(3)> i
66 | Term
67 | 1
68 | Data type
69 | Integer
70 | Reference modules
71 | Integer
72 | Implemented protocols
73 | IEx.Info, Inspect, List.Chars, String.Chars
74 | iex(4)> str = "Hello World"
75 | "Hello World"
76 | iex(5)> i
77 | Term
78 | "Hello World"
79 | Data type
80 | BitString
81 | Byte size
82 | 11
83 | Description
84 | This is a string: a UTF-8 encoded binary. It's printed surrounded by
85 | "double quotes" because all UTF-8 encoded code points in it are printable.
86 | Raw representation
87 | <<72, 101, 108, 108, 111, 32, 87, 111, 114, 108, 100>>
88 | Reference modules
89 | String, :binary
90 | Implemented protocols
91 | Collectable, IEx.Info, Inspect, List.Chars, String.Chars
92 | ```
93 |
94 | You can also use one of the following built-in type-check functions defined in the [Kernel](https://hexdocs.pm/elixir/Kernel.html) module; they return true if their argument (term) matches the specified type
95 |
96 | `is_atom` `is_binary` `is_bitstring` `is_boolean` `is_exception` `is_float`
97 | `is_function` `is_integer` `is_list` `is_map` `is_number` `is_pid`
98 | `is_port` `is_record` `is_reference` `is_tuple`
99 |
100 | For example
101 |
102 |
103 |
104 | ```elixir
105 | iex(3)> is_integer(3)
106 | true
107 | iex(4)> is_integer(3.0)
108 | false
109 | ```
110 |
111 | You can try it here
112 |
113 | ```elixir
114 | a = 1
115 | i(a)
116 |
117 | is_integer(a)
118 | ```
119 |
120 | ```elixir
121 | str = "Hello World"
122 | i(str)
123 |
124 | is_bitstring(str)
125 | ```
126 |
127 | [Strings](https://hexdocs.pm/elixir/1.7.1/syntax-reference.html#strings) belong to the type `BitString` and integers belong to the type `Integer`. Less obviously, numbers with a decimal point belong to a type called `Float`, because these numbers are represented in a format called `floating point`.
128 |
129 |
130 |
131 | ```elixir
132 | iex(1)> i 3.2
133 | Term
134 | 3.2
135 | Data type
136 | Float
137 | Reference modules
138 | Float
139 | Implemented protocols
140 | IEx.Info, Inspect, List.Chars, String.Chars
141 | ```
142 |
143 | ```elixir
144 | i(3.2)
145 |
146 | is_float(3.2)
147 | ```
148 |
149 | What about values like `"17"` and `"3.2"`? They look like numbers, but they are in quotation marks like strings.
150 |
151 | ```elixir
152 | i("17")
153 | is_integer("17")
154 | ```
155 |
156 | ```elixir
157 | i("3.2")
158 | is_float("3.2")
159 | ```
160 |
161 | They’re strings. When you type a large integer, you might be tempted to use commas between groups of three digits, as in 1,000,000. This is not a legal integer in Elixir
162 |
163 |
164 |
165 | ```elixir
166 | iex(8)> i = 1,000,000
167 | ** (SyntaxError) invalid syntax found on iex:8:6:
168 | error: syntax error before: ','
169 | │
170 | 8 │ i = 1,000,000
171 | │ ^
172 | │
173 | └─ iex:8:6
174 | (iex 1.16.2) lib/iex/evaluator.ex:295: IEx.Evaluator.parse_eval_inspect/4
175 | (iex 1.16.2) lib/iex/evaluator.ex:187: IEx.Evaluator.loop/1
176 | (iex 1.16.2) lib/iex/evaluator.ex:32: IEx.Evaluator.init/5
177 | (stdlib 5.2.3) proc_lib.erl:241: :proc_lib.init_p_do_apply/3
178 | ```
179 |
180 | But you can write
181 |
182 |
183 |
184 | ```elixir
185 | iex(8)> i = 1_000_000
186 | 1000000
187 | ```
188 |
189 | ## Variables
190 |
191 | Elixir is a functional programming language that encourages immutability and **pattern matching**. In Elixir, the `=` symbol is not used for assignment. Instead, we use pattern matching to bind values to names (variables). It means: **"Make the left side equal to the right side."**
192 |
193 | ```
194 | pattern = expression
195 | ```
196 |
197 | Let's start with a basic example:
198 |
199 |
200 |
201 | ```elixir
202 | x = 1
203 | ```
204 |
205 | This looks like an assignment, but it's actually saying:
206 | "Match `x` with `1`." Since `x` is unbound, it will match and `x` becomes `1`.
207 |
208 | Now, if we do:
209 |
210 |
211 |
212 | ```elixir
213 | x = 2
214 | ```
215 |
216 | This will rebind the variable `x`, and now `x` becomes `2`.
217 |
218 | If we want to force Elixir match the existing value of the variable in the pattern, we
219 | need to use the `pin operator`, we need to prefix the variable with `^` a caret.
220 |
221 | So now if we try to do the following
222 |
223 |
224 |
225 | ```elixir
226 | ^x = 1
227 | ```
228 |
229 | This will cause a Mathing error because we're asserting that x is already 2, and it can't be 1.
230 |
231 | Pattern matching shines when dealing with more complex structures, just a quick overview
232 |
233 |
234 |
235 | ```elixir
236 | {a, b, c} = {1, 2, 3}
237 | ```
238 |
239 | `a` becomes `1`, `b` becomes `2`, and `c` becomes `3`.
240 |
241 | You can also use pattern matching to extract values from lists:
242 | [h | t] = [1, 2, 3]
243 | `h` becomes `1`, and `t` becomes `[2, 3]`.
244 |
245 | To learn more about [Pattern Matching](https://hexdocs.pm/elixir/main/pattern-matching.html)
246 |
247 | Examples of Binding Values to Variables in Elixir:
248 |
249 |
250 |
251 | ```elixir
252 | message = "And now for something completely different"
253 | n = 17
254 | pi = 3.1415926535897931 # Note: This is an approximation of pi
255 | ```
256 |
257 | This example creates three bindings.
258 |
259 | * The first binds a string to a new variable named `message`
260 | * The second binds the integer `17` to `n`
261 | * The third binds the (approximate) value of `π` to `pi`.
262 |
263 | To display the value of a variable, you can use a `IO.puts/1` function:
264 |
265 |
266 |
267 | ```elixir
268 | iex(6)> IO.puts n
269 | 17
270 | :ok
271 | iex(7)> IO.puts message
272 | And now for something completely different
273 | :ok
274 | ```
275 |
276 | #### Playtime
277 |
278 | ```elixir
279 | # Bind a value to a variable
280 | # Display the value
281 | # Display the info of the value
282 | # Use the pin operator
283 | ```
284 |
285 | ## Variable names and keywords
286 |
287 | Programmers generally choose names for their variables that are meaningful and document what the variable is used for.
288 |
289 | [Variable Names](https://hexdocs.pm/elixir/naming-conventions.html)
290 |
291 | * Can start with lowercase letters or underscores(e.g., `user_name` `_ignored_value`).
292 | * Can start with an underscore character, but it's only used to indicate that the value of the variable should be ignored.
293 | * Can be arbitrarily long.
294 | * Can contain letters, numbers and underscores.
295 | * The underscore character `_` can appear in a name. It is often used in names with multiple words, such as `my_name` or `airspeed_of_unladen_swallow`.
296 | * Can end with the question mark (?) or exclamation mark (!) characters
297 | * Cannot start with a number or upper case.
298 |
299 | ###### Illegal names
300 |
301 |
302 |
303 | ```elixir
304 | 76trombones = "big parade"
305 | ** (SyntaxError) invalid syntax found on iex:7:1:
306 | error: invalid character "t" after number 76. If you intended to write a number, make sure to separate the number from the character (using comma, space, etc). If you meant to write a function name or a variable, note that identifiers in Elixir cannot start with numbers. Unexpected token: t
307 | │
308 | 7 │ 76trombones = "big parade"
309 | │ ^
310 | │
311 | └─ iex:7:1
312 | (iex 1.16.2) lib/iex/evaluator.ex:295: IEx.Evaluator.parse_eval_inspect/4
313 | (iex 1.16.2) lib/iex/evaluator.ex:187: IEx.Evaluator.loop/1
314 | (iex 1.16.2) lib/iex/evaluator.ex:32: IEx.Evaluator.init/5
315 | (stdlib 5.2.3) proc_lib.erl:241: :proc_lib.init_p_do_apply/3
316 | ```
317 |
318 |
319 |
320 | ```elixir
321 | iex(1)> more@ = 1000000
322 | ** (SyntaxError) invalid syntax found on iex:1:1:
323 | error: invalid character "@" (code point U+0040) in identifier: more@
324 | │
325 | 1 │ more@ = 1000000
326 | │ ^
327 | │
328 | └─ iex:1:1
329 | (iex 1.16.2) lib/iex/evaluator.ex:295: IEx.Evaluator.parse_eval_inspect/4
330 | (iex 1.16.2) lib/iex/evaluator.ex:187: IEx.Evaluator.loop/1
331 | (iex 1.16.2) lib/iex/evaluator.ex:32: IEx.Evaluator.init/5
332 | (stdlib 5.2.3) proc_lib.erl:241: :proc_lib.init_p_do_apply/3
333 | ```
334 |
335 |
336 |
337 | ```elixir
338 | iex(1)>in = "Hello Elixir"
339 | ** (SyntaxError) invalid syntax found on iex:1:4:
340 | error: syntax error before: '='
341 | │
342 | 1 │ in = "Hello Elixir"
343 | │ ^
344 | │
345 | └─ iex:1:4
346 | (iex 1.16.2) lib/iex/evaluator.ex:295: IEx.Evaluator.parse_eval_inspect/4
347 | (iex 1.16.2) lib/iex/evaluator.ex:187: IEx.Evaluator.loop/1
348 | (iex 1.16.2) lib/iex/evaluator.ex:32: IEx.Evaluator.init/5
349 | (stdlib 5.2.3) proc_lib.erl:241: :proc_lib.init_p_do_apply/3
350 | ```
351 |
352 |
353 |
354 | ```elixir
355 | iex(1)> NotValid = "Why is not valid?"
356 | ** (MatchError) no match of right hand side value: "Why is not valid?"
357 | (stdlib 5.2.3) erl_eval.erl:498: :erl_eval.expr/6
358 | iex:1: (file)
359 | ```
360 |
361 | `76trombones` is illegal because it begins with a number. `more@` is illegal because it contains an illegal character, `@`. But what's wrong with `in`?
362 |
363 | It turns out that `in` is one of Elixir's [reserverd keywords](https://hexdocs.pm/elixir/1.7.1/syntax-reference.html#reserved-words). The interpreter uses keywords to recognize the structure of the program, and they cannot be used as variable names.
364 |
365 | The last one `NotValid` is an invalid variable name because it's an `Atom` more on that later.
366 | When Atoms start with an upper case are called [`Alias`](https://hexdocs.pm/elixir/syntax-reference.html#aliases).
367 |
368 | ## Expressions
369 |
370 | In Elixir, everything is an expression that has a return value. So, there are no `statements` in the same sense as in Python. Instead, we have expressions that are evaluated and return a value.
371 |
372 | When you type an expression in the Elixir interactive shell, the shell evaluates it and displays the result, if there is one.
373 |
374 | A script usually contains a sequence of expressions. If there is more than one expression, the results appear one at a time as the expressions are evaluated.
375 |
376 | For example, the script:
377 |
378 |
379 |
380 | ```elixir
381 | IO.puts(1)
382 | x = 2
383 | IO.puts(x)
384 | ```
385 |
386 | produces the output:
387 |
388 | ```
389 | iex(1)> IO.puts(1)
390 | 1
391 | :ok
392 | iex(2)> x = 2
393 | 2
394 | iex(3)> IO.puts(x)
395 | 2
396 | :ok
397 | ```
398 |
399 | So, in Elixir, there are no statements in the same sense as in Python. Instead, we have expressions that are evaluated and return a value.
400 |
401 | ## Operators and operands
402 |
403 | Operators are special symbols that represent computations like addition and multiplication. The values the operator is applied to are called operands.
404 |
405 | The operators `+`, `-`, `*`, `/`, and `**` perform addition, subtraction, multiplication, division, and exponentiation, as in the following examples:
406 |
407 |
408 |
409 | ```elixir
410 | 20+32
411 | hour-1
412 | hour*60+minute
413 | minute/60
414 | 5**2
415 | (5+9)*(15-7)
416 | ```
417 |
418 | In Elixir the result of the divsion is a floting point result
419 |
420 |
421 |
422 | ```elixir
423 | iex(5)> 3 / 2
424 | 1.5
425 | ```
426 |
427 | To perform an integer division, use the auto-imported funcion `Kernel.div/2`
428 |
429 |
430 |
431 | ```elixir
432 |
433 | iex(6)> div(3,2)
434 | 1
435 |
436 |
437 | ##### Exercise 1: Type the following expressions in IEx or in the Next Cell to see what they do:
438 | ```
439 |
440 | ```elixir
441 | # Exercise 1: Type the following expressions: Note # is a comment.
442 | # x = 5
443 | # x + 1
444 | # IO.puts x ## Guess the value of x
445 | ```
446 |
447 | #### Note:
448 |
449 | _In Elixir many operators are functions_. So the expression `a - b`, can be rewritten
450 | as `Kernel.-(a,b)`
451 | To learn more in the following cell or IEx type `h Kernel.-`
452 |
453 | ```elixir
454 | # Learn about Operators, many of them are actually functions.
455 | # Uncomment the following line and Evaluate it. Spend time to learn more about it.
456 | # h Kernel.-
457 | ```
458 |
459 | ## Order of operations
460 |
461 | When more than one operator appears in an expression, the order of evaluation depends on the `rules of precedence`. Elixir follows mathematical convention for mathematical operators. The acronym `PEMDAS` is a useful way to remember the rules:
462 |
463 | * Parentheses have the highest precedence and can be used to force an expression to evaluate in the order you want. For example, `2 * (3-1) is 4`, and `(1+1)**(5-2) is 8`. Parentheses can also be used to make an expression easier to read, as in `(minute * 100) / 60`, even if it doesn't change the result.
464 |
465 | * Exponentiation has the next highest precedence, so `2**1+1 is 3`, not `4`, and `3*1**3 is 3`, not `27`.
466 |
467 | * Multiplication and Division have the same precedence, which is higher than Addition and Subtraction, which also have the same precedence. So `2*3-1 is 5`, not `4`, and `6+4/2` is `8.0`, not `5`.
468 |
469 | * Operators with the same precedence are evaluated from left to right. So the expression `5-3-1 is 1`, not `3`, because the `5-3` happens first and then `1` is subtracted from `2`.
470 |
471 | When in doubt, it's always a good idea to use parentheses in your expressions to make sure the computations are performed in the order you intend.
472 |
473 | To learn more read the [Elixir Operators Reference](https://hexdocs.pm/elixir/operators.html)
474 |
475 | ## Modulus operator
476 |
477 | The modulus operator works on integers and yields the remainder when the first operand is divided by the second. In Elixir, the modulus operator is the `rem` function. The syntax is `rem(dividend, divisor)`.
478 |
479 |
480 |
481 | ```elixir
482 | iex(1)> quotient = div(7, 3)
483 | 2
484 |
485 | iex(2)> remainder = rem(7, 3)
486 | 1
487 | ```
488 |
489 | So 7 divided by 3 is 2 with 1 left over.
490 |
491 | The modulus operator turns out to be surprisingly useful. For example, you can check whether one number is divisible by another: if `rem(x, y)` is zero, then `x` is divisible by `y`.
492 |
493 | You can also extract the right-most digit or digits from a number. For example, `rem(x, 10)` yields the right-most digit of x (in base 10). Similarly, `rem(x, 100)` yields the last two digits.
494 |
495 | ```elixir
496 |
497 | ```
498 |
499 | ## String operations
500 |
501 | In Elixir, we work with strings as UTF-8 encoded binaries. Here are some key points about string operations:
502 |
503 | * **Concatenation**
504 |
505 | * To concatenate two strings, we use the `<>` operator
506 | * Example
507 | ```elixir
508 | iex(1)> first = "hello"
509 | "hello"
510 | iex(2)> second = "world"
511 | "world"
512 | iex(3)> result = first <> " " <> second
513 | "hello world"
514 | iex(4)> IO.puts(result) # Output: "hello world"
515 | hello world
516 | :ok
517 | ```
518 |
519 | * **Interpolation**
520 |
521 | * Elixir strings support interpolation using the `#{}` syntax.
522 | * You can place expressions inside a string using interpolation
523 | * Example
524 | ```elixir
525 | iex(1)> name = "joe"
526 | "joe"
527 | iex(2)> greeting = "Hello #{name}"
528 | "Hello joe"
529 | iex(3)> result = "The value is #{10 * 5}"
530 | "The value is 50"
531 | ```
532 |
533 | Remember that Elixir strings are UTF-8 encoded binaries, and we can perform various operations on them.
534 |
535 | Additional helper functions for working with strings are available in the [String module](https://hexdocs.pm/elixir/String.html)
536 |
537 | ## Asking the user for input
538 |
539 | Sometimes we would like to take the value for a variable from the user via their keyboard. In Elixir we can use the function `IO.get/1`. When this function is called, the program stops and waits for the user to type something. When the user presses Return or Enter, the program resumes and input returns what the user typed as a string.
540 |
541 |
542 |
543 | ```elixir
544 | iex(1)> inp = IO.gets("")
545 | Some silly stuff
546 | "Some silly stuff\n"
547 | iex(2)> IO.puts inp
548 | Some silly stuff
549 |
550 | :ok
551 | ```
552 |
553 | Before getting input from the user, it is a good idea to print a prompt telling the user what to input. You can pass a string to input to be displayed to the user before pausing for input:
554 |
555 |
556 |
557 | ```elixir
558 | iex(38)> name = IO.gets("What is your name?\n")
559 | What is your name?
560 | Chuck
561 | "Chuck\n"
562 | iex(39)> IO.puts(name)
563 | Chuck
564 |
565 | :ok
566 | ```
567 |
568 | The sequence `\n` at the end of the prompt represents a `newline`, which is a special character that causes a line break. That's why the user's input appears below the prompt.
569 | When we get the input from the user we have that sequence at the end. If we want to remove it we can use `String.trim_trailing/1, String.trim_trailing/2 or String.trim/1, String.trim_trim/2`
570 |
571 |
572 |
573 | ```elixir
574 | iex(47)> String.trim_trailing("Hello\n")
575 | "Hello"
576 | iex(48)> String.trim_trailing("Hello\n", "\n")
577 | "Hello"
578 | iex(49)> String.trim("Hello\n")
579 | "Hello"
580 | iex(50)> String.trim("Hello\n", "\n")
581 | "Hello"
582 | ```
583 |
584 |
585 |
586 | #### Exercises: Explore the modules documentation using IEx
587 |
588 | ```elixir
589 | # Uncomment the following lines and explore the documentation for the following functions.
590 |
591 | h(IO.gets())
592 | # h String.trim/1
593 | # h String.trim/2
594 | # h String.trim_trailing/1
595 | # h String.trim_trailing/2
596 | ```
597 |
598 | If you expect the user to type an integer, you can try to convert the return value to integer, we can use the function `String.trim` to remove any leading and trailing whitespace from the string, including the newline character at the end of the user's input.
599 | Then we can call the function `String.to_integer` that convert the string into an integer.
600 |
601 |
602 |
603 | ```elixir
604 | iex(1)> prompt = IO.gets("What...is the airspeed velocity of an unladen swallow?\n")
605 | What...is the airspeed velocity of an unladen swallow?
606 | 17
607 | "17\n"
608 | iex(2)> speed_string = String.trim(prompt)
609 | "17"
610 | iex(3)> speed = String.to_integer(speed_string)
611 | 17
612 | ```
613 |
614 | But if the user types something other than a string of digits, you get an error:
615 |
616 |
617 |
618 | ```elixir
619 | iex(4)> prompt = IO.gets("What...is the airspeed velocity of an unladen swallow?\n")
620 | What...is the airspeed velocity of an unladen swallow?
621 | 17s
622 | "17s\n"
623 | iex(5)> speed_string = String.trim(prompt)
624 | "17s"
625 | iex(6)> speed = String.to_integer(speed_string)
626 | ** (ArgumentError) errors were found at the given arguments:
627 |
628 | * 1st argument: not a textual representation of an integer
629 |
630 | erlang.erl:4719: :erlang.binary_to_integer("17s")
631 | iex:10: (file)
632 | ```
633 |
634 | We will see how to handle this kind of error later.
635 |
636 | Before we continue let's rewrite this code in more idiomatic Elixir way.
637 |
638 |
639 |
640 | ```elixir
641 | speed = IO.gets("What...is the airspeed velocity of an unladen swallow?\n")
642 | |> String.trim()
643 | |> String.to_integer()
644 | ```
645 |
646 | In this solution we use the pipe operator `(|>)` to pass the result of one function into the next. This can make the code more readable and elegant.
647 |
648 | ## Comments
649 |
650 | As programs get bigger and more complicated, they get more difficult to read. Formal languages are dense, and it is often difficult to look at a piece of code and figure out what it is doing, or why.
651 |
652 | For this reason, it is a good idea to add notes to your programs to explain in natural language what the program is doing. These notes are called comments, and in Elixir they start with the `#` symbol and run to the end of the line.
653 |
654 |
655 |
656 | ```elixir
657 | # compute the percentage of the hour that has elapsed
658 | percentage = (minute * 100) / 60
659 | ```
660 |
661 | In this case, the comment appears on a line by itself. You can also put comments at the end of a line:
662 |
663 |
664 |
665 | ```elixir
666 | percentage = (minute * 100) / 60 # percentage of an hour
667 | ```
668 |
669 | Everything from the # to the end of the line is ignored; it has no effect on the program.
670 |
671 | Comments are most useful when they document non-obvious features of the code. It is reasonable to assume that the reader can figure out what the code does; it is much more useful to explain why.
672 |
673 | This comment is redundant with the code and useless
674 |
675 |
676 |
677 | ```elixir
678 | v = 5 # assign 5 to v
679 | ```
680 |
681 | This comment contains useful information that is not in the code:
682 |
683 |
684 |
685 | ```elixir
686 | v = 5 # velocity in meters/second.
687 | ```
688 |
689 | Good variable names can reduce the need for comments, but long names can make complex expressions hard to read, so there is a trade-off.
690 |
691 | ## Choosing mnemonic variable names
692 |
693 | As long as you follow the simple rules of variable naming, and avoid reserved words, you have a lot of choice when you name your variables. In the beginning, this choice can be confusing both when you read a program and when you write your own programs. For example, the following three programs are identical in terms of what they accomplish, but very different when you read them and try to understand them.
694 |
695 |
696 |
697 | ```elixir
698 | a = 35.0
699 | b = 12.50
700 | c = a * b
701 | IO.puts c
702 | ```
703 |
704 | ---
705 |
706 |
707 |
708 | ```elixir
709 | hours = 35.0
710 | rate = 12.50
711 | pay = hours * rate
712 | IO.puts pay
713 | ```
714 |
715 | ---
716 |
717 |
718 |
719 | ```elixir
720 | x1q3z9ahd = 35.0
721 | x1q3z9afd = 12.50
722 | x1q3p9afd = x1q3z9ahd * x1q3z9afd
723 | IO.puts x1q3p9afd
724 | ```
725 |
726 | The Elixir compiler sees all three of these programs as exactly the same but humans see and understand these programs quite differently. Humans will most quickly understand the intent of the second program because the programmer has chosen variable names that reflect their intent regarding what data will be stored in each variable.
727 |
728 | We call these wisely chosen variable names `mnemonic variable names`. The word `mnemonic` means `memory aid`. We choose mnemonic variable names to help us remember why we created the variable in the first place.
729 |
730 | While this all sounds great, and it is a very good idea to use mnemonic variable names, mnemonic variable names can get in the way of a beginning programmer's ability to parse and understand code. This is because beginning programmers have not yet memorized the reserved words and sometimes variables with names that are too descriptive start to look like part of the language and not just well-chosen variable names.
731 |
732 | Take a quick look at the following Elixir sample code which loops through some data. We will cover loops soon, but for now try to just puzzle through what this means:
733 |
734 |
735 |
736 | ```elixir
737 | Enum.each(words, fn(word) -> IO.puts(word) end)
738 | ```
739 |
740 | What is happening here? Which of the tokens (fn, word, end, etc.) are reserved words, macros, modules and which are just variable names? Does Elixir understand at a fundamental level the notion of words? Beginning programmers have trouble separating what parts of the code must be the same as this example and what parts of the code are simply choices made by the programmer.
741 |
742 | For beginners, it's essential to recognize which parts of the code are defined by the language and which are choices made by the programmer. As you become more familiar with Elixir's reserved words,macros and modules, you'll be able to read and write code more intuitively.
743 |
744 | ## Debugging
745 |
746 | At this point, the syntax error you are most likely to make is an illegal variable name, like using reserved keyword or macros like `in` and `end`, or `odd~job` and `US$`, which contain illegal characters.
747 |
748 | If you put a space in a variable name, Elixir thinks the first one is a function and it's undefined.
749 |
750 |
751 |
752 | ```elixir
753 | iex(1)> bad name = 5
754 | error: undefined function bad/1 (there is no such import)
755 | └─ iex:19
756 |
757 | ** (CompileError) cannot compile code (errors have been logged)
758 | ```
759 |
760 | In Elixir the compiler does a great job describing the error message. For example in the following syntax error, the compiler is very informative
761 |
762 |
763 |
764 | ```elixir
765 | iex(1)> end = 2
766 | ** (SyntaxError) invalid syntax found on iex:19:1:
767 | error: unexpected reserved word: end
768 | │
769 | 19 │ end = 2
770 | │ ^
771 | │
772 | └─ iex:19:1
773 | (iex 1.16.2) lib/iex/evaluator.ex:295: IEx.Evaluator.parse_eval_inspect/4
774 | (iex 1.16.2) lib/iex/evaluator.ex:187: IEx.Evaluator.loop/1
775 | (iex 1.16.2) lib/iex/evaluator.ex:32: IEx.Evaluator.init/5
776 | (stdlib 5.2.3) proc_lib.erl:241: :proc_lib.init_p_do_apply/3
777 | ```
778 |
779 | The runtime error you are most likely to make is a `use before def;` that is, trying to use a variable before you have assigned a value. This can happen if you spell a variable name wrong:
780 |
781 |
782 |
783 | `````elixir
784 | iex(1)> principal = 327.68
785 | 327.68
786 | iex(2)> interest = principle * rate
787 | error: undefined variable "principle"
788 | └─ iex:2
789 |
790 | ** (CompileError) cannot compile code (errors have been logged)
791 |
792 | ````
793 | At this point, the most likely cause of a semantic error is the order of operations. For example, to evaluate `1/2π`, you might be tempted to write
794 |
795 | `````
796 |
797 | 1.0 / 2.0 * :math.pi
798 |
799 | ```
800 | But the division happens first, so you would get `π/2`, which is not the same thing! There is no way for Elixir to know what you meant to write, so in this case you don't get an error message; you just get the wrong answer.
801 | ```
802 |
803 | ## Glossary
804 |
805 | * **bind / re-bind**
806 | An expression that assing(binds)/reassign(rebind) a value to a variable.
807 | * **concatenate**
808 | To join two operands end to end.
809 | * **comment**
810 | Information in a program that is meant for other programmers (or anyone reading the source code) and has no effect on the execution of the program.
811 | * **evaluate**
812 | To simplify an expression by performing the operations in order to yield a single value.
813 | * **expression**
814 | A combination of variables, operators, and values that represents a single result value.
815 | * **floating point**
816 | A type that represents numbers with fractional parts.
817 | * **integer**
818 | A type that represents whole numbers.
819 | * **keyword**
820 | A reserved word that is used by the compiler to parse a program; you cannot use keywords like in, end, as variable names.
821 | * **mnemonic**
822 | A memory aid. We often give variables mnemonic names to help us remember what is stored in the variable.
823 | * **modulus operator**
824 | An operator,`rem`, that works on integers and yields the remainder when one number is divided by another.
825 | * **operand**
826 | One of the values on which an operator operates.
827 | * **operator**
828 | A special symbol that represents a simple computation like addition, multiplication, or string concatenation.
829 | * **rules of precedence**
830 | The set of rules governing the order in which expressions involving multiple operators and operands are evaluated.
831 | * **string**
832 | A type that represents sequences of characters.
833 | * **type**
834 | A category of values. The types we have seen so far are integers (type int), floating-point numbers (type Integers), and strings (type BitString).
835 | * **value**
836 | One of the basic units of data, like a number or string, that a program manipulates.
837 | * **variable**
838 | A name that refers to a value.
839 |
840 | ## Exercises
841 |
842 | ##### Exercise 2:
843 |
844 | Write a program that uses `IO.gets` to prompt a user for their name and then
845 | welcomes them.
846 |
847 | ```cmd
848 | Enter your name: Chuck
849 | Hello Chuck
850 | ```
851 |
852 | ##### Exercise 3:
853 |
854 | Write a program to prompt the user for hours and rate per hour to compute gross pay.
855 |
856 | `````cmd
857 | Enter Hours: 35
858 | Enter Rate: 2.75
859 | Pay: 96.25
860 | ````
861 |
862 | We won't worry about making sure our pay has exactly two digits after the decimal place for now. If you want, you can play with the `Float.round/2` function to round a floating-point number to a specified number of decimal places.
863 |
864 | #### Exercise 4
865 | Assume that we execute the following binding expressions:
866 |
867 | `````
868 |
869 | width = 17
870 | height = 12.0
871 |
872 | ```
873 | For each of the following expressions, write the value of the expression and the type (of the value of the expression).
874 |
875 | ```
876 |
877 | div(width, 2) === ?
878 |
879 | width / 2.0 === ?
880 |
881 | height / 3 === ?
882 |
883 | 1 + 2 * 5 === ?
884 |
885 | ```
886 | Check your answer in IEx or create an Elixir cell. Copy the expressions and replace the ? with the answer.
887 |
888 |
889 | #### Exercise 5:
890 |
891 | Write a program which prompts the user for a Celsius temperature, convert the temperature to Fahrenheit, and print out the converted temperature.
892 | ```
893 |
894 |
895 |
896 | #### References
897 |
898 | [1] [Syntax Reference](https://hexdocs.pm/elixir/syntax-reference.html#aliases)
899 |
--------------------------------------------------------------------------------
/chapters/chapter_1.livemd:
--------------------------------------------------------------------------------
1 |
2 |
3 | # Chapter 1: Introduction
4 |
5 | ```elixir
6 | Mix.install([
7 | {:kino, "~> 0.12.0"}
8 | ])
9 |
10 | import IEx.Helpers
11 | ```
12 |
13 | ## Why should you learn to write programs?
14 |
15 | 
16 |
17 |
18 |
19 | Writing programs (or programming) is a very creative and rewarding activity. You can write programs for many reasons, ranging from making your living to solving a difficult data analysis problem to having fun to helping someone else solve a problem. This book assumes that everyone needs to know how to program, and that once you know how to program you will figure out what you want to do with your newfound skills.
20 |
21 | We are surrounded in our daily lives with computers ranging from laptops to cell phones. We can think of these computers as our “personal assistants” who can take care of many things on our behalf. The hardware in our current-day computers is essentially built to continuously ask us the question, “What would you like me to do next?”
22 |
23 |
24 |
25 | ##### Personal Digital Assistant
26 |
27 | Programmers add an operating system and a set of applications to the hardware and we end up with a `Personal Digital Assistant` that is quite helpful and capable of helping us do many different things.
28 |
29 | Our computers are fast and have vast amounts of memory and could be very helpful to us if we only knew the language to speak to explain to the computer what we would like it to “do next”. If we knew this language, we could tell the computer to do tasks on our behalf that were repetitive. Interestingly, the kinds of things computers can do best are often the kinds of things that we humans find boring and mind-numbing.
30 |
31 | For example, look at the first three paragraphs of this chapter and tell me the most commonly used word and how many times the word is used. While you were able to read and understand the words in a few seconds, counting them is almost painful because it is not the kind of problem that human minds are designed to solve. For a computer, the opposite is true, reading and understanding text from a piece of paper is hard for a computer to do but counting the words and telling you how many times the most used word was used is very easy for the computer:
32 |
33 | ## Code example
34 |
35 | ```elixir
36 | file = Kino.Input.file("Upload your file")
37 | ```
38 |
39 | ```elixir
40 | value = Kino.Input.read(file)
41 | ```
42 |
43 | ```elixir
44 | path = Kino.Input.file_path(value.file_ref)
45 | ```
46 |
47 | ```elixir
48 | # Read the file name from the user
49 |
50 | {word, count} =
51 | File.stream!(path)
52 | |> Stream.map(&String.split(&1, ~r{\s+}, trim: true))
53 | |> Stream.filter(&(Enum.empty?(&1) == false))
54 | |> Enum.to_list()
55 | |> List.flatten()
56 | |> Enum.frequencies()
57 | |> Enum.max_by(fn {_, value} -> value end)
58 |
59 | IO.puts("The most used word is `#{word}` with #{count} appearances")
60 | ```
61 |
62 | Our “personal information analysis assistant” quickly told us that the word “to” was used sixteen times in the first three paragraphs of this chapter.
63 |
64 | This very fact that computers are good at things that humans are not is why you need to become skilled at talking “computer language”. Once you learn this new language, you can delegate mundane tasks to your partner (the computer), leaving more time for you to do the things that you are uniquely suited for. You bring creativity, intuition, and inventiveness to this partnership.
65 |
66 | ## Creativity and motivation
67 |
68 | While this book is not intended for professional programmers, professional programming can be a very rewarding job both financially and personally. Building useful, elegant, and clever programs for others to use is a very creative activity. Your computer or Personal Digital Assistant (PDA) usually contains many different programs from many different groups of programmers, each competing for your attention and interest. They try their best to meet your needs and give you a great user experience in the process. In some situations, when you choose a piece of software, the programmers are directly compensated because of your choice.
69 |
70 | If we think of programs as the creative output of groups of programmers, perhaps the following figure is a more sensible version of our PDA:
71 |
72 |
73 |
74 | 
75 |
76 |
77 |
78 | Programmers Talking to You
79 |
80 | For now, our primary motivation is not to make money or please end users, but instead for us to be more productive in handling the data and information that we will encounter in our lives. When you first start, you will be both the programmer and the end user of your programs. As you gain skill as a programmer and programming feels more creative to you, your thoughts may turn toward developing programs for others.
81 |
82 | ## Computer hardware architecture
83 |
84 | Before we start learning the language we speak to give instructions to computers to develop software, we need to learn a small amount about how computers are built. If you were to take apart your computer or cell phone and look deep inside, you would find the following parts:
85 |
86 |
87 |
88 | 
89 |
90 |
91 |
92 | Hardware Architecture
93 |
94 | The high-level definitions of these parts are as follows:
95 |
96 | * The Central Processing Unit (or CPU) is the part of the computer that is built to be obsessed with “what is next?” If your computer is rated at 3.0 Gigahertz, it means that the CPU will ask “What next?” three billion times per second. You are going to have to learn how to talk fast to keep up with the CPU.
97 |
98 | * The Main Memory is used to store information that the CPU needs in a hurry. The main memory is nearly as fast as the CPU. But the information stored in the main memory vanishes when the computer is turned off.
99 |
100 | * The Secondary Memory is also used to store information, but it is much slower than the main memory. The advantage of the secondary memory is that it can store information even when there is no power to the computer. Examples of secondary memory are disk drives or flash memory (typically found in USB sticks and portable music players).
101 |
102 | * The Input and Output Devices are simply our screen, keyboard, mouse, microphone, speaker, touchpad, etc. They are all of the ways we interact with the computer.
103 |
104 | * These days, most computers also have a Network Connection to retrieve information over a network. We can think of the network as a very slow place to store and retrieve data that might not always be “up”. So in a sense, the network is a slower and at times unreliable form of Secondary Memory.
105 |
106 | While most of the detail of how these components work is best left to computer builders, it helps to have some terminology so we can talk about these different parts as we write our programs.
107 |
108 | As a programmer, your job is to use and orchestrate each of these resources to solve the problem that you need to solve and analyze the data you get from the solution. As a programmer you will mostly be “talking” to the CPU and telling it what to do next. Sometimes you will tell the CPU to use the main memory, secondary memory, network, or the input/output devices.
109 |
110 |
111 |
112 | 
113 |
114 |
115 |
116 | Where Are You?
117 |
118 | You need to be the person who answers the CPU’s “What next?” question. But it would be very uncomfortable to shrink you down to 5mm tall and insert you into the computer just so you could issue a command three billion times per second. So instead, you must write down your instructions in advance. We call these stored instructions a program and the act of writing these instructions down and getting the instructions to be correct programming.
119 |
120 | ## Understanding programming
121 |
122 | In the rest of this book, we will try to turn you into a person who is skilled in the art of programming. In the end you will be a programmer - perhaps not a professional programmer, but at least you will have the skills to look at a data/information analysis problem and develop a program to solve the problem.
123 |
124 | In a sense, you need two skills to be a programmer:
125 |
126 | * First, you need to know the programming language (Elixir) - you need to know the vocabulary and the grammar. You need to be able to spell the words in this new language properly and know how to construct well-formed “sentences” in this new language.
127 |
128 | * Second, you need to “tell a story”. In writing a story, you combine words and sentences to convey an idea to the reader. There is a skill and art in constructing the story, and skill in story writing is improved by doing some writing and getting some feedback. In programming, our program is the “story” and the problem you are trying to solve is the “idea”.
129 |
130 | Once you learn one programming language such as Elixir, you will find it much easier to learn a second programming language such as Gleam or Python. The new programming language has very different vocabulary and grammar but the problem-solving skills will be the same across all programming languages.
131 |
132 | You will learn the “vocabulary” and “sentences” of Elixir pretty quickly. It will take longer for you to be able to write a coherent program to solve a brand-new problem. We teach programming much like we teach writing. We start reading and explaining programs, then we write simple programs, and then we write increasingly complex programs over time. At some point you “get your muse” and see the patterns on your own and can see more naturally how to take a problem and write a program that solves that problem. And once you get to that point, programming becomes a very pleasant and creative process.
133 |
134 | We start with the vocabulary and structure of Elixir programs. Be patient as the simple examples remind you of when you started reading for the first time.
135 |
136 | ## Words and sentences
137 |
138 | Elixir's vocabulary, is minimal, it has a very few reserve words. Elixir also has macros such as `def` and `defmodule` which serve a special purpose. We will treat these macros as keywords in other languages because they have a specific meaning in Elixir programs. When writing Elixir programs, you'll define your own names for variables and functions, but you must avoid using these special macros as names.
139 |
140 | Training a Pet Analogy
141 |
142 | Just as we use commands like "sit" or "fetch" to communicate with a dog, we use Elixir's macros to instruct the Elixir compiler. If we were to anthropomorphize Elixir, we might imagine it patiently waiting for us to use these macros correctly to perform actions.
143 |
144 |
145 |
146 | These are the reserved words in the Elixir language.
147 |
148 |
149 |
150 | ```elixir
151 | true, false, nil - used as atoms
152 | when, and, or, not, in - used as operators
153 | fn - used for anonymous function definitions
154 | do, end, catch, rescue, after, else - used in do-end blocks
155 | ```
156 |
157 | For a complete and official reference, you can check the Elixir syntax documentation https://hexdocs.pm/elixir/1.7.1/syntax-reference.html.
158 |
159 | Elixir heavily relies on macros for various functionalities. Here are some of the most common macros you'll encounter in Elixir programs:
160 |
161 | ### Module and Function Definition Macros:
162 |
163 | * defmodule: This macro defines a new module in your code.
164 | * def: This macro defines a function within a module.
165 |
166 | ### Control Flow Macros:
167 |
168 | * if: This macro provides conditional branching based on expressions.
169 | * case: This macro performs pattern matching for complex decision making.
170 | * cond: This macro provides a more concise way to express chained conditional expressions. It evaluates each condition in a series of clauses and executes the associated code block if the condition is true.
171 | * with: Introduced in Elixir 1.2, this macro helps with chaining multiple expressions and handling their results in a more readable way. It allows you to bind the result of one expression to a variable and use it in subsequent expressions within the same block.
172 | * unless: This macro acts as the opposite of if. It executes the code block only if the given condition evaluates to false or nil.
173 |
174 | ### Quoting and Unquoting:
175 |
176 | * quote: This macro allows you to embed Elixir code within an expression for manipulation at compile time.
177 | * unquote: This macro injects the result of an expression back into the quoted code during compilation.
178 |
179 | ### Other Common Macros:
180 |
181 | * defp: Similar to def but defines a private function within a module.
182 | * defmacro: This macro allows you to define your own custom macros for code generation.
183 | * use: This macro imports functionalities (behaviors, modules) into your current module.
184 |
185 | That is it, and unlike a dog, Elixir is already completely trained. When you say "defmodule", Elixir will try every time you say it without fail.
186 |
187 | We will learn these reserved words, macros and how they are used in good time, but for now we will focus on the Elixir equivalent of “speak” (in human-to-dog language). The nice thing about telling Elixir to speak is that we can even tell it what to say by giving it a message in quotes:
188 |
189 | ```elixir
190 | IO.puts("Hello World!")
191 | ```
192 |
193 | And we have even written our first syntactically correct Elixir sentence. Our sentence starts with the function print followed by a string of text of our choosing enclosed in single quotes. The strings in the print statements are enclosed in quotes. Single quotes and double quotes do the same thing; most people use single quotes except in cases like this where a single quote (which is also an apostrophe) appears in the string.
194 |
195 | ## Conversing with Elixir
196 |
197 | Now that we have a word and a simple sentence that we know in Elixir, we need to know how to start a conversation with Elixir to test our new language skills.
198 |
199 | Before you can converse with Elixir, you must first install the Elixir software on your computer and learn how to start Elixir on your computer. That is too much detail for this chapter so I suggest that you consult https://elixir-lang.org/install.html where they have detailed instructions of setting up and starting Elixir on Macintosh and Windows systems. At some point, you will be in a terminal or command window and you will type `iex` and the Elixir REPL interpreter will start executing in interactive mode and appear somewhat as follows:
200 |
201 |
202 |
203 | ```dos
204 | >iex
205 | Erlang/OTP 26 [erts-14.2.5] [source] [64-bit] [smp:20:20] [ds:20:20:10] [async-threads:1] [jit:ns]
206 |
207 | Interactive Elixir (1.16.2) - press Ctrl+C to exit (type h() ENTER for help)
208 | iex(1)>
209 | ```
210 |
211 |
212 |
213 | The iex(1)> prompt is the Elixir interpreter’s way of asking you, “What do you want me to do next?” Elixir is ready to have a conversation with you. All you have to know is how to speak the Elixir language.
214 |
215 | Let’s say for example that you did not know even the simplest Elixir language words or sentences. You might want to use the standard line that astronauts use when they land on a faraway planet and try to speak with the inhabitants of the planet
216 |
217 | ```dos
218 | iex(1)> I come in peace, please take me to your leader
219 | ** (SyntaxError) invalid syntax found on iex:1:3:
220 | error: syntax error before: come
221 | │
222 | 1 │ I come in peace, please take me to your leader
223 | │ ^
224 | │
225 | └─ iex:1:3
226 | (iex 1.16.2) lib/iex/evaluator.ex:295: IEx.Evaluator.parse_eval_inspect/4
227 | (iex 1.16.2) lib/iex/evaluator.ex:187: IEx.Evaluator.loop/1
228 | (iex 1.16.2) lib/iex/evaluator.ex:32: IEx.Evaluator.init/5
229 | (stdlib 5.2.3) proc_lib.erl:241: :proc_lib.init_p_do_apply/3
230 | iex(1)>
231 | ```
232 |
233 | This is not going so well. Unless you think of something quickly, the inhabitants of the planet are likely to stab you with their spears, put you on a spit, roast you over a fire, and eat you for dinner.
234 |
235 | Luckily you brought a copy of this book on your travels, and you thumb to this very page and try again:
236 |
237 | ```dos
238 | iex(1)> IO.puts("Hello World")
239 | Hello World
240 | :ok
241 | ```
242 |
243 | This is looking much better, so you try to communicate some more:
244 |
245 | ```dos
246 | iex(1)> TO be added
247 | ```
248 |
249 | The conversation was going so well for a while and then you made the tiniest mistake using the Elixir language and Elixir brought the spears back out.
250 |
251 | At this point, you should also realize that while Elixir is amazingly complex and powerful and very picky about the syntax you use to communicate with it, Elixir is not intelligent. You are really just having a conversation with yourself, but using proper syntax.
252 |
253 | In a sense, when you use a program written by someone else the conversation is between you and those other programmers with Elixir acting as an intermediary. Elixir is a way for the creators of programs to express how the conversation is supposed to proceed. And in just a few more chapters, you will be one of those programmers using Elixir to talk to the users of your program.
254 |
255 | Before we leave our first conversation with the Elixir interpreter, you should probably know the proper way to say “good-bye” when interacting with the inhabitants of Planet Elixir:
256 |
257 | ```dos
258 | iex(6)>System.halt()
259 | ```
260 |
261 | Click Ctrl + C twice
262 |
263 | ## Terminology: Interpreter and compiler
264 |
265 | Elixir is a high-level language designed to be easily readable and writable by humans,
266 | and efficiently executable by computers. It is built on top of the Erlang virtual machine,
267 | known for running low-latency, distributed, and fault-tolerant systems.
268 | Other high-level languages include Java, C++, PHP, Ruby, Basic, Perl, JavaScript, and more.
269 | The CPU's actual hardware does not directly understand these high-level languages;
270 | instead, they are translated into machine code that the CPU can execute.
271 |
272 | The CPU understands a language we call machine language. Machine language is very simple and frankly very tiresome to write because it is represented all in zeros and ones:
273 |
274 | ```code
275 | 001010001110100100101010000001111
276 | 11100110000011101010010101101101
277 | ...
278 | ```
279 |
280 | Machine language seems quite simple on the surface, given that there are only zeros and ones, but its syntax is even more complex and far more intricate than Elixir. So very few programmers ever write machine language. Instead we build various translators to allow programmers to write in high-level languages like Elixir or Gleam and these translators convert the programs to machine language for actual execution by the CPU.
281 |
282 | Since machine language is tied to the computer hardware, machine language is not portable across different types of hardware. Programs written in high-level languages can be moved between different computers by using a different interpreter on the new machine or recompiling the code to create a machine language version of the program for the new machine.
283 |
284 | These programming language translators fall into two general categories: (1) interpreters and (2) compilers.
285 |
286 | An interpreter, such as IEx in Elixir, reads the source code written by the programmer, parses it, and executes the instructions immediately. IEx is an interactive shell that allows you to type Elixir expressions and get the results instantly. This interactive mode is particularly useful for experimenting with code and testing functions on the fly.
287 |
288 | In Elixir, when you want the interpreter to remember a value for later use, you bind it to a variable. Variables in Elixir are labels that you can use to refer to stored data. For example, you can bind a value to a variable and then retrieve or re-bind that value later in your code.
289 |
290 | ```elixir
291 | x = 6
292 | IO.puts(x)
293 | ```
294 |
295 | ```elixir
296 | y = x * 7
297 | IO.puts(y)
298 | ```
299 |
300 | In this example, we instruct Elixir to bind the value six to the variable x. We then output the value of x using IO.puts. Next, we bind the result of multiplying x by seven to the variable y, and output the value of y using IO.puts again.
301 |
302 | We entered the commands one by one, Elixir processes them as a sequence of statements, where later statements can access data defined in earlier ones. This sequence forms our first simple Elixir program, composed logically and meaningfully.
303 |
304 | The nature of an interpreter like Elixir’s IEx is to facilitate an interactive dialogue, as demonstrated. A compiler, on the other hand, requires the entire program in a file, which it then translates into machine language. The compiled machine code is stored in a file for execution.
305 |
306 | On a Windows system, executable files typically have a “.exe” or “.dll” extension, denoting “executable” and “dynamic link library,” respectively. Linux and Macintosh systems do not use a specific suffix to indicate executability.
307 |
308 | Attempting to view an executable file with a text editor would result in a jumble of characters, as the file contains machine language, not human-readable text.
309 |
310 | ```code
311 | ^?ELF^A^A^A^@^@^@^@^@^@^@^@^@^B^@^C^@^A^@^@^@\xa0\x82
312 | ^D^H4^@^@^@\x90^]^@^@^@^@^@^@4^@ ^@^G^@(^@$^@!^@^F^@
313 | ^@^@4^@^@^@4\x80^D^H4\x80^D^H\xe0^@^@^@\xe0^@^@^@^E
314 | ^@^@^@^D^@^@^@^C^@^@^@^T^A^@^@^T\x81^D^H^T\x81^D^H^S
315 | ^@^@^@^S^@^@^@^D^@^@^@^A^@^@^@^A\^D^HQVhT\x83^D^H\xe8
316 | ....
317 | ```
318 |
319 | Thankfully, we have the Elixir virtual machine (BEAM) that acts as an interpreter, allowing us to write code in a more readable language.
320 |
321 | By now, you might be curious: what language is the Elixir VM itself written in? Is it compiled or interpreted? What happens when we run iex (the Elixir interactive shell)?
322 |
323 | The BEAM virtual machine is written in a compiled language called Erlang. You can find the Erlang source code by visiting the official Erlang website and navigating to their source downloads. So, the BEAM VM itself is a program written in Erlang, compiled into machine code. When you install Elixir, you essentially copy a machine code version of the BEAM VM onto your system.
324 |
325 | ## Writing a Program
326 |
327 | While playing around in iex (Elixir's interactive shell) is a fantastic way to explore Elixir's functionalities, it's not ideal for tackling complex problems.
328 |
329 | When you want to create a simple program in Elixir, you’ll use a text editor to write Elixir instructions into a file script. By convention, Elixir scripts typically have names that end with .exs (for Elixir Script).
330 |
331 | To execute an Elixir script, you need to inform the Elixir interpreter (IEx) about the script’s filename. In your command window, you would type:
332 |
333 | ```cmd
334 | elixir hello.exs
335 | ```
336 |
337 | To write a more substantial program, we organize code in a files called a modules. Elixir modules typically have names ending with .ex.
338 |
339 | To run the module, we use the elixir command followed by the module name. For instance, to execute a module named hello.ex, you would type the following command in your terminal:
340 |
341 | ```cmd
342 | elixir hello.ex
343 | ```
344 |
345 | [See the code here](#code-example)
346 |
347 | ## What is a program?
348 |
349 | In Elixir, a program is a sequence of statements crafted to achieve specific goals. Even our simplest Elixir script,our hello.exs, qualifies as a program. Although concise, it serves a purpose—albeit not particularly useful in this case.
350 |
351 | #### Programs Solving Problems:
352 |
353 | Understanding programs often comes easier when we consider the problems they solve. Let's take a scenario relevant to Elixir: analyzing text data. Imagine you're researching the most frequent word in a collection of tweets. Manually sifting through the data would be time-consuming and error-prone. Instead, an Elixir program can analyze the text efficiently and accurately, freeing up your time for more engaging tasks.
354 |
355 | ##### Example: Finding the Most Frequent Word:
356 |
357 | Consider the following text about a dog and its walk:
358 |
359 | ```
360 | text = "The happy dog went for a long walk in the park with its owner. The dog chased a frisbee and had a great time!"
361 | ```
362 |
363 | Imagine you're faced with the daunting task of analyzing millions of lines of text. Scanning each word manually would be incredibly time-consuming. In fact, it would likely be faster for you to learn Elixir and write a program to count the words automatically.
364 |
365 | We can write an Elixir program to find the most frequent word in this text. This will help us understand how to analyze textual data using Elixir.
366 |
367 | ## The building blocks of programs
368 |
369 | In the following sections, we'll delve into the core concepts of Elixir programming. We'll explore functions, modules, pattern matching, and processes, the building blocks used to construct powerful Elixir applications. These concepts are fundamental to most programming languages, not just Elixir.
370 |
371 | ##### Fundamental Patterns:
372 |
373 | While specific syntax might differ, core programming patterns exist across languages. Let's explore these patterns in the context of Elixir:
374 |
375 | * **Input**:
376 | Retrieve data from external sources. This could involve reading information from a file, receiving data through network requests, or interacting with user input.
377 | * **Output**:
378 | Present the program's results. This could involve displaying data on the console, writing it to a file, or sending it to external devices.
379 | * **Sequential Execution**:
380 | Execute code statements one after another in the order they appear in the module file.
381 | * **Conditional Execution**:
382 | Evaluate conditions and execute specific code blocks based on the outcome (true or false).
383 | * **Repeated Execution**:
384 | Employ loops or recursion to perform a set of instructions repeatedly, often with variations in each iteration.
385 | * **Function Reuse**:
386 | Define functions that encapsulate specific functionalities. These functions can be called and reused throughout your program for modularity and code organization.
387 | * **Pattern Matching**: Elixir is a functional language that heavily relies on pattern matching. This allows you to compare data structures and extract specific values based on their structure. It's a powerful tool for data manipulation and control flow.
388 | * **High-Order Functions**: Elixir functions can accept other functions as arguments and return functions as results. This enables powerful abstractions and allows you to write concise and reusable code.
389 |
390 | These foundational patterns form the cornerstone of Elixir programming. While seemingly simple, the true power lies in creatively combining these concepts to build robust and efficient applications.
391 |
392 | ## What could possibly go wrong?
393 |
394 | In our initial interactions with Elixir, we discover that precision matters when writing Elixir code. Even the slightest deviation or mistake can lead Elixir to abandon your program.
395 |
396 | Novice programmers sometimes interpret Elixi's intolerance for errors as a personal vendetta. While Elixir appears friendly to everyone else, it seems to harbor a grudge against them. It's as if Elixir deliberately deems their meticulously crafted programs as `unfit`, just to play tricks on them.
397 |
398 | But fear not! Elixir's strictness serves a purpose—it encourages clarity and correctness. As you continue your journey, remember that Elixir's demands are not personal; they're simply part of the language's commitment to quality.
399 |
400 |
401 |
402 | ```elixir
403 | # Attempting to use an undefined function
404 | iex(1)> IO.print('Hello world!')
405 | ** (UndefinedFunctionError) function IO.print/1 is undefined or private
406 | (elixir 1.16.2) IO.print(42)
407 | iex:3: (file)
408 |
409 | # Calling a defined function with incorrect arguments
410 | iex(2)> IO.puts(42, "This won't work")
411 | ** (FunctionClauseError) no function clause matching in IO.puts/2
412 |
413 | The following arguments were given to IO.puts/2:
414 |
415 | # 1
416 | 42
417 |
418 | # 2
419 | "This won't work"
420 |
421 | Attempted function clauses (showing 1 out of 1):
422 |
423 | def puts(device, item) when is_atom(device) or is_pid(device)
424 |
425 | (elixir 1.16.2) lib/io.ex:295: IO.puts/2
426 | iex:3: (file)
427 |
428 | ```
429 |
430 | ## Debugging
431 |
432 | When your Elixir code throws an error or produces unexpected results, it's time to enter debug mode. Debugging is the process of identifying and resolving the root cause of the issue. Here are four key strategies to tackle those pesky bugs:
433 |
434 | * **Scrutinize Your Code**: Take a deep dive into your code. Read it line by line, ensuring it aligns with your intended functionality. Look for typos, syntax errors, or logical inconsistencies.
435 |
436 | * **Experiment with Modifications**: Make incremental changes and test them. Often, strategically placed `IO.inspect` statements can reveal the problem. In complex scenarios, you might need to build temporary helper functions to isolate the issue.
437 |
438 | * **Contemplate and Analyze**: Dedicate time to think. What kind of error are you facing? Is it a syntax error, a runtime error, or a semantic issue? Analyze error messages and program output for clues. Consider what errors could lead to the observed behavior. Remember recent changes that might have introduced the bug.
439 |
440 | * **Strategic Retreat**: Sometimes, the best approach is to step back. Revert recent changes until you reach a working and well-understood state. Then, you can rebuild incrementally, ensuring each step functions correctly.
441 |
442 | ##### Avoiding Debugging Pitfalls:
443 |
444 | * **Focusing on One Strategy**: Beginner programmers often get fixated on a single approach, neglecting the others. Effective debugging utilizes all four techniques: reading, running, thinking, and sometimes retreating.
445 |
446 | * **Reading Limitations**: Rereading code helps with typos, but not with conceptual errors. If the logic itself is flawed, you can reread endlessly.
447 |
448 | * **Random Walk Programming**: Running experiments without a plan leads to "random walk programming" - making random changes hoping for a fix. This is a time-consuming and inefficient approach.
449 |
450 | * **Importance of Thinking**: Debugging is similar to scientific exploration. Form a hypothesis about the cause of the error. If multiple possibilities exist, design tests to eliminate one at a time.
451 |
452 | * **The Power of Breaks and Discussion**: Taking a break or explaining the problem to someone (even yourself) can spark insights. Often, the act of explaining clarifies the issue.
453 |
454 | ##### Knowing When to Retreat:
455 |
456 | Advanced debugging techniques become ineffective with excessive errors or overly complex code. Sometimes, simplification is key. Retreat by systematically removing code until you reach a functional and understandable state. Rebuild upon this solid foundation.
457 |
458 | ###### Beginner's Reluctance to Retreat:
459 |
460 | New programmers often resist retreating, hesitant to delete code (even if incorrect). To ease the process, consider copying your code into a separate file before simplification. This allows you to paste sections back incrementally as you debug.
461 |
462 | By mastering these debugging strategies, you'll be well-equipped to tackle errors effectively and become a confident Elixir developer.
463 |
464 | ## The learning journey
465 |
466 | Don't be discouraged if Elixir concepts seem disjointed at first. Learning a new language, even a programming one, takes time. Imagine a child babbling before forming sentences, then paragraphs, and finally, crafting stories. We aim to accelerate your Elixir journey, but similar to language acquisition, true understanding takes time.
467 |
468 | This book introduces Elixir concepts progressively, but feel free to explore non-linearly. Peek ahead for context, and revisit earlier sections for deeper comprehension. Skimming advanced topics can spark curiosity about the "why" behind programming. Revising past material reinforces your learning, even if newer concepts seem challenging.
469 |
470 | In your Elixir learning journey, expect "Aha!" moments. You'll step back from the code and see the beautiful structure emerging. If something feels particularly difficult, take a break, refresh your mind, and come back with new perspective. Explaining your problem to someone (even a pet!) can often lead to breakthroughs.
471 |
472 | Trust the process. Once you grasp Elixir concepts, you'll realize it was all elegant and well-designed, just waiting for you to absorb it.
473 |
474 | ## Key Points in Chapter 1
475 |
476 | * **Introduction to Programming**: The chapter begins with a discussion on the importance of learning to program, emphasizing it as a creative and rewarding activity that can solve repetitive tasks and handle data analysis problems.
477 | * **Personal Digital Assistants**: It introduces the concept of computers as personal assistants, capable of performing tasks on our behalf if we know the language to communicate with them.
478 | * **Basics of Elixir**: The chapter provides an introduction to Elixir, including setting up the environment, basic syntax, reserved words, and macros. It also touches on the interactive Elixir shell (IEx) and the process of writing and running Elixir programs.
479 | * **Programming Concepts**: Fundamental programming concepts such as input/output devices, sequential and conditional execution, and debugging strategies are explained, setting the stage for more advanced programming topics in subsequent chapters.
480 |
481 | ## Glossary
482 |
483 | * **Bug**: An error in an Elixir program that causes unexpected behavior or incorrect results. Debugging involves identifying and fixing these issues.
484 | * **Central Processing Unit (CPU)**: The core component of a computer responsible for executing instructions. It runs the software written by programmers.
485 | * **Compile**: The process of translating an Elixir program (written in a high-level language) into machine code (a low-level language) all at once. Compilation prepares the program for later execution.
486 | * **High-Level Language**: A programming language like Elixir that is designed to be easy for humans to read and write. It abstracts away low-level details and provides expressive syntax.
487 | * **Interactive Mode**: In Elixir, this refers to using the interactive shell (IEx) by typing commands and expressions directly at the prompt. It allows experimentation and quick feedback.
488 | * **Interpret**: To execute an Elixir program by translating it one line at a time. Elixir’s interpreter (IEx) processes code interactively.
489 | * **Low-Level Language**: Also known as machine code or assembly language, it is a programming language designed for efficient execution by the computer. It is not human-readable.
490 | * **Machine Code**: The lowest-level language for software, directly executed by the CPU. It consists of binary instructions.
491 | * **Main Memory**: Stores programs and data while the computer is running. Main memory loses its information when the power is turned off (volatile memory).
492 | * **Parse**: The process of examining an Elixir program and analyzing its syntactic structure. Parsing ensures that the code adheres to the language rules.
493 | * **Portability**: A property of an Elixir program that allows it to run on different kinds of computers without modification. Elixir’s portability is enhanced by its compatibility with the Erlang virtual machine (BEAM).
494 | * **Print Function**: An instruction in Elixir (using IO.puts or similar) that displays a value on the screen. It helps with debugging and output.
495 | * **Problem Solving**: The process of formulating a problem, finding a solution, and expressing that solution in code. Effective problem-solving is essential for writing good programs.
496 | * **Program**: A set of instructions written in Elixir (or any other language) that specifies a computation. Programs solve specific tasks or problems.
497 | * **Prompt**: When an Elixir program displays a message and waits for user input. The user can type responses or commands at the prompt.
498 | * **Secondary Memory**: Stores programs and data even when the power is turned off (non-volatile memory). Examples include disk drives and flash memory (e.g., USB sticks). It is generally slower than main memory.
499 | * **Semantics**: Refers to the meaning of an Elixir program—the behavior it exhibits when executed. Understanding semantics is crucial for writing correct programs.
500 | * **Semantic Error**: An error in an Elixir program that causes it to behave differently from the programmer’s intention. These errors are often subtle and require careful debugging.
501 | * **Source Code**: The human-readable representation of an Elixir program. It consists of Elixir code written in files with .ex or .exs extensions.
502 |
503 | ## Exercises
504 |
505 | ##### Exercise 1: What is the function of the secondary memory in a computer?
506 |
507 | * a) Execute all of the computation and logic of the program
508 | * b) Retrieve web pages over the Internet
509 | * c) Store information for the long term, even beyond a power cycle
510 | * d) Take input from the user
511 |
512 | ##### Exercise 2: What is a program?
513 |
514 | ##### Exercise 3: What is the difference between a compiler and an interpreter?
515 |
516 | ##### Exercise 4: Which of the following contains “machine code”?
517 |
518 | * a) The IEx interpreter
519 | * b) The keyboard
520 | * c) Elixir source file
521 | * d) A word processing document
522 |
523 | ##### Exercise 5: What is wrong with the following code:
524 |
525 |
526 |
527 | ```elixir
528 | iex(1)> IO.put "Hello Elixir"
529 | ** (UndefinedFunctionError) function IO.put/1 is undefined or private. Did you mean:
530 |
531 | * puts/1
532 | * puts/2
533 |
534 | (elixir 1.16.2) IO.put("Hello Elixir")
535 | iex:1: (file)
536 | ```
537 |
538 | ##### Exercise 6: Where in the computer is a variable such as “x” stored after the following Elixir line finishes?
539 |
540 |
541 |
542 | ```elixir
543 | x = 123
544 | ```
545 |
546 | * a) Central processing unit
547 | * b) Main Memory
548 | * c) Secondary Memory
549 | * d) Input Devices
550 | * e) Output Devices
551 |
552 | ##### Exercise 7: What will the following program print out:
553 |
554 | ```Elixir
555 | x = 43
556 | x = x - 1
557 | IO.puts(x)
558 | ```
559 |
560 | * a) 43
561 | * b) 42
562 | * c) x + 1
563 | * d) Error because x = x + 1 is not possible mathematically
564 |
565 | ### Exercise 8: Explain each of the following using an example of a human capability:
566 |
567 | * (1) Central processing unit
568 | * (2) Main Memory
569 | * (3) Secondary Memory
570 | * (4) Input Device
571 | * (5) Output Device.
572 |
573 | For example, “What is the human equivalent to a Central Processing Unit”?
574 |
--------------------------------------------------------------------------------