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 }