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 }