CS代考程序代写 Elixir algorithm concurrency python C/CPS 506

C/CPS 506
Comparative Programming Languages Prof. Alex Ufkes
Topic 5: Finishing up Elixir

Notice!
Obligatory copyright notice in the age of digital delivery and online classrooms:
The copyright to this original work is held by Alex Ufkes. Students registered in course CCPS 506 can use this material for the purposes of this course but no other use is permitted, and there can be no sale or transfer or use of the work for any other purpose without explicit permission of Alex Ufkes.
© Alex Ufkes, 2020, 2021 2

Course Administration
© Alex Ufkes, 2020, 2021
3
Midterm next week!
• •
Takes place on D2L during the 2h lecture period. Test is ONLY available during these two hours.

This Week
© Alex Ufkes, 2020, 2021
4
Finish up Elixir
• Pattern Matching
• Control flow, keyword lists
• Enum VS Stream
• List comprehensions
• Elixir processes

© Alex Ufkes, 2020, 2021
5
Let’s Get Started!

Any Questions?
© Alex Ufkes, 2020, 2021
6

Pattern Matching
© Alex Ufkes, 2020, 2021 7

© Alex Ufkes, 2020, 2021
8
=
This is not the assignment operator. It is the match operator. Pattern matching is a fundamental part of Elixir
x= 1
When a name is on the left-hand side of the match operator, we bind or rebind the name.

© Alex Ufkes, 2020, 2021
9
iex> x = 2 2
iex> 2 = x 2
This is a valid expression!
(Variable) Name on the Right?
iex> 3 = x
** (MatchError) no match of right hand side value: 2
iex> 3 = x + 1 3
If a match is successful, it returns the value of the right-hand side of the expression. If not, a MatchError.

(Variable) Name on the Right?
iex> x = 2 2
iex> 2 = x 2
This is a valid expression!
iex> 3 = x
** (MatchError) no match of right hand side value: 2
iex> 3 = x + 1 3
Names on the left? Bind or rebind to value on the right. Names on the right? Pattern match with value on the left.
© Alex Ufkes, 2020, 2021 10

Matching Lists Let’s see matching with lists:
iex> list = [1, 2, 3, 4, 5]
[1, 2, 3, 4, 5]
iex> [1, 2, 3, 4, 5] = list [1, 2, 3, 4, 5]
© Alex Ufkes, 2020, 2021
11

Matching Lists
iex> list = [1, 2, 3, 4, 5]
[1, 2, 3, 4, 5]
iex> [1 | tail] = list
[1, 2, 3, 4, 5]
iex> tail
[2, 3, 4, 5]
iex> [2 | tail] = list
** (MatchError) no match of right hand side
value: [1, 2, 3, 4, 5]
A pattern match will error if the sides can’t be matched
• Separates list into head and tail.
• In this case, the head must be 1!
© Alex Ufkes, 2020, 2021 12

• Not equating the tail (of tail) with anything
• ‘_’ can never be read from. Value discarded.
iex> tail
[2, 3, 4, 5]
iex> [2 | _] = tail [2, 3, 4, 5]
iex> [2, 3 | test ] = tail
[2, 3, 4, 5]
iex> test
[4, 5]
iex> [_ | test ] = tail
[2, 3, 4, 5]
Match first two values
Match test with tail of tail
© Alex Ufkes, 2020, 2021 13

iex> tup = {:OK, “Hello”} {:OK, “Hello”}
iex> {:OK, value} = tup
{:OK, “Hello”}
iex> value
“Hello”
Matching Tuples
• When matching tuples, the comma is used as a separator.
• Tuples don’t deal in head/tail
• They aren’t linked lists.
• Comma for tuples, | for lists.
© Alex Ufkes, 2020, 2021 14

iex> {a | b} = {1, 2, 3, 4, 5}
** (CompileError) iex: misplaced operator |/2
The | operator is typically used between brackets as the cons operator: [head | tail]
where head is a single element and the tail is the remaining of a list. It is also used to update maps and structs, via the %{map | key: value} notation, and in typespecs, such as @type and @spec, to express the union of two types
© Alex Ufkes, 2020, 2021 15

Matching Tuples
iex> {a, b, c} = {:hello, “World”, 42}
{:hello, “World”, 42}
iex> {a, b} = {:hello, “World”, 42}
** (MatchError) no match of right hand side
value: {:hello, “World”, 42}
This is called destructuring. a, b, c are now bound to individual elements of the tuple.
© Alex Ufkes, 2020, 2021 16

Pin Operator
If we use the match operator with a variable on the left side of the expression, that variable is simply re-bound to that value. For example:
iex> x = 3 3
iex> x = 2 2
iex> x = 3 3
iex> ^x = 2
** (MatchError) no match of right hand side value: 2
This is often undesirable!
© Alex Ufkes, 2020, 2021
17
Use ^ operator to force x to hold its binding

iex> x = 2 2
Pin Operator: Lists & Tuples
iex> [^x, y] = [1, 3]
** (MatchError) no match of right hand side value: [1, 3]
iex> y
** (CompileError) iex:2: undefined function y/0
iex> [^x, y] = [2, 3] [2, 3]
iex> y 3
© Alex Ufkes, 2020, 2021 18

Functions & Patterns
© Alex Ufkes, 2020, 2021 19

Pattern Matching: Function Signatures Function “overloading” is just pattern matching on the signature
© Alex Ufkes, 2020, 2021 20

Ideas? What can we do?
© Alex Ufkes, 2020, 2021 21

© Alex Ufkes, 2020, 2021 22

What about…
?
© Alex Ufkes, 2020, 2021
23
Single argument, a tuple

© Alex Ufkes, 2020, 2021 24

© Alex Ufkes, 2020, 2021 25

© Alex Ufkes, 2020, 2021 26

Recursion in Elixir
Who needs looping anyway?
defmodule Length do
def of([]), do: 0
def of([_ | t]), do: 1 + of(t)
end
When there’s one value left in the list, t will be [ ]
© Alex Ufkes, 2020, 2021 27

Argument pattern matching makes recursion straightforward:
Base cases
Recursive case
© Alex Ufkes, 2020, 2021
28

Tail Recursion?
Consider UserMath.fac()
defmodule UserMath do
def fac(0), do: 1
def fac(n), do: n*fac(n-1)
end
defmodule UserMath do
do: fac(num, 1)
def fac(0, prod), do: prod
def fac(num, prod), do: fac(num-1, num*prod) end
Wrapper function so user can invoke without initializing the running product
def fac(num),
Pass running product as argument
© Alex Ufkes, 2020, 2021 29

Private Functions, Default Arguments
defmodule UserMath do
def fac(num), do: fac(num, 1)
defp fac(0, prod), do: prod
defp fac(num, prod), do: fac(num-1, num*prod)
end
Hide the tail helper functions from the outside world
© Alex Ufkes, 2020, 2021 30

Control “Structures”
Implemented using function calls and pattern matching
© Alex Ufkes, 2020, 2021 31

Selection: if/else
© Alex Ufkes, 2020, 2021
32

© Alex Ufkes, 2020, 2021 33

As long as this expressions evaluates to true or false
Why?
© Alex Ufkes, 2020, 2021 34

Boolean:
true, false
Boolean Expressions
With these operators:
• non-false and non-nil are true.
• nil and false are false.
• 0 is considered true!
iex> “gh” && false Except…
© Alex Ufkes, 2020, 2021
35
false
iex> “gh” || false
“gh”
• •
The result isn’t true or false It’s the value that decided the result of true or false
&&, ||, !
What we actually get is the value that determined the truthiness of the expression

“No loop/if-else/case constructs”
In Elixir, we have several control structures that are implemented as macros. They are not actually constructs of the programming language.
Their implementation exists in the Elixir Kernel module.
They allow us to write if/else-style constructs in a familiar way. However, these are function calls behind the scenes.
© Alex Ufkes, 2020, 2021
36

?
© Alex Ufkes, 2020, 2021
37

if 1 < 2 do “Hello” end Is the same as: Is the same as: if 1 < 2, do: “Hello” if(1 < 2, do: “Hello”) do/end VS Keyword List This form is a syntactic convenience allowed by Elixir to make the language more accessible. © Alex Ufkes, 2020, 2021 38 if 1 < 2 do “Hello” else “World” end do/end VS Keyword List Is the same as: if 1 < 2, do: “Hello”, else: “World” Is the same as: if(1 < 2, do: “Hello”, else: “World”) iex> if 1 < 2, do: "Hello", else: "World" "Hello" iex> if(1 < 2, do: "Hello", else: "World") "Hello" © Alex Ufkes, 2020, 2021 39 if 1 < 2 do “Hello” else “World” end do/end VS Keyword List Is the same as: if(1 < 2, do: “Hello”, else: “World”) Is the same as: if(1<2, [{:do, "Hello"}, {:else, "World"}]) © Alex Ufkes, 2020, 2021 40 if(1<2, [{:do, "Hello"}, {:else, "World"}]) © Alex Ufkes, 2020, 2021 41 if 1 < 2 do “Hello” else “World” end do/end VS Keyword List Is the same as: if 1 < 2, do: “Hello”, else: “World” Is the same as: if(1<2, [{:do, "Hello"}, {:else, "World"}]) © Alex Ufkes, 2020, 2021 42 iex> if 1 < 2, do: "Hello", else: "World" "Hello" iex> if(1 < 2, [{:do, "Hello”}, {:else, "World"}]) "Hello" Can be any expression! iex> if(1 < 2, [{:do, IO.puts "Hello"}, {:else, "World"}]) Hello :ok iex>
© Alex Ufkes, 2020, 2021 43

unless
unless is_integer(“hello”) do
“Not an Int”
end
iex> unless(is_integer(“hello”), do: “Not an Int”) “Not an Int”
iex> unless(is_integer(“hello”), [{:do, “Not an Int”}]) “Not an Int”
© Alex Ufkes, 2020, 2021 44

unless: With an else
unless is_integer(0b10101) do “Not an Int”
else
“An Int”
end
© Alex Ufkes, 2020, 2021
45
“An Int”

case
if and unless can’t handle pattern matching gracefully:
We can never get here!
• Matching returns the right-hand side…
• UNLESS no match is found, then it yields
a MatchError.
• If a match is found we’d be OK – [1, 2, 3]
is true (non-nil, non-false)
© Alex Ufkes, 2020, 2021 46

case
Match this tuple successively
with each case:
tup = {:ok, “Hello World”} case tup do
{:ok, result} -> result {:error} -> “Uh oh!”
_ -> “Catch all”
end
Pattern match!
{:ok, result} = {:ok, “Hello World”}
{:error} = {:ok, “Hello World”}
_ = {:ok, “Hello World”}
Without a catch-all, we’d get an error if no match was found.
© Alex Ufkes, 2020, 2021
47

© Alex Ufkes, 2020, 2021 48

© Alex Ufkes, 2020, 2021 49

© Alex Ufkes, 2020, 2021 50

Comment out catch all case
© Alex Ufkes, 2020, 2021 51

case: Matching Variables pi = 3.14
IO.puts pi What prints?
case do
pi -> IO.puts “Tasty ” <> pi
_ -> IO.puts “#{pi} is not tasty”
end
Attempts to match: pi = “apple pie”
• What’s the problem here?
© Alex Ufkes, 2020, 2021
52
“apple pie”

Pin pi using ^
© Alex Ufkes, 2020, 2021 53

Guard Clauses
Guard reference: https://hexdocs.pm/elixir/master/guards.html
© Alex Ufkes, 2020, 2021 54
Place a condition on the match:
• In this case, match is only successful if x < 0 Guard Clauses Guard reference: https://hexdocs.pm/elixir/master/guards.html © Alex Ufkes, 2020, 2021 55 cond case is for pattern matching, cond is for conditions: Evaluating cond: • We say y=cond ... • Cond evaluates to the final expression under the first true condition. • Similar to a block in Smalltalk © Alex Ufkes, 2020, 2021 56 © Alex Ufkes, 2020, 2021 57 cond: Always have a catch-all © Alex Ufkes, 2020, 2021 58 © Alex Ufkes, 2020, 2021 59 Enum Enum A set of algorithms for enumeration over enumerables! (lists, tuples, and more) Enum applies functions to lists in various ways. We will see a few: Enum.all? # Entire collection must evaluate to true for a given condition Enum.any? # Any value in the collection must evaluate true Enum.map # Apply a function to every element in the collection More: https://elixirschool.com/en/lessons/basics/enum/ © Alex Ufkes, 2020, 2021 60 Enum.all? Entire collection must evaluate to true for a given condition Pass list as first arg Anon function as second arg © Alex Ufkes, 2020, 2021 61 Enum.all? We can do it! • and the function result with the running Boolean result. • If we hit an element for which f.(h) is false, the entire running Boolean becomes false. © Alex Ufkes, 2020, 2021 62 Tail recursive! Enum.all? We can do it! © Alex Ufkes, 2020, 2021 63 Enum.any? Any value in collection must evaluate to true for a given condition © Alex Ufkes, 2020, 2021 64 Enum.any? We can do it! Very similar to MyEnum.all • Initialize res to false • Any true value from function f will turn result true. • We are ORing instead of ANDing © Alex Ufkes, 2020, 2021 65 Enum.any? We can do it! © Alex Ufkes, 2020, 2021 66 Enum.map Very useful! Apply a function to every element © Alex Ufkes, 2020, 2021 67 Enum.map: We can do it! • Result initialized as an empty list • Concatenate [f.(h)] to the running result list © Alex Ufkes, 2020, 2021 68 Enum.map: We can do it! © Alex Ufkes, 2020, 2021 69 Enum.map: We can do it! © Alex Ufkes, 2020, 2021 Huh? 70 Interlude: IO.puts VS IO.inspect IO.puts can’t handle arbitrary lists: iex> x = [1, 2.0, “Hello”, :world] [1, 2.0, “Hello”, :world]
iex> IO.puts x
** (ArgumentError) argument error
(stdlib) :io.put_chars(:standard_io, :unicode,
[[1, 2.0, “Hello”, :world], 10])
IO.puts wants a list containing things it can convert to Unicode.
© Alex Ufkes, 2020, 2021 71

Interlude: IO.puts VS IO.inspect We can use IO.inspect:
iex> x = [1, 2.0, “Hello”, :world] [1, 2.0, “Hello”, :world]
iex> IO.inspect x
[1, 2.0, “Hello”, :world]
[1, 2.0, “Hello”, :world]
© Alex Ufkes, 2020, 2021 72
IO.inspect prints and returns the list.

Interlude: IO.puts VS IO.inspect We can use IO.inspect:
iex> x = [104, 101, 108, 108, 111] ‘hello’
iex> IO.inspect x
‘hello’
‘hello’
IO.inspect still prints as Unicode!
© Alex Ufkes, 2020, 2021 73

Interlude: IO.puts VS IO.inspect We can use IO.inspect:
iex> x = [104, 101, 108, 108, 111] ‘hello’
iex> IO.inspect x
‘hello’
‘hello’
iex> IO.inspect x, charlists: :as_lists
[104, 101, 108, 108, 111] ‘hello’
Prints list as a list, rather than converting to Unicode.
© Alex Ufkes, 2020, 2021 74
Invoke IO.inspect thusly:

Interlude: IO.puts VS IO.inspect IO.inspect x, charlists: :as_lists
Recall keyword list form:
IO.inspect(x, [{:charlists, :as_lists}])
© Alex Ufkes, 2020, 2021 75

Enum.reduce
Distill collection to single value based on some function
• acc is the running value
• By default, initialized to first element in list
© Alex Ufkes, 2020, 2021
76

Enum.reduce
Distill collection to single value based on some function
(9 – (8 – (7 – (6 – (5 – (4 – (3 – (2 – (1 – (0 – acc)…)
VS
(…(acc – 1) – 2) – 3) – 4) – 5) – 6) – 7) – 8) – 9)
© Alex Ufkes, 2020, 2021 77

Enum.reduce: We can do it!
• Result initialized as head of list
• Pass head of list and current
result into f
© Alex Ufkes, 2020, 2021
78

Enum.reduce: We can do it!
© Alex Ufkes, 2020, 2021
79

• acc is the running value
• By default, initialized to first element in list
We can add an optional 3rd argument to initialize acc:
iex> Enum.reduce([1, 2, 3], 10, fn(x, acc) -> x+acc end) 16
iex> Enum.reduce([1, 2, 3], fn(x, acc) -> x+acc end)
6
10 + 1 + 2 + 3
VS
1+ 2 + 3
© Alex Ufkes, 2020, 2021 80

Stream
© Alex Ufkes, 2020, 2021 81

Streams
Like Enum, but Streams are lazy! • Enum functions are strict/eager.
• The result of an Enum is the list that results from applying it:
iex> list = [1, 2, 3, 4, 5] [1, 2, 3, 4, 5]
iex> Enum.map(list, &(&1 + 1)) [2, 3, 4, 5, 6]
© Alex Ufkes, 2020, 2021
82

Streams
Like Enum, but Streams are lazy!
• Stream and Enum share many functions.
• What is the result of evaluating Stream.map?
iex> list = [1, 2, 3, 4, 5] [1, 2, 3, 4, 5]
iex> Stream.map(list, &(&1 + 1)) #Stream<[ enum: [1, 2, 3, 4, 5], funs: [#Function<48.103564624/1 in Stream.map/2>] ]>
© Alex Ufkes, 2020, 2021 83

iex> list = [1, 2, 3, 4, 5] [1, 2, 3, 4, 5]
iex> Stream.map(list, &(&1 + 1)) #Stream<[ enum: [1, 2, 3, 4, 5], funs: [#Function<48.103564624/1 in Stream.map/2>] ]>
• That’s not a list! Stream is its own type.
• Think of a stream as a recipe for producing the transformed list.
• Here, our stream is a recipe for adding 1 to every element.
• We haven’t actually done the cooking!
• Why is this useful?
© Alex Ufkes, 2020, 2021 84

Streams
Consider the following script:
list = [1, 2, 3, 4, 5]
r1 = Enum.map(list, &(&1 + 1)) |>
Enum.map(&(&1 * 3)) |> Enum.map(&(&1 / 2))
An aside: Pipe is useful here!
• Output list from Enum piped
into next call as 1st arg.
• Thus, subsequent Enum calls
only have 1 arg.
© Alex Ufkes, 2020, 2021
85
How many new lists are created when we evaluate this? One for each Enum call! Very inefficient.

Streams
list = [1, 2, 3, 4, 5]
r1 = Stream.map(list, &(&1 + 1)) |>
Stream.map(&(&1 * 3)) |> Stream.map(&(&1 / 2))
list = [1, 2, 3, 4, 5]
r1 = Stream.map(list, &(&1 + 1)) |>
Stream.map(&(&1 * 3)) |> Enum.map(&(&1 / 2))
• r1is a recipe for a new list
• At this point, no new list(s)
have been created!
• If we finish with an Enum call, the stream is applied.
• Only one new list created
© Alex Ufkes, 2020, 2021
86

© Alex Ufkes, 2020, 2021 87

Apply the Stream?
Can also use Enum.to_list
list = [1, 2, 3, 4, 5]
r1 = Stream.map(list, &(&1 + 1)) |>
Stream.map(&(&1 * 3)) |>
Stream.map(&(&1 / 2))
Enum.to_list(r1)
© Alex Ufkes, 2020, 2021
88

Lots more: https://hexdocs.pm/elixir/Stream.html
© Alex Ufkes, 2020, 2021 89

List Comprehensions
for:
• Generating • Filtering
• Operating
Produces a list when it’s done!
Not the same as an imperative-style for loop! Not for general purpose iteration.
© Alex Ufkes, 2020, 2021
90

List Comprehensions
Very much like comprehensions in Python:
Three parts:
• Generator • Filter
• Collector
Comprehensions are syntactic sugar for things we could otherwise do with Enum or recursive functions
© Alex Ufkes, 2020, 2021
91

List Comprehensions
iex> Enum.map([1, 2, 3, 4], &(&1*&1)) [1, 4, 9, 16]
iex> for n <- [1, 2, 3, 4], do: n*n [1, 4, 9, 16] Generator: Any enumerable • In this case, a plain old list © Alex Ufkes, 2020, 2021 92 List Comprehensions iex> Enum.map([1, 2, 3, 4], &(&1*&1)) [1, 4, 9, 16]
iex> for n <- [1, 2, 3, 4], do: n*n [1, 4, 9, 16] iex> for n <- do: n*n [1, 4, 9, 16] 1..4, • Used to produce list [1, 2, 3, 4] • Can generate large lists this way • Note: 1..4 is NOT itself a list! • It is a Range © Alex Ufkes, 2020, 2021 93 © Alex Ufkes, 2020, 2021 94 List Comprehensions iex> for n <- 1..4, do: n*n [1, 4, 9, 16] • List comprehensions produce lists • Generators like the above are lazy (Range) • Operate on elements one at a time, discarding previous. • That is, at no point do we produce the complete list [1, 2, 3, 4] in memory. https://hexdocs.pm/elixir/Range.html © Alex Ufkes, 2020, 2021 95 List Comprehensions: Pattern Matching iex> vals = [good: 1, good: 2, bad: 3, good: 4]
Keyword list!
iex> vals = [{:good, 1}, {:good, 2}, {:bad, 3}, {:good, 4}] [good: 1, good: 2, bad: 3, good: 4]
© Alex Ufkes, 2020, 2021 96

List Comprehensions: Pattern Matching iex> vals = [good: 1, good: 2, bad: 3, good: 4]
[good: 1, good: 2, bad: 3, good: 4]
iex> for {:good, n} <- vals, do: n*n [1, 4, 16] © Alex Ufkes, 2020, 2021 97 • • Pattern matching is powerful We can also filter in a Boolean fashion List Comprehensions: Filtering iex> fun = &(rem(&1, 3) == 0) #Function<6.99386804/1 in :erl_eval.expr/5>
iex> for n <- 1..20, do: n [3, 6, 9, 12, 15, 18] fun.(n), Filter is optional • Include it after generator if desired • Only elements that evaluate to true when filtered will make it to the do: block © Alex Ufkes, 2020, 2021 98 List Comprehensions: Filtering & Matching iex> list = [a: 1, b: “2”, a: 3.0, a: “4.0”, b: {5}, a: [“6.0”]]
[a: 1, b: “2”, a: 3.0, a: “4.0”, b: {5}, a: [“6.0”]]
iex> for {:a, n} <- list, is_number(n), do: n [1, 3.0] Nothing semantically new here • Anything we can do with comprehensions we can do with Enum or our own functions. • It might require more syntax, but we can do it. • Comprehensions can be used to create concise code © Alex Ufkes, 2020, 2021 99 List Comprehensions: In 2D? iex> for i <- [:a, :b, :c], j <- [1, 2], do: {i, j} [a: 1, a: 2, b: 1, b: 2, c: 1, c: 2] We get a keyword list containing combinations of all elements from both generators © Alex Ufkes, 2020, 2021 100 © Alex Ufkes, 2020, 2021 101 • • Elixir Processes (In Brief): Elixir is built on a process model. Recall: Elixir code runs inside lightweight threads of execution. o Isolated, exchange information via message passing. Not uncommon to have hundreds of thousands of processes running concurrently in same VM. o Note: These are NOT operating system processes! o Extremely lightweight in terms of CPU and memory o A process need not be an expensive resource Elixir Processes Playing with processes: • self() Returns PID of current process. o In this case, it’s the PID of our interactive shell session • Process.alive?() tests if a process is currently active. • We can spawn functions as processes! © Alex Ufkes, 2020, 2021 102 Elixir Processes • spawn takes a function as an argument and returns its PID once spawned. • Function executes when spawned Process is not active, the function is not currently executing © Alex Ufkes, 2020, 2021 103 Elixir Processes: Send & Receive iex> send(self(), {:Hello, “World”})
{:Hello, “World”}
• send/2 can be used to send a message (!!!) to a process (by PID)
• This message goes into a mailbox and can be received using the
receive/1 function
• When invoking receive, it will go through the messages in the mailbox
and attempt to match the messages with the provided patterns
© Alex Ufkes, 2020, 2021 104

Elixir Processes: Send & Receive
iex> send(self(), {:Hello, “World”}) {:Hello, “World”}
iex> receive do
…> {:Hello, msg} -> msg
…> {:World, msg} -> “won’t match”
…> end
“World”
iex>
• Once the message is received, it is consumed!
• We can’t receive the same message twice.
• Subsequent receive calls will be blocking
© Alex Ufkes, 2020, 2021
105

Elixir Processes: Send & Receive
© Alex Ufkes, 2020, 2021
106

Elixir Processes: Send & Receive
Receive is blocking!
• We sent one message, and received it.
• We then try and receive again, but the
mailbox is empty.
• Process sits and waits.
© Alex Ufkes, 2020, 2021
107

Elixir Processes: Send & Receive
© Alex Ufkes, 2020, 2021
108

Elixir Processes: Send & Receive
• Spawning a function as a process executes that function.
• A blocking receive can be used to wait for messages.
• Once the function receives a message, it will pattern match.
• Receive only blocks once! We have to spawn the function three times.
• Different order?
• Execution is interleaved.
• Up to scheduler.
© Alex Ufkes, 2020, 2021
109

Elixir Processes: Send & Receive
• Spawn all three, send each a message.
• Which child process gets chosen to
execute is up to the scheduler.
© Alex Ufkes, 2020, 2021
110

Elixir Processes
• This has been a taste. There’s lots more.
• Elixir is famous for powerful concurrent processing.
• Processes can be used to emulate the object message
passing model in languages like Smalltalk.
• If you understand a bit about concurrency from 209 or
590, check it out.
https://elixir-lang.org/getting-started/processes.html
© Alex Ufkes, 2020, 2021 111

© Alex Ufkes, 2020, 2021 112

Functional Programming & Elixir
We saw:
• Functionsasfirst-classentities
o How to create and pass anonymous functions as arguments o How to return anonymous functions
• •
Immutable data – variables (names) are bound and matched using = o Collections are not modified.
o Enum.map returns a new collection
Recursion – Loops are tail-recursive (ideally) functions calls. o Enum functions work this way behind the scenes
Elixir provides many syntax conveniences that make code more familiar to programmers accustomed to the imperative style.
© Alex Ufkes, 2020, 2021 113

Functional Programming & Elixir
Flow control is not built into the language as syntax constructs
• This suggests there’s no iteration in the typical sense of the word.
• Looping is accomplished with control structures in imperative languages.
• If control structures are functions, that means looping is always recursive.
• However
• This refers to the high level implementation only.
• Machine instructions are optimized into iteration via tail recursion.
© Alex Ufkes, 2020, 2021 114

Elixir Syntax
• Dynamically typed
o Type inferred at run-time
o Need not explicitly specify type upon declaration
• Provides syntax conveniences to make it more intuitive to programmers accustomed to imperative languages
• Interactive shell provides help/search functionality
https://media.pragprog.com/titles/elixir/ElixirCheat.pdf
© Alex Ufkes, 2020, 2021 115

Elixir Syntax
Reserved words
• true,false,nil o Used as atoms
• when,and,or,not,in o Used as operators
• fn
o Used for anonymous function definitions
• do,end,catch,rescue,after,else o Used in do/end blocks
https://github.com/elixir-lang/elixir/blob/master/lib/elixir/pages/Syntax%20Reference.md
© Alex Ufkes, 2020, 2021 116

Further Reading
https://elixir-lang.org/getting-started/introduction.html https://elixirschool.com/en/lessons/basics/basics/
© Alex Ufkes, 2020, 2021 117

Elixir Popularity
© Alex Ufkes, 2020, 2021
118

Elixir Popularity
https://techbeacon.com/5-emerging-programming-languages-bright-future
This list also includes Rust!
© Alex Ufkes, 2020, 2021
119

© Alex Ufkes, 2020, 2021 120

© Alex Ufkes, 2020, 2021 121