├── 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 | Produced by OmniGraffle 6.5 2016-04-08 10:48:19 +0000Canvas 1Layer 1x > 0IO.puts(‘x is postitive’)Yes 4 | -------------------------------------------------------------------------------- /chapters/files/if-else.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | Produced by OmniGraffle 6.5 2016-04-08 10:48:40 +0000Canvas 1Layer 1rem(x,2)==0IO.puts("#{x} is even")YesIO.puts("#{x} is odd")No 4 | -------------------------------------------------------------------------------- /chapters/files/elsif.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | Produced by OmniGraffle 6.5 2016-04-08 10:49:53 +0000Canvas 1Layer 1x < yprint(‘less’)Yesx > yprint (‘greater’)Yesprint(‘equal’) 4 | -------------------------------------------------------------------------------- /chapters/files/pda.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | Produced by OmniGraffle 6.0.5 2015-12-11 01:22ZCanvas 1Layer 1WhatNext?WhatNext?WhatNext?WhatNext?WhatNext?WhatNext? 4 | -------------------------------------------------------------------------------- /chapters/files/pda2.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | Produced by OmniGraffle 6.4.1 2016-01-16 16:04:07 +0000Canvas 1Layer 1PickMe!Pick Me!PickMe!Pick Me!Pick Me!Buy Me :) 4 | -------------------------------------------------------------------------------- /chapters/files/cond.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Produced by ChatGPT 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | Canvas 1 30 | 31 | 32 | Layer 1 33 | 34 | 35 | 36 | 37 | 38 | x < y 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | IO.puts("less") 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | x > y 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | IO.puts("greater") 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | true 79 | 80 | 81 | 82 | 83 | 84 | 85 | IO.puts("equal") 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | -------------------------------------------------------------------------------- /chapters/files/nested.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | Produced by OmniGraffle 6.5 2016-04-08 10:47:33 +0000Canvas 1Layer 1x == yNoIO.puts("equal")Yesx < yIO.puts("greater")NoIO.puts("less")Yes 4 | -------------------------------------------------------------------------------- /chapters/files/arch.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | Produced by OmniGraffle 6.4.1 2016-01-16 18:45:26 +0000Canvas 1Layer 1Input andOutputDevicesSoftwareMain MemoryCentralProcessingUnitWhatNext?NetworkSecondaryMemory 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 | Produced by OmniGraffle 6.4.1 2016-01-16 18:45:19 +0000Canvas 1Layer 1Input andOutputDevicesSoftwareMain MemoryCentralProcessingUnitWhatNext?NetworkSecondaryMemory 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 | ![](files/if.svg) 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 | ![](files/if-else.svg) 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 | ![](files/cond.svg) 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 | ![](files/nested.svg) 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 | ![](files/pda.svg) 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 | ![](files/pda2.svg) 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 | ![](files/arch.svg) 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 | ![](files/arch2.svg) 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 | --------------------------------------------------------------------------------