Warning, /frameworks/syntax-highlighting/autotests/input/learnelixir.exs is written in an unsupported language. File is not indexed.
0001 # Original: https://learnxinyminutes.com/docs/elixir/ 0002 0003 # Single line comments start with a number symbol. 0004 0005 # There's no multi-line comment, 0006 # but you can stack multiple comments. 0007 0008 # To use the elixir shell use the `iex` command. 0009 # Compile your modules with the `elixirc` command. 0010 0011 # Both should be in your path if you installed elixir correctly. 0012 0013 ## --------------------------- 0014 ## -- Basic types 0015 ## --------------------------- 0016 0017 # There are numbers 0018 3 # integer 0019 0x1F # integer 0020 3.0 # float 0021 0022 # Atoms, that are literals, a constant with name. They start with `:`. 0023 :hello # atom 0024 0025 # Tuples that are stored contiguously in memory. 0026 {1,2,3} # tuple 0027 0028 # We can access a tuple element with the `elem` function: 0029 elem({1, 2, 3}, 0) #=> 1 0030 0031 # Lists that are implemented as linked lists. 0032 [1,2,3] # list 0033 0034 # We can access the head and tail of a list as follows: 0035 [head | tail] = [1,2,3] 0036 head #=> 1 0037 tail #=> [2,3] 0038 0039 # In elixir, just like in Erlang, the `=` denotes pattern matching and 0040 # not an assignment. 0041 # 0042 # This means that the left-hand side (pattern) is matched against a 0043 # right-hand side. 0044 # 0045 # This is how the above example of accessing the head and tail of a list works. 0046 0047 # A pattern match will error when the sides don't match, in this example 0048 # the tuples have different sizes. 0049 # {a, b, c} = {1, 2} #=> ** (MatchError) no match of right hand side value: {1,2} 0050 0051 # There are also binaries 0052 <<1,2,3>> # binary 0053 0054 # Strings and char lists 0055 "hello" # string 0056 'hello' # char list 0057 0058 # Multi-line strings 0059 """ 0060 I'm a multi-line 0061 string. 0062 """ 0063 #=> "I'm a multi-line\nstring.\n" 0064 0065 # Strings are all encoded in UTF-8: 0066 "héllò" #=> "héllò" 0067 0068 # Strings are really just binaries, and char lists are just lists. 0069 <<?a, ?b, ?c>> #=> "abc" 0070 [?a, ?b, ?c] #=> 'abc' 0071 0072 # `?a` in elixir returns the ASCII integer for the letter `a` 0073 ?a #=> 97 0074 0075 # To concatenate lists use `++`, for binaries use `<>` 0076 [1,2,3] ++ [4,5] #=> [1,2,3,4,5] 0077 'hello ' ++ 'world' #=> 'hello world' 0078 0079 <<1,2,3>> <> <<4,5>> #=> <<1,2,3,4,5>> 0080 "hello " <> "world" #=> "hello world" 0081 0082 # Ranges are represented as `start..end` (both inclusive) 0083 1..10 #=> 1..10 0084 lower..upper = 1..10 # Can use pattern matching on ranges as well 0085 [lower, upper] #=> [1, 10] 0086 0087 ## --------------------------- 0088 ## -- Operators 0089 ## --------------------------- 0090 0091 # Some math 0092 1 + 1 #=> 2 0093 10 - 5 #=> 5 0094 5 * 2 #=> 10 0095 10 / 2 #=> 5.0 0096 0097 # In elixir the operator `/` always returns a float. 0098 0099 # To do integer division use `div` 0100 div(10, 2) #=> 5 0101 0102 # To get the division remainder use `rem` 0103 rem(10, 3) #=> 1 0104 0105 # There are also boolean operators: `or`, `and` and `not`. 0106 # These operators expect a boolean as their first argument. 0107 true and true #=> true 0108 false or true #=> true 0109 # 1 and true #=> ** (ArgumentError) argument error 0110 0111 # Elixir also provides `||`, `&&` and `!` which accept arguments of any type. 0112 # All values except `false` and `nil` will evaluate to true. 0113 1 || true #=> 1 0114 false && 1 #=> false 0115 nil && 20 #=> nil 0116 !true #=> false 0117 0118 # For comparisons we have: `==`, `!=`, `===`, `!==`, `<=`, `>=`, `<` and `>` 0119 1 == 1 #=> true 0120 1 != 1 #=> false 0121 1 < 2 #=> true 0122 0123 # `===` and `!==` are more strict when comparing integers and floats: 0124 1 == 1.0 #=> true 0125 1 === 1.0 #=> false 0126 0127 # We can also compare two different data types: 0128 1 < :hello #=> true 0129 0130 # The overall sorting order is defined below: 0131 # number < atom < reference < functions < port < pid < tuple < list < bit string 0132 0133 # To quote Joe Armstrong on this: "The actual order is not important, 0134 # but that a total ordering is well defined is important." 0135 0136 ## --------------------------- 0137 ## -- Control Flow 0138 ## --------------------------- 0139 0140 # `if` expression 0141 if false do 0142 "This will never be seen" 0143 else 0144 "This will" 0145 end 0146 0147 # There's also `unless` 0148 unless true do 0149 "This will never be seen" 0150 else 0151 "This will" 0152 end 0153 0154 # Remember pattern matching? Many control-flow structures in elixir rely on it. 0155 0156 # `case` allows us to compare a value against many patterns: 0157 case {:one, :two} do 0158 {:four, :five} -> 0159 "This won't match" 0160 {:one, x} -> 0161 "This will match and bind `x` to `:two`" 0162 _ -> 0163 "This will match any value" 0164 end 0165 0166 # It's common to bind the value to `_` if we don't need it. 0167 # For example, if only the head of a list matters to us: 0168 [head | _] = [1,2,3] 0169 head #=> 1 0170 0171 # For better readability we can do the following: 0172 [head | _tail] = [:a, :b, :c] 0173 head #=> :a 0174 0175 # `cond` lets us check for many conditions at the same time. 0176 # Use `cond` instead of nesting many `if` expressions. 0177 cond do 0178 1 + 1 == 3 -> 0179 "I will never be seen" 0180 2 * 5 == 12 -> 0181 "Me neither" 0182 1 + 2 == 3 -> 0183 "But I will" 0184 end 0185 0186 # It is common to set the last condition equal to `true`, which will always match. 0187 cond do 0188 1 + 1 == 3 -> 0189 "I will never be seen" 0190 2 * 5 == 12 -> 0191 "Me neither" 0192 true -> 0193 "But I will (this is essentially an else)" 0194 end 0195 0196 # `try/catch` is used to catch values that are thrown, it also supports an 0197 # `after` clause that is invoked whether or not a value is caught. 0198 try do 0199 throw(:hello) 0200 catch 0201 message -> "Got #{message}." 0202 after 0203 IO.puts("I'm the after clause.") 0204 end 0205 #=> I'm the after clause 0206 # "Got :hello" 0207 0208 ## --------------------------- 0209 ## -- Modules and Functions 0210 ## --------------------------- 0211 0212 # Anonymous functions (notice the dot) 0213 square = fn(x) -> x * x end 0214 square.(5) #=> 25 0215 0216 # They also accept many clauses and guards. 0217 # Guards let you fine tune pattern matching, 0218 # they are indicated by the `when` keyword: 0219 f = fn 0220 x, y when x > 0 -> x + y 0221 x, y -> x * y 0222 end 0223 0224 f.(1, 3) #=> 4 0225 f.(-1, 3) #=> -3 0226 0227 # Elixir also provides many built-in functions. 0228 # These are available in the current scope. 0229 is_number(10) #=> true 0230 is_list("hello") #=> false 0231 elem({1,2,3}, 0) #=> 1 0232 0233 # You can group several functions into a module. Inside a module use `def` 0234 # to define your functions. 0235 defmodule Math do 0236 def sum(a, b) do 0237 a + b 0238 end 0239 0240 def square(x) do 0241 x * x 0242 end 0243 end 0244 0245 Math.sum(1, 2) #=> 3 0246 Math.square(3) #=> 9 0247 0248 # To compile our simple Math module save it as `math.ex` and use `elixirc` 0249 # in your terminal: elixirc math.ex 0250 0251 # Inside a module we can define functions with `def` and private functions with `defp`. 0252 # A function defined with `def` is available to be invoked from other modules, 0253 # a private function can only be invoked locally. 0254 defmodule PrivateMath do 0255 def sum(a, b) do 0256 do_sum(a, b) 0257 end 0258 0259 defp do_sum(a, b) do 0260 a + b 0261 end 0262 end 0263 0264 PrivateMath.sum(1, 2) #=> 3 0265 # PrivateMath.do_sum(1, 2) #=> ** (UndefinedFunctionError) 0266 0267 # Function declarations also support guards and multiple clauses: 0268 defmodule Geometry do 0269 def area({:rectangle, w, h}) do 0270 w * h 0271 end 0272 0273 def area({:circle, r}) when is_number(r) do 0274 3.14 * r * r 0275 end 0276 end 0277 0278 Geometry.area({:rectangle, 2, 3}) #=> 6 0279 Geometry.area({:circle, 3}) #=> 28.25999999999999801048 0280 # Geometry.area({:circle, "not_a_number"}) 0281 #=> ** (FunctionClauseError) no function clause matching in Geometry.area/1 0282 0283 # Due to immutability, recursion is a big part of elixir 0284 defmodule Recursion do 0285 def sum_list([head | tail], acc) do 0286 sum_list(tail, acc + head) 0287 end 0288 0289 def sum_list([], acc) do 0290 acc 0291 end 0292 end 0293 0294 Recursion.sum_list([1,2,3], 0) #=> 6 0295 0296 # Elixir modules support attributes, there are built-in attributes and you 0297 # may also add custom ones. 0298 defmodule MyMod do 0299 @moduledoc """ 0300 This is a built-in attribute on a example module. 0301 """ 0302 0303 @my_data 100 # This is a custom attribute. 0304 IO.inspect(@my_data) #=> 100 0305 end 0306 0307 ## --------------------------- 0308 ## -- Structs and Exceptions 0309 ## --------------------------- 0310 0311 # Structs are extensions on top of maps that bring default values, 0312 # compile-time guarantees and polymorphism into Elixir. 0313 defmodule Person do 0314 defstruct name: nil, age: 0, height: 0 0315 end 0316 0317 joe_info = %Person{ name: "Joe", age: 30, height: 180 } 0318 #=> %Person{age: 30, height: 180, name: "Joe"} 0319 0320 # Access the value of name 0321 joe_info.name #=> "Joe" 0322 0323 # Update the value of age 0324 older_joe_info = %{ joe_info | age: 31 } 0325 #=> %Person{age: 31, height: 180, name: "Joe"} 0326 0327 # The `try` block with the `rescue` keyword is used to handle exceptions 0328 try do 0329 raise "some error" 0330 rescue 0331 RuntimeError -> "rescued a runtime error" 0332 _error -> "this will rescue any error" 0333 end 0334 #=> "rescued a runtime error" 0335 0336 # All exceptions have a message 0337 try do 0338 raise "some error" 0339 rescue 0340 x in [RuntimeError] -> 0341 x.message 0342 end 0343 #=> "some error" 0344 0345 ## --------------------------- 0346 ## -- Concurrency 0347 ## --------------------------- 0348 0349 # Elixir relies on the actor model for concurrency. All we need to write 0350 # concurrent programs in elixir are three primitives: spawning processes, 0351 # sending messages and receiving messages. 0352 0353 # To start a new process we use the `spawn` function, which takes a function 0354 # as argument. 0355 f = fn -> 2 * 2 end #=> #Function<erl_eval.20.80484245> 0356 spawn(f) #=> #PID<0.40.0> 0357 0358 # `spawn` returns a pid (process identifier), you can use this pid to send 0359 # messages to the process. To do message passing we use the `send` operator. 0360 # For all of this to be useful we need to be able to receive messages. This is 0361 # achieved with the `receive` mechanism: 0362 0363 # The `receive do` block is used to listen for messages and process 0364 # them when they are received. A `receive do` block will only 0365 # process one received message. In order to process multiple 0366 # messages, a function with a `receive do` block must recursively 0367 # call itself to get into the `receive do` block again. 0368 0369 defmodule Geometry do 0370 def area_loop do 0371 receive do 0372 {:rectangle, w, h} -> 0373 IO.puts("Area = #{w * h}") 0374 area_loop() 0375 {:circle, r} -> 0376 IO.puts("Area = #{3.14 * r * r}") 0377 area_loop() 0378 end 0379 end 0380 end 0381 0382 # Compile the module and create a process that evaluates `area_loop` in the shell 0383 pid = spawn(fn -> Geometry.area_loop() end) #=> #PID<0.40.0> 0384 # Alternatively 0385 pid = spawn(Geometry, :area_loop, []) 0386 0387 # Send a message to `pid` that will match a pattern in the receive statement 0388 send pid, {:rectangle, 2, 3} 0389 #=> Area = 6 0390 # {:rectangle,2,3} 0391 0392 send pid, {:circle, 2} 0393 #=> Area = 12.56000000000000049738 0394 # {:circle,2} 0395 0396 # The shell is also a process, you can use `self` to get the current pid 0397 self() #=> #PID<0.27.0> 0398 0399 # Code not found in the original, but needed to test the full range of the syntax 0400 0401 def function, do: {:ok, result} 0402 0403 [ 0404 :a, 0405 :b, 0406 :c 0407 ] 0408 0409 %{ 0410 a: "a", 0411 b: "b", 0412 c: "c" 0413 } 0414 0415 %A{ 0416 a: "a", 0417 b: "b", 0418 c: "c" 0419 }