Pre-bootcamp
introduction to Elixir
Paweł Dawczak
# 1. Introduction - what is Elixir, how to start?
# 2. Basic and compound data types
# 3. Functions and modules
# 4. Let’s talk about `=`
# 5. Let’s have some fun!
# 6. Let’s talk about `mix`
# 7. Let’s talk about dependencies
# 8. Suggestions where to find more information
# 9. QA
# What is Elixir?
# Background
JVM
BEAM
Erlang/OTP
O
T
P
Erlang/OTP
O - Open
T - Telecom
P - Platform
José Valim
-module(my_module).
-export([hello/1]).
hello(Str) ->
my_helper(Str).
my_helper(Str) ->
string:reverse(string:uppercase(Str)).
-module(my_module).
-export([hello/1]).
hello(Str) ->
my_helper(Str).
my_helper(Str) ->
string:reverse(string:uppercase(Str)).
defmodule MyModule do
def hello(str) do
my_helper(str)
end
defp my_helper(str) do
str
|> String.upcase()
|> String.reverse()
end
end
# Where do I start?
# Install erlang
$ asdf plugin-add erlang
$ asdf list-all erlang # will list all the available versions
$ asdf install erlang 20.3.7
$ asdf global erlang 20.3.7
# Install erlang
$ asdf plugin-add erlang
$ asdf list-all erlang # will list all the available versions
$ asdf install erlang 20.3.7
$ asdf global erlang 20.3.7
# Install Elixir
$ asdf plugin-add elixir
$ asdf list-all elixir
$ asdf install elixir 1.6.5
$ asdf global elixir 1.6.5
# Install erlang
$ asdf plugin-add erlang
$ asdf list-all erlang # will list all the available versions
$ asdf install erlang 20.3.7
$ asdf global erlang 20.3.7
# Install Elixir
$ asdf plugin-add elixir
$ asdf list-all elixir
$ asdf install elixir 1.6.5
$ asdf global elixir 1.6.5
# [Optional] Install Node
# Basic data types
nil # => nil
true # => true
:test # => :test
nil # => nil
true # => true
:test # => :test
1 # => 1
1.2 # => 1.2
"Testing" # => "Testing"
'Testing' # => 'Testing'
nil # => nil
true # => true
:test # => :test
1 # => 1
1.2 # => 1.2
"Testing" # => "Testing"
'Testing' # => 'Testing'
is_binary("Testing") # => true
is_binary('Testing') # => false
is_list("Testing") # => false
is_list('Testing') # => true
nil # => nil
true # => true
:test # => :test
1 # => 1
1.2 # => 1.2
"Testing" # => "Testing"
'Testing' # => 'Testing'
is_binary("Testing") # => true
is_binary('Testing') # => false
is_list("Testing") # => false
is_list('Testing') # => true
pid(0, 13, 0) # => #PID<0.13.0>
nil # => nil
true # => true
:test # => :test
1 # => 1
1.2 # => 1.2
"Testing" # => "Testing"
'Testing' # => 'Testing'
is_binary("Testing") # => true
is_binary('Testing') # => false
is_list("Testing") # => false
is_list('Testing') # => true
pid(0, 13, 0) # => #PID<0.13.0>
make_ref() # => #Reference<0.1816712181.2865233921.217076>
# The reference is unique among connected nodes.
# Compound data types
# Lists
[1, 2, 3] # => [1, 2, 3]
# Lists
[1, 2, 3] # => [1, 2, 3]
# Tuples
{:ok, "Test"} # => {:ok, "Test"}
# Lists
[1, 2, 3] # => [1, 2, 3]
# Tuples
{:ok, "Test"} # => {:ok, "Test"}
# Maps
map = %{first_name: "Pawel", last_name: "Dawczak"} # => %{first_name: "Pawel", last_name: "Dawczak"}
map[:first_name] # => "Pawel"
# Lists
[1, 2, 3] # => [1, 2, 3]
# Tuples
{:ok, "Test"} # => {:ok, "Test"}
# Maps
map = %{first_name: "Pawel", last_name: "Dawczak"} # => %{first_name: "Pawel", last_name: "Dawczak"}
map[:first_name] # => "Pawel"
# Keyword Lists
opts = [per_page: 3, current_page: 1, per_page: 5] # => [per_page: 3, current_page: 1, per_page: 5]
# Lists
[1, 2, 3] # => [1, 2, 3]
# Tuples
{:ok, "Test"} # => {:ok, "Test"}
# Maps
map = %{first_name: "Pawel", last_name: "Dawczak"} # => %{first_name: "Pawel", last_name: "Dawczak"}
map[:first_name] # => "Pawel"
# Keyword Lists
opts = [per_page: 3, current_page: 1, per_page: 5] # => [per_page: 3, current_page: 1, per_page: 5]
opts[:per_page] # => 3
# Lists
[1, 2, 3] # => [1, 2, 3]
# Tuples
{:ok, "Test"} # => {:ok, "Test"}
# Maps
map = %{first_name: "Pawel", last_name: "Dawczak"} # => %{first_name: "Pawel", last_name: "Dawczak"}
map[:first_name] # => "Pawel"
# Keyword Lists
opts = [per_page: 3, current_page: 1, per_page: 5] # => [per_page: 3, current_page: 1, per_page: 5]
opts[:per_page] # => 3
Keyword.get_values(opts, :per_page) # => [3, 5]
# Lists
[1, 2, 3] # => [1, 2, 3]
# Tuples
{:ok, "Test"} # => {:ok, "Test"}
# Maps
map = %{first_name: "Pawel", last_name: "Dawczak"} # => %{first_name: "Pawel", last_name: "Dawczak"}
map[:first_name] # => "Pawel"
# Keyword Lists
opts = [per_page: 3, current_page: 1, per_page: 5] # => [per_page: 3, current_page: 1, per_page: 5]
opts[:per_page] # => 3
Keyword.get_values(opts, :per_page) # => [3, 5]
# Structs
# Lists
[1, 2, 3] # => [1, 2, 3]
# Tuples
{:ok, "Test"} # => {:ok, "Test"}
# Maps
map = %{first_name: "Pawel", last_name: "Dawczak"} # => %{first_name: "Pawel", last_name: "Dawczak"}
map[:first_name] # => "Pawel"
# Keyword Lists
opts = [per_page: 3, current_page: 1, per_page: 5] # => [per_page: 3, current_page: 1, per_page: 5]
opts[:per_page] # => 3
Keyword.get_values(opts, :per_page) # => [3, 5]
# Structs
defmodule User do
end
# Lists
[1, 2, 3] # => [1, 2, 3]
# Tuples
{:ok, "Test"} # => {:ok, "Test"}
# Maps
map = %{first_name: "Pawel", last_name: "Dawczak"} # => %{first_name: "Pawel", last_name: "Dawczak"}
map[:first_name] # => "Pawel"
# Keyword Lists
opts = [per_page: 3, current_page: 1, per_page: 5] # => [per_page: 3, current_page: 1, per_page: 5]
opts[:per_page] # => 3
Keyword.get_values(opts, :per_page) # => [3, 5]
# Structs
defmodule User do
defstruct [:first_name, :last_name, username: "Guest"]
end
# Lists
[1, 2, 3] # => [1, 2, 3]
# Tuples
{:ok, "Test"} # => {:ok, "Test"}
# Maps
map = %{first_name: "Pawel", last_name: "Dawczak"} # => %{first_name: "Pawel", last_name: "Dawczak"}
map[:first_name] # => "Pawel"
# Keyword Lists
opts = [per_page: 3, current_page: 1, per_page: 5] # => [per_page: 3, current_page: 1, per_page: 5]
opts[:per_page] # => 3
Keyword.get_values(opts, :per_page) # => [3, 5]
# Structs
defmodule User do
defstruct [:first_name, :last_name, username: "Guest"]
end
%User{first_name: "Pawel", last_name: "Dawczak"}
# Lists
[1, 2, 3] # => [1, 2, 3]
# Tuples
{:ok, "Test"} # => {:ok, "Test"}
# Maps
map = %{first_name: "Pawel", last_name: "Dawczak"} # => %{first_name: "Pawel", last_name: "Dawczak"}
map[:first_name] # => "Pawel"
# Keyword Lists
opts = [per_page: 3, current_page: 1, per_page: 5] # => [per_page: 3, current_page: 1, per_page: 5]
opts[:per_page] # => 3
Keyword.get_values(opts, :per_page) # => [3, 5]
# Structs
defmodule User do
defstruct [:first_name, :last_name, username: "Guest"]
end
%User{first_name: "Pawel", last_name: "Dawczak"}
# => User{first_name: "Pawel", last_name: "Dawczak", username: "Guest"}
# Functions and modules
# Functions
inc = fn x -> x + 1 end
inc.(3) # => 4
# Functions
inc = fn x -> x + 1 end
inc.(3) # => 4
# Capture operator
dec = &(&1 - 1)
dec.(3) # => 2
# Functions
inc = fn x -> x + 1 end
inc.(3) # => 4
# Capture operator
dec = &(&1 - 1)
dec.(3) # => 2
# Modules
defmodule MyModule do
end
# Functions
inc = fn x -> x + 1 end
inc.(3) # => 4
# Capture operator
dec = &(&1 - 1)
dec.(3) # => 2
# Modules
defmodule MyModule do
def square(x) do
x * 2
end
end
MyModule.square(4) # => 8
# Functions
inc = fn x -> x + 1 end
inc.(3) # => 4
# Capture operator
dec = &(&1 - 1)
dec.(3) # => 2
# Modules
defmodule MyModule do
def square(x) do
x * 2
end
def multi(x, y), do: x * y
end
MyModule.square(4) # => 8
MyModule.multi(3, 4) # => 12
# Comprehensions
for x <- (1..5),
y <- [:blue, :red],
rem(x, 2) == 0,
do: "#{x * x}-#{y}" # => ["4-blue", "4-red", "16-blue", "16-red"]
# Comprehensions
for x <- (1..5),
y <- [:blue, :red],
rem(x, 2) == 0,
do: "#{x * x}-#{y}" # => ["4-blue", "4-red", "16-blue", "16-red"]
# Higher-order functions
# Comprehensions
for x <- (1..5),
y <- [:blue, :red],
rem(x, 2) == 0,
do: "#{x * x}-#{y}" # => ["4-blue", "4-red", "16-blue", "16-red"]
# Higher-order functions
Enum.map([1, 2, 3], fn num -> to_string(num) end) # => ["1", "2", "3"]
# Comprehensions
for x <- (1..5),
y <- [:blue, :red],
rem(x, 2) == 0,
do: "#{x * x}-#{y}" # => ["4-blue", "4-red", "16-blue", "16-red"]
# Higher-order functions
Enum.map([1, 2, 3], fn num -> to_string(num) end) # => ["1", "2", "3"]
Enum.map([1, 2, 3], &to_string(&1)) # => ["1", "2", "3"]
# Comprehensions
for x <- (1..5),
y <- [:blue, :red],
rem(x, 2) == 0,
do: "#{x * x}-#{y}" # => ["4-blue", "4-red", "16-blue", "16-red"]
# Higher-order functions
Enum.map([1, 2, 3], fn num -> to_string(num) end) # => ["1", "2", "3"]
Enum.map([1, 2, 3], &to_string(&1)) # => ["1", "2", "3"]
Enum.map([1, 2, 3], &to_string/1) # => ["1", "2", "3"]
# Comprehensions
for x <- (1..5),
y <- [:blue, :red],
rem(x, 2) == 0,
do: "#{x * x}-#{y}" # => ["4-blue", "4-red", "16-blue", "16-red"]
# Higher-order functions
Enum.map([1, 2, 3], fn num -> to_string(num) end) # => ["1", "2", "3"]
Enum.map([1, 2, 3], &to_string(&1)) # => ["1", "2", "3"]
Enum.map([1, 2, 3], &to_string/1) # => ["1", "2", "3"]
Enum.reduce(1..3, 0, fn num, acc -> num + acc end) # => 6
# Comprehensions
for x <- (1..5),
y <- [:blue, :red],
rem(x, 2) == 0,
do: "#{x * x}-#{y}" # => ["4-blue", "4-red", "16-blue", "16-red"]
# Higher-order functions
Enum.map([1, 2, 3], fn num -> to_string(num) end) # => ["1", "2", "3"]
Enum.map([1, 2, 3], &to_string(&1)) # => ["1", "2", "3"]
Enum.map([1, 2, 3], &to_string/1) # => ["1", "2", "3"]
Enum.reduce(1..3, 0, fn num, acc -> num + acc end) # => 6
Enum.reduce(1..3, 0, &(&1 + &2)) # => 6
# Comprehensions
for x <- (1..5),
y <- [:blue, :red],
rem(x, 2) == 0,
do: "#{x * x}-#{y}" # => ["4-blue", "4-red", "16-blue", "16-red"]
# Higher-order functions
Enum.map([1, 2, 3], fn num -> to_string(num) end) # => ["1", "2", "3"]
Enum.map([1, 2, 3], &to_string(&1)) # => ["1", "2", "3"]
Enum.map([1, 2, 3], &to_string/1) # => ["1", "2", "3"]
Enum.reduce(1..3, 0, fn num, acc -> num + acc end) # => 6
Enum.reduce(1..3, 0, &(&1 + &2)) # => 6
Enum.reduce(1..3, 0, &+/2) # => 6
# Comprehensions
for x <- (1..5),
y <- [:blue, :red],
rem(x, 2) == 0,
do: "#{x * x}-#{y}" # => ["4-blue", "4-red", "16-blue", "16-red"]
# Higher-order functions
Enum.map([1, 2, 3], fn num -> to_string(num) end) # => ["1", "2", "3"]
Enum.map([1, 2, 3], &to_string(&1)) # => ["1", "2", "3"]
Enum.map([1, 2, 3], &to_string/1) # => ["1", "2", "3"]
Enum.reduce(1..3, 0, fn num, acc -> num + acc end) # => 6
Enum.reduce(1..3, 0, &(&1 + &2)) # => 6
Enum.reduce(1..3, 0, &+/2) # => 6
pass = "MySecretPass123"
# Comprehensions
for x <- (1..5),
y <- [:blue, :red],
rem(x, 2) == 0,
do: "#{x * x}-#{y}" # => ["4-blue", "4-red", "16-blue", "16-red"]
# Higher-order functions
Enum.map([1, 2, 3], fn num -> to_string(num) end) # => ["1", "2", "3"]
Enum.map([1, 2, 3], &to_string(&1)) # => ["1", "2", "3"]
Enum.map([1, 2, 3], &to_string/1) # => ["1", "2", "3"]
Enum.reduce(1..3, 0, fn num, acc -> num + acc end) # => 6
Enum.reduce(1..3, 0, &(&1 + &2)) # => 6
Enum.reduce(1..3, 0, &+/2) # => 6
pass = "MySecretPass123"
Enum.map([3, 8, 14], fn x -> String.at(pass, x) end) # => ["e", "P", "3"]
# Comprehensions
for x <- (1..5),
y <- [:blue, :red],
rem(x, 2) == 0,
do: "#{x * x}-#{y}" # => ["4-blue", "4-red", "16-blue", "16-red"]
# Higher-order functions
Enum.map([1, 2, 3], fn num -> to_string(num) end) # => ["1", "2", "3"]
Enum.map([1, 2, 3], &to_string(&1)) # => ["1", "2", "3"]
Enum.map([1, 2, 3], &to_string/1) # => ["1", "2", "3"]
Enum.reduce(1..3, 0, fn num, acc -> num + acc end) # => 6
Enum.reduce(1..3, 0, &(&1 + &2)) # => 6
Enum.reduce(1..3, 0, &+/2) # => 6
pass = "MySecretPass123"
Enum.map([3, 8, 14], fn x -> String.at(pass, x) end) # => ["e", "P", "3"]
Enum.map([3, 8, 14], &String.at(pass, &1)) # => ["e", "P", "3"]
# Let’s talk about `=`
x = 1
1 = x
2 = x
** (MatchError) no match of right hand side value: 1
^x = 1 # ^ - pin operator
^x = 2
list = [20, 21, 22]
[a, b, c] = list
a # => 20
b # => 21
[a, ^x, c] = list
** (MatchError) no match of right hand side value: [20, 21, 22]
hd(list) # => 20
tl(list) # => [21, 22]
[head | tail] = list
head # => 20
tail # => [21, 22]
[a2, 21 | tail] = list
a2 # => 20
tail # => [22]
map = %{a: "Foo", b: "Bar"}
map = %{a: "Foo", b: "Bar"}
%{a: a} = map
a # => "Foo"
map = %{a: "Foo", b: "Bar"}
%{a: a} = map
a # => "Foo"
%{a: "Baz", b: b} = map
map = %{a: "Foo", b: "Bar"}
%{a: a} = map
a # => "Foo"
%{a: "Baz", b: b} = map
** (MatchError) no match of right hand side value: %{a: "Foo", b: "Bar"}
map = %{a: "Foo", b: "Bar"}
%{a: a} = map
a # => "Foo"
%{a: "Baz", b: b} = map
** (MatchError) no match of right hand side value: %{a: "Foo", b: "Bar"}
defmodule Employee do
end
map = %{a: "Foo", b: "Bar"}
%{a: a} = map
a # => "Foo"
%{a: "Baz", b: b} = map
** (MatchError) no match of right hand side value: %{a: "Foo", b: "Bar"}
defmodule Employee do
defstruct [:name, :lang]
end
map = %{a: "Foo", b: "Bar"}
%{a: a} = map
a # => "Foo"
%{a: "Baz", b: b} = map
** (MatchError) no match of right hand side value: %{a: "Foo", b: "Bar"}
defmodule Employee do
defstruct [:name, :lang]
end
p = %Employee{name: "Pawel", lang: "Elixir"}
map = %{a: "Foo", b: "Bar"}
%{a: a} = map
a # => "Foo"
%{a: "Baz", b: b} = map
** (MatchError) no match of right hand side value: %{a: "Foo", b: "Bar"}
defmodule Employee do
defstruct [:name, :lang]
end
p = %Employee{name: "Pawel", lang: "Elixir"}
%{name: name} = p
name # => "Pawel"
map = %{a: "Foo", b: "Bar"}
%{a: a} = map
a # => "Foo"
%{a: "Baz", b: b} = map
** (MatchError) no match of right hand side value: %{a: "Foo", b: "Bar"}
defmodule Employee do
defstruct [:name, :lang]
end
p = %Employee{name: "Pawel", lang: "Elixir"}
%{name: name} = p
name # => "Pawel"
%Employee{name: name} = p
name # => "Pawel"
map = %{a: "Foo", b: "Bar"}
%{a: a} = map
a # => "Foo"
%{a: "Baz", b: b} = map
** (MatchError) no match of right hand side value: %{a: "Foo", b: "Bar"}
defmodule Employee do
defstruct [:name, :lang]
end
p = %Employee{name: "Pawel", lang: "Elixir"}
%{name: name} = p
name # => "Pawel"
%Employee{name: name} = p
name # => "Pawel"
lang = "Elixir"
map = %{a: "Foo", b: "Bar"}
%{a: a} = map
a # => "Foo"
%{a: "Baz", b: b} = map
** (MatchError) no match of right hand side value: %{a: "Foo", b: "Bar"}
defmodule Employee do
defstruct [:name, :lang]
end
p = %Employee{name: "Pawel", lang: "Elixir"}
%{name: name} = p
name # => "Pawel"
%Employee{name: name} = p
name # => "Pawel"
lang = "Elixir"
%Employee{name: name, lang: ^lang} = p
name # => "Pawel"
File.read("./nice_file")
File.read("./nice_file") # => {:ok, "Hello World! Nice to see you all!n"}
File.read("./nice_file") # => {:ok, "Hello World! Nice to see you all!n"}
File.read("./non_existing_one") # => {:error, :enoent}
File.read("./nice_file") # => {:ok, "Hello World! Nice to see you all!n"}
File.read("./non_existing_one") # => {:error, :enoent}
File.read!("./non_existing_one")
** (File.Error) could not read file "./non_existing_one": no such file or directory
File.read("./nice_file") # => {:ok, "Hello World! Nice to see you all!n"}
File.read("./non_existing_one") # => {:error, :enoent}
File.read!("./non_existing_one")
** (File.Error) could not read file "./non_existing_one": no such file or directory
HTTPoison.get("https://google.com")
File.read("./nice_file") # => {:ok, "Hello World! Nice to see you all!n"}
File.read("./non_existing_one") # => {:error, :enoent}
File.read!("./non_existing_one")
** (File.Error) could not read file "./non_existing_one": no such file or directory
HTTPoison.get("https://google.com")
{:ok,
%HTTPoison.Response{
body: "...",
headers: [
{"Location", "https://www.google.com/"},
{"Content-Type", "text/html; charset=UTF-8"},
{"Cache-Control", "public, max-age=2592000"},
{"Server", "gws"},
{"Content-Length", "220"},
{"X-Frame-Options", "SAMEORIGIN"},
],
request_url: "https://google.com",
status_code: 301
}}
String.reverse(String.upcase(format.(String.replace("My Content - 1", ~r/W/, "_"))))
String.reverse(String.upcase(format.(String.replace("My Content - 1", ~r/W/, "_"))))
# Pipe operator
String.replace("My Content - 1", ~r/W/, "_")
|> format.()
|> String.upcase()
|> String.reverse()
String.reverse(String.upcase(format.(String.replace("My Content - 1", ~r/W/, "_"))))
# Pipe operator
String.replace("My Content - 1", ~r/W/, "_")
|> format.()
|> String.upcase()
|> String.reverse()
# Pipe operator in action
String.reverse(String.upcase(format.(String.replace("My Content - 1", ~r/W/, "_"))))
# Pipe operator
String.replace("My Content - 1", ~r/W/, "_")
|> format.()
|> String.upcase()
|> String.reverse()
# Pipe operator in action
process = fn
{:ok, %{body: body}} ->
# parse the body, etc…
{:error, error_message} ->
Logger.error("Request failed due to: #{error_message}")
end
String.reverse(String.upcase(format.(String.replace("My Content - 1", ~r/W/, "_"))))
# Pipe operator
String.replace("My Content - 1", ~r/W/, "_")
|> format.()
|> String.upcase()
|> String.reverse()
# Pipe operator in action
process = fn
{:ok, %{body: body}} ->
# parse the body, etc…
{:error, error_message} ->
Logger.error("Request failed due to: #{error_message}")
end
"https://google.com"
|> HTTPoison.get()
|> process.()
# Let the fun begin!
# 1. Fibonacci!
# 1. Fibonacci!
defmodule Fib do
def of(0), do: 1
def of(1), do: 1
def of(n) when n > 1, do: of(n-1) + of(n-2)
end
Fib.of(5)
Fib.of(-6)
# 2. map
# 2. map
M.map([], fn x -> x * x end) # => []
M.map([1, 2, 3], fn x -> x * x end) # => [1, 4, 9]
# 2. map
defmodule M do
def map(list, fun), do: do_map(list, fun, [])
defp do_map([], _fun, acc), do: Enum.reverse(acc)
defp do_map([head | tail], fun, acc) do
result = fun.(head)
do_map(tail, fun, [result | acc])
end
end
M.map([], fn x -> x * x end) # => []
M.map([1, 2, 3], fn x -> x * x end) # => [1, 4, 9]
# 3. dedup
# 3. dedup
[1, 1, 1, 2, 3, 3]
# 3. dedup
[1, 1, 1, 2, 3, 3] => [{3, 1}, 2, {2, 3}]
# 3. dedup
3
1
1
1
list acc
# 3. dedup
1
1
1 3
list acc
# 3. dedup
1
{2, 1}
3
list acc
# 3. dedup
{3, 1}
3
list acc
# 3. dedup
list acc
3
{3, 1}
reversed
# 3. dedup
defmodule D do
end
D.dedup([1, 2, 3, 4]) # => [1, 2, 3, 4]
D.dedup([1, 1, 1, 2, 3, 3]) # => [{3, 1}, 2, {2, 3}]
# 3. dedup
defmodule D do
def dedup(list), do: do_dedup(list, [])
end
D.dedup([1, 2, 3, 4]) # => [1, 2, 3, 4]
D.dedup([1, 1, 1, 2, 3, 3]) # => [{3, 1}, 2, {2, 3}]
# 3. dedup
defmodule D do
def dedup(list), do: do_dedup(list, [])
defp do_dedup([], acc), do: Enum.reverse(acc)
end
D.dedup([1, 2, 3, 4]) # => [1, 2, 3, 4]
D.dedup([1, 1, 1, 2, 3, 3]) # => [{3, 1}, 2, {2, 3}]
# 3. dedup
defmodule D do
def dedup(list), do: do_dedup(list, [])
defp do_dedup([], acc), do: Enum.reverse(acc)
defp do_dedup([num, num | tail], acc) do
do_dedup(tail, [{2, num} | acc])
end
end
D.dedup([1, 2, 3, 4]) # => [1, 2, 3, 4]
D.dedup([1, 1, 1, 2, 3, 3]) # => [{3, 1}, 2, {2, 3}]
# 3. dedup
defmodule D do
def dedup(list), do: do_dedup(list, [])
defp do_dedup([], acc), do: Enum.reverse(acc)
defp do_dedup([num | tail], [{cnt, num} | acc_tail]) do
do_dedup(tail, [{cnt + 1, num} | acc_tail])
end
defp do_dedup([num, num | tail], acc) do
do_dedup(tail, [{2, num} | acc])
end
end
D.dedup([1, 2, 3, 4]) # => [1, 2, 3, 4]
D.dedup([1, 1, 1, 2, 3, 3]) # => [{3, 1}, 2, {2, 3}]
# 3. dedup
defmodule D do
def dedup(list), do: do_dedup(list, [])
defp do_dedup([], acc), do: Enum.reverse(acc)
defp do_dedup([num | tail], [{cnt, num} | acc_tail]) do
do_dedup(tail, [{cnt + 1, num} | acc_tail])
end
defp do_dedup([num, num | tail], acc) do
do_dedup(tail, [{2, num} | acc])
end
defp do_dedup([head | tail], acc), do: do_dedup(tail, [head | acc])
end
D.dedup([1, 2, 3, 4]) # => [1, 2, 3, 4]
D.dedup([1, 1, 1, 2, 3, 3]) # => [{3, 1}, 2, {2, 3}]
# 3. dedup
defmodule D do
def dedup(list), do: do_dedup(list, [])
defp do_dedup([], acc), do: Enum.reverse(acc)
defp do_dedup([num | tail], [{cnt, num} | acc_tail]) do
do_dedup(tail, [{cnt + 1, num} | acc_tail])
end
defp do_dedup([num, num | tail], acc) do
do_dedup(tail, [{2, num} | acc])
end
defp do_dedup([head | tail], acc), do: do_dedup(tail, [head | acc])
end
D.dedup([1, 2, 3, 4]) # => [1, 2, 3, 4]
D.dedup([1, 1, 1, 2, 3, 3]) # => [{3, 1}, 2, {2, 3}]
# 4. 5 most common words - eager approach
# 4. 5 most common words - eager approach
defmodule Eager do
def answer do
File.read!("moby.txt")
|> String.split("n")
|> Enum.map(%String.trim/1)
|> Enum.filter(&(&1 != ""))
|> Enum.flat_map(&String.split/1)
|> Enum.filter(&(String.length(&1) > 3))
|> Enum.reduce(%{}, fn word, words ->
Map.update(words, word, 1, fn curr -> curr + 1 end)
end)
|> Enum.sort_by(fn {_word, count} -> count end, &>=/2)
|> Enum.take(5)
end
end
Eager.answer()
# => [{"that", 2692}, {"with", 1695}, {"this", 1169}, {"from", 1072}, {"have", 752}]
# 5. 5 most common words - lazy approach
defmodule Lazy do
def answer do
File.stream!("moby.txt")
|> Stream.map(&String.trim/1)
|> Stream.filter(&(&1 != ""))
|> Stream.flat_map(&String.split/1)
|> Stream.filter(&(String.length(&1) > 3))
|> Enum.reduce(%{}, fn word, words ->
Map.update(words, word, 1, fn curr -> curr + 1 end)
end)
|> Enum.sort_by(fn {_word, count} -> count end, &>=/2)
|> Enum.take(5)
end
end
Lazy.answer()
# => [{"that", 2692}, {"with", 1695}, {"this", 1169}, {"from", 1072}, {"have", 752}]
# Let’s talk about `mix`
# Let’s talk about dependencies
# Where to go next?
http://exercism.io/
http://exercism.io/languages/elixir/about
https://adventofcode.com/
https://elixirschool.com/en/
https://github.com/elixirkoans/elixir-koans
https://pragprog.com/book/elixir16/programming-elixir-1-6
https://pragprog.com/book/cdc-elixir/learn-functional-programming-with-elixir
Thank You!
Pre-bootcamp
introduction to Elixir
Paweł Dawczak

Pre-Bootcamp introduction to Elixir

  • 1.
  • 2.
    # 1. Introduction- what is Elixir, how to start? # 2. Basic and compound data types # 3. Functions and modules # 4. Let’s talk about `=` # 5. Let’s have some fun! # 6. Let’s talk about `mix` # 7. Let’s talk about dependencies # 8. Suggestions where to find more information # 9. QA
  • 3.
    # What isElixir?
  • 8.
  • 11.
  • 12.
  • 13.
  • 14.
    Erlang/OTP O - Open T- Telecom P - Platform
  • 16.
  • 18.
  • 19.
    -module(my_module). -export([hello/1]). hello(Str) -> my_helper(Str). my_helper(Str) -> string:reverse(string:uppercase(Str)). defmoduleMyModule do def hello(str) do my_helper(str) end defp my_helper(str) do str |> String.upcase() |> String.reverse() end end
  • 20.
    # Where doI start?
  • 22.
    # Install erlang $asdf plugin-add erlang $ asdf list-all erlang # will list all the available versions $ asdf install erlang 20.3.7 $ asdf global erlang 20.3.7
  • 23.
    # Install erlang $asdf plugin-add erlang $ asdf list-all erlang # will list all the available versions $ asdf install erlang 20.3.7 $ asdf global erlang 20.3.7 # Install Elixir $ asdf plugin-add elixir $ asdf list-all elixir $ asdf install elixir 1.6.5 $ asdf global elixir 1.6.5
  • 24.
    # Install erlang $asdf plugin-add erlang $ asdf list-all erlang # will list all the available versions $ asdf install erlang 20.3.7 $ asdf global erlang 20.3.7 # Install Elixir $ asdf plugin-add elixir $ asdf list-all elixir $ asdf install elixir 1.6.5 $ asdf global elixir 1.6.5 # [Optional] Install Node
  • 25.
  • 26.
    nil # =>nil true # => true :test # => :test
  • 27.
    nil # =>nil true # => true :test # => :test 1 # => 1 1.2 # => 1.2 "Testing" # => "Testing" 'Testing' # => 'Testing'
  • 28.
    nil # =>nil true # => true :test # => :test 1 # => 1 1.2 # => 1.2 "Testing" # => "Testing" 'Testing' # => 'Testing' is_binary("Testing") # => true is_binary('Testing') # => false is_list("Testing") # => false is_list('Testing') # => true
  • 29.
    nil # =>nil true # => true :test # => :test 1 # => 1 1.2 # => 1.2 "Testing" # => "Testing" 'Testing' # => 'Testing' is_binary("Testing") # => true is_binary('Testing') # => false is_list("Testing") # => false is_list('Testing') # => true pid(0, 13, 0) # => #PID<0.13.0>
  • 30.
    nil # =>nil true # => true :test # => :test 1 # => 1 1.2 # => 1.2 "Testing" # => "Testing" 'Testing' # => 'Testing' is_binary("Testing") # => true is_binary('Testing') # => false is_list("Testing") # => false is_list('Testing') # => true pid(0, 13, 0) # => #PID<0.13.0> make_ref() # => #Reference<0.1816712181.2865233921.217076> # The reference is unique among connected nodes.
  • 31.
  • 32.
    # Lists [1, 2,3] # => [1, 2, 3]
  • 33.
    # Lists [1, 2,3] # => [1, 2, 3] # Tuples {:ok, "Test"} # => {:ok, "Test"}
  • 34.
    # Lists [1, 2,3] # => [1, 2, 3] # Tuples {:ok, "Test"} # => {:ok, "Test"} # Maps map = %{first_name: "Pawel", last_name: "Dawczak"} # => %{first_name: "Pawel", last_name: "Dawczak"} map[:first_name] # => "Pawel"
  • 35.
    # Lists [1, 2,3] # => [1, 2, 3] # Tuples {:ok, "Test"} # => {:ok, "Test"} # Maps map = %{first_name: "Pawel", last_name: "Dawczak"} # => %{first_name: "Pawel", last_name: "Dawczak"} map[:first_name] # => "Pawel" # Keyword Lists opts = [per_page: 3, current_page: 1, per_page: 5] # => [per_page: 3, current_page: 1, per_page: 5]
  • 36.
    # Lists [1, 2,3] # => [1, 2, 3] # Tuples {:ok, "Test"} # => {:ok, "Test"} # Maps map = %{first_name: "Pawel", last_name: "Dawczak"} # => %{first_name: "Pawel", last_name: "Dawczak"} map[:first_name] # => "Pawel" # Keyword Lists opts = [per_page: 3, current_page: 1, per_page: 5] # => [per_page: 3, current_page: 1, per_page: 5] opts[:per_page] # => 3
  • 37.
    # Lists [1, 2,3] # => [1, 2, 3] # Tuples {:ok, "Test"} # => {:ok, "Test"} # Maps map = %{first_name: "Pawel", last_name: "Dawczak"} # => %{first_name: "Pawel", last_name: "Dawczak"} map[:first_name] # => "Pawel" # Keyword Lists opts = [per_page: 3, current_page: 1, per_page: 5] # => [per_page: 3, current_page: 1, per_page: 5] opts[:per_page] # => 3 Keyword.get_values(opts, :per_page) # => [3, 5]
  • 38.
    # Lists [1, 2,3] # => [1, 2, 3] # Tuples {:ok, "Test"} # => {:ok, "Test"} # Maps map = %{first_name: "Pawel", last_name: "Dawczak"} # => %{first_name: "Pawel", last_name: "Dawczak"} map[:first_name] # => "Pawel" # Keyword Lists opts = [per_page: 3, current_page: 1, per_page: 5] # => [per_page: 3, current_page: 1, per_page: 5] opts[:per_page] # => 3 Keyword.get_values(opts, :per_page) # => [3, 5] # Structs
  • 39.
    # Lists [1, 2,3] # => [1, 2, 3] # Tuples {:ok, "Test"} # => {:ok, "Test"} # Maps map = %{first_name: "Pawel", last_name: "Dawczak"} # => %{first_name: "Pawel", last_name: "Dawczak"} map[:first_name] # => "Pawel" # Keyword Lists opts = [per_page: 3, current_page: 1, per_page: 5] # => [per_page: 3, current_page: 1, per_page: 5] opts[:per_page] # => 3 Keyword.get_values(opts, :per_page) # => [3, 5] # Structs defmodule User do end
  • 40.
    # Lists [1, 2,3] # => [1, 2, 3] # Tuples {:ok, "Test"} # => {:ok, "Test"} # Maps map = %{first_name: "Pawel", last_name: "Dawczak"} # => %{first_name: "Pawel", last_name: "Dawczak"} map[:first_name] # => "Pawel" # Keyword Lists opts = [per_page: 3, current_page: 1, per_page: 5] # => [per_page: 3, current_page: 1, per_page: 5] opts[:per_page] # => 3 Keyword.get_values(opts, :per_page) # => [3, 5] # Structs defmodule User do defstruct [:first_name, :last_name, username: "Guest"] end
  • 41.
    # Lists [1, 2,3] # => [1, 2, 3] # Tuples {:ok, "Test"} # => {:ok, "Test"} # Maps map = %{first_name: "Pawel", last_name: "Dawczak"} # => %{first_name: "Pawel", last_name: "Dawczak"} map[:first_name] # => "Pawel" # Keyword Lists opts = [per_page: 3, current_page: 1, per_page: 5] # => [per_page: 3, current_page: 1, per_page: 5] opts[:per_page] # => 3 Keyword.get_values(opts, :per_page) # => [3, 5] # Structs defmodule User do defstruct [:first_name, :last_name, username: "Guest"] end %User{first_name: "Pawel", last_name: "Dawczak"}
  • 42.
    # Lists [1, 2,3] # => [1, 2, 3] # Tuples {:ok, "Test"} # => {:ok, "Test"} # Maps map = %{first_name: "Pawel", last_name: "Dawczak"} # => %{first_name: "Pawel", last_name: "Dawczak"} map[:first_name] # => "Pawel" # Keyword Lists opts = [per_page: 3, current_page: 1, per_page: 5] # => [per_page: 3, current_page: 1, per_page: 5] opts[:per_page] # => 3 Keyword.get_values(opts, :per_page) # => [3, 5] # Structs defmodule User do defstruct [:first_name, :last_name, username: "Guest"] end %User{first_name: "Pawel", last_name: "Dawczak"} # => User{first_name: "Pawel", last_name: "Dawczak", username: "Guest"}
  • 43.
  • 44.
    # Functions inc =fn x -> x + 1 end inc.(3) # => 4
  • 45.
    # Functions inc =fn x -> x + 1 end inc.(3) # => 4 # Capture operator dec = &(&1 - 1) dec.(3) # => 2
  • 46.
    # Functions inc =fn x -> x + 1 end inc.(3) # => 4 # Capture operator dec = &(&1 - 1) dec.(3) # => 2 # Modules defmodule MyModule do end
  • 47.
    # Functions inc =fn x -> x + 1 end inc.(3) # => 4 # Capture operator dec = &(&1 - 1) dec.(3) # => 2 # Modules defmodule MyModule do def square(x) do x * 2 end end MyModule.square(4) # => 8
  • 48.
    # Functions inc =fn x -> x + 1 end inc.(3) # => 4 # Capture operator dec = &(&1 - 1) dec.(3) # => 2 # Modules defmodule MyModule do def square(x) do x * 2 end def multi(x, y), do: x * y end MyModule.square(4) # => 8 MyModule.multi(3, 4) # => 12
  • 49.
    # Comprehensions for x<- (1..5), y <- [:blue, :red], rem(x, 2) == 0, do: "#{x * x}-#{y}" # => ["4-blue", "4-red", "16-blue", "16-red"]
  • 50.
    # Comprehensions for x<- (1..5), y <- [:blue, :red], rem(x, 2) == 0, do: "#{x * x}-#{y}" # => ["4-blue", "4-red", "16-blue", "16-red"] # Higher-order functions
  • 51.
    # Comprehensions for x<- (1..5), y <- [:blue, :red], rem(x, 2) == 0, do: "#{x * x}-#{y}" # => ["4-blue", "4-red", "16-blue", "16-red"] # Higher-order functions Enum.map([1, 2, 3], fn num -> to_string(num) end) # => ["1", "2", "3"]
  • 52.
    # Comprehensions for x<- (1..5), y <- [:blue, :red], rem(x, 2) == 0, do: "#{x * x}-#{y}" # => ["4-blue", "4-red", "16-blue", "16-red"] # Higher-order functions Enum.map([1, 2, 3], fn num -> to_string(num) end) # => ["1", "2", "3"] Enum.map([1, 2, 3], &to_string(&1)) # => ["1", "2", "3"]
  • 53.
    # Comprehensions for x<- (1..5), y <- [:blue, :red], rem(x, 2) == 0, do: "#{x * x}-#{y}" # => ["4-blue", "4-red", "16-blue", "16-red"] # Higher-order functions Enum.map([1, 2, 3], fn num -> to_string(num) end) # => ["1", "2", "3"] Enum.map([1, 2, 3], &to_string(&1)) # => ["1", "2", "3"] Enum.map([1, 2, 3], &to_string/1) # => ["1", "2", "3"]
  • 54.
    # Comprehensions for x<- (1..5), y <- [:blue, :red], rem(x, 2) == 0, do: "#{x * x}-#{y}" # => ["4-blue", "4-red", "16-blue", "16-red"] # Higher-order functions Enum.map([1, 2, 3], fn num -> to_string(num) end) # => ["1", "2", "3"] Enum.map([1, 2, 3], &to_string(&1)) # => ["1", "2", "3"] Enum.map([1, 2, 3], &to_string/1) # => ["1", "2", "3"] Enum.reduce(1..3, 0, fn num, acc -> num + acc end) # => 6
  • 55.
    # Comprehensions for x<- (1..5), y <- [:blue, :red], rem(x, 2) == 0, do: "#{x * x}-#{y}" # => ["4-blue", "4-red", "16-blue", "16-red"] # Higher-order functions Enum.map([1, 2, 3], fn num -> to_string(num) end) # => ["1", "2", "3"] Enum.map([1, 2, 3], &to_string(&1)) # => ["1", "2", "3"] Enum.map([1, 2, 3], &to_string/1) # => ["1", "2", "3"] Enum.reduce(1..3, 0, fn num, acc -> num + acc end) # => 6 Enum.reduce(1..3, 0, &(&1 + &2)) # => 6
  • 56.
    # Comprehensions for x<- (1..5), y <- [:blue, :red], rem(x, 2) == 0, do: "#{x * x}-#{y}" # => ["4-blue", "4-red", "16-blue", "16-red"] # Higher-order functions Enum.map([1, 2, 3], fn num -> to_string(num) end) # => ["1", "2", "3"] Enum.map([1, 2, 3], &to_string(&1)) # => ["1", "2", "3"] Enum.map([1, 2, 3], &to_string/1) # => ["1", "2", "3"] Enum.reduce(1..3, 0, fn num, acc -> num + acc end) # => 6 Enum.reduce(1..3, 0, &(&1 + &2)) # => 6 Enum.reduce(1..3, 0, &+/2) # => 6
  • 57.
    # Comprehensions for x<- (1..5), y <- [:blue, :red], rem(x, 2) == 0, do: "#{x * x}-#{y}" # => ["4-blue", "4-red", "16-blue", "16-red"] # Higher-order functions Enum.map([1, 2, 3], fn num -> to_string(num) end) # => ["1", "2", "3"] Enum.map([1, 2, 3], &to_string(&1)) # => ["1", "2", "3"] Enum.map([1, 2, 3], &to_string/1) # => ["1", "2", "3"] Enum.reduce(1..3, 0, fn num, acc -> num + acc end) # => 6 Enum.reduce(1..3, 0, &(&1 + &2)) # => 6 Enum.reduce(1..3, 0, &+/2) # => 6 pass = "MySecretPass123"
  • 58.
    # Comprehensions for x<- (1..5), y <- [:blue, :red], rem(x, 2) == 0, do: "#{x * x}-#{y}" # => ["4-blue", "4-red", "16-blue", "16-red"] # Higher-order functions Enum.map([1, 2, 3], fn num -> to_string(num) end) # => ["1", "2", "3"] Enum.map([1, 2, 3], &to_string(&1)) # => ["1", "2", "3"] Enum.map([1, 2, 3], &to_string/1) # => ["1", "2", "3"] Enum.reduce(1..3, 0, fn num, acc -> num + acc end) # => 6 Enum.reduce(1..3, 0, &(&1 + &2)) # => 6 Enum.reduce(1..3, 0, &+/2) # => 6 pass = "MySecretPass123" Enum.map([3, 8, 14], fn x -> String.at(pass, x) end) # => ["e", "P", "3"]
  • 59.
    # Comprehensions for x<- (1..5), y <- [:blue, :red], rem(x, 2) == 0, do: "#{x * x}-#{y}" # => ["4-blue", "4-red", "16-blue", "16-red"] # Higher-order functions Enum.map([1, 2, 3], fn num -> to_string(num) end) # => ["1", "2", "3"] Enum.map([1, 2, 3], &to_string(&1)) # => ["1", "2", "3"] Enum.map([1, 2, 3], &to_string/1) # => ["1", "2", "3"] Enum.reduce(1..3, 0, fn num, acc -> num + acc end) # => 6 Enum.reduce(1..3, 0, &(&1 + &2)) # => 6 Enum.reduce(1..3, 0, &+/2) # => 6 pass = "MySecretPass123" Enum.map([3, 8, 14], fn x -> String.at(pass, x) end) # => ["e", "P", "3"] Enum.map([3, 8, 14], &String.at(pass, &1)) # => ["e", "P", "3"]
  • 60.
    # Let’s talkabout `=`
  • 61.
    x = 1 1= x 2 = x ** (MatchError) no match of right hand side value: 1 ^x = 1 # ^ - pin operator ^x = 2 list = [20, 21, 22] [a, b, c] = list a # => 20 b # => 21 [a, ^x, c] = list ** (MatchError) no match of right hand side value: [20, 21, 22] hd(list) # => 20 tl(list) # => [21, 22] [head | tail] = list head # => 20 tail # => [21, 22] [a2, 21 | tail] = list a2 # => 20 tail # => [22]
  • 62.
    map = %{a:"Foo", b: "Bar"}
  • 63.
    map = %{a:"Foo", b: "Bar"} %{a: a} = map a # => "Foo"
  • 64.
    map = %{a:"Foo", b: "Bar"} %{a: a} = map a # => "Foo" %{a: "Baz", b: b} = map
  • 65.
    map = %{a:"Foo", b: "Bar"} %{a: a} = map a # => "Foo" %{a: "Baz", b: b} = map ** (MatchError) no match of right hand side value: %{a: "Foo", b: "Bar"}
  • 66.
    map = %{a:"Foo", b: "Bar"} %{a: a} = map a # => "Foo" %{a: "Baz", b: b} = map ** (MatchError) no match of right hand side value: %{a: "Foo", b: "Bar"} defmodule Employee do end
  • 67.
    map = %{a:"Foo", b: "Bar"} %{a: a} = map a # => "Foo" %{a: "Baz", b: b} = map ** (MatchError) no match of right hand side value: %{a: "Foo", b: "Bar"} defmodule Employee do defstruct [:name, :lang] end
  • 68.
    map = %{a:"Foo", b: "Bar"} %{a: a} = map a # => "Foo" %{a: "Baz", b: b} = map ** (MatchError) no match of right hand side value: %{a: "Foo", b: "Bar"} defmodule Employee do defstruct [:name, :lang] end p = %Employee{name: "Pawel", lang: "Elixir"}
  • 69.
    map = %{a:"Foo", b: "Bar"} %{a: a} = map a # => "Foo" %{a: "Baz", b: b} = map ** (MatchError) no match of right hand side value: %{a: "Foo", b: "Bar"} defmodule Employee do defstruct [:name, :lang] end p = %Employee{name: "Pawel", lang: "Elixir"} %{name: name} = p name # => "Pawel"
  • 70.
    map = %{a:"Foo", b: "Bar"} %{a: a} = map a # => "Foo" %{a: "Baz", b: b} = map ** (MatchError) no match of right hand side value: %{a: "Foo", b: "Bar"} defmodule Employee do defstruct [:name, :lang] end p = %Employee{name: "Pawel", lang: "Elixir"} %{name: name} = p name # => "Pawel" %Employee{name: name} = p name # => "Pawel"
  • 71.
    map = %{a:"Foo", b: "Bar"} %{a: a} = map a # => "Foo" %{a: "Baz", b: b} = map ** (MatchError) no match of right hand side value: %{a: "Foo", b: "Bar"} defmodule Employee do defstruct [:name, :lang] end p = %Employee{name: "Pawel", lang: "Elixir"} %{name: name} = p name # => "Pawel" %Employee{name: name} = p name # => "Pawel" lang = "Elixir"
  • 72.
    map = %{a:"Foo", b: "Bar"} %{a: a} = map a # => "Foo" %{a: "Baz", b: b} = map ** (MatchError) no match of right hand side value: %{a: "Foo", b: "Bar"} defmodule Employee do defstruct [:name, :lang] end p = %Employee{name: "Pawel", lang: "Elixir"} %{name: name} = p name # => "Pawel" %Employee{name: name} = p name # => "Pawel" lang = "Elixir" %Employee{name: name, lang: ^lang} = p name # => "Pawel"
  • 73.
  • 74.
    File.read("./nice_file") # =>{:ok, "Hello World! Nice to see you all!n"}
  • 75.
    File.read("./nice_file") # =>{:ok, "Hello World! Nice to see you all!n"} File.read("./non_existing_one") # => {:error, :enoent}
  • 76.
    File.read("./nice_file") # =>{:ok, "Hello World! Nice to see you all!n"} File.read("./non_existing_one") # => {:error, :enoent} File.read!("./non_existing_one") ** (File.Error) could not read file "./non_existing_one": no such file or directory
  • 77.
    File.read("./nice_file") # =>{:ok, "Hello World! Nice to see you all!n"} File.read("./non_existing_one") # => {:error, :enoent} File.read!("./non_existing_one") ** (File.Error) could not read file "./non_existing_one": no such file or directory HTTPoison.get("https://google.com")
  • 78.
    File.read("./nice_file") # =>{:ok, "Hello World! Nice to see you all!n"} File.read("./non_existing_one") # => {:error, :enoent} File.read!("./non_existing_one") ** (File.Error) could not read file "./non_existing_one": no such file or directory HTTPoison.get("https://google.com") {:ok, %HTTPoison.Response{ body: "...", headers: [ {"Location", "https://www.google.com/"}, {"Content-Type", "text/html; charset=UTF-8"}, {"Cache-Control", "public, max-age=2592000"}, {"Server", "gws"}, {"Content-Length", "220"}, {"X-Frame-Options", "SAMEORIGIN"}, ], request_url: "https://google.com", status_code: 301 }}
  • 79.
  • 80.
    String.reverse(String.upcase(format.(String.replace("My Content -1", ~r/W/, "_")))) # Pipe operator String.replace("My Content - 1", ~r/W/, "_") |> format.() |> String.upcase() |> String.reverse()
  • 81.
    String.reverse(String.upcase(format.(String.replace("My Content -1", ~r/W/, "_")))) # Pipe operator String.replace("My Content - 1", ~r/W/, "_") |> format.() |> String.upcase() |> String.reverse() # Pipe operator in action
  • 82.
    String.reverse(String.upcase(format.(String.replace("My Content -1", ~r/W/, "_")))) # Pipe operator String.replace("My Content - 1", ~r/W/, "_") |> format.() |> String.upcase() |> String.reverse() # Pipe operator in action process = fn {:ok, %{body: body}} -> # parse the body, etc… {:error, error_message} -> Logger.error("Request failed due to: #{error_message}") end
  • 83.
    String.reverse(String.upcase(format.(String.replace("My Content -1", ~r/W/, "_")))) # Pipe operator String.replace("My Content - 1", ~r/W/, "_") |> format.() |> String.upcase() |> String.reverse() # Pipe operator in action process = fn {:ok, %{body: body}} -> # parse the body, etc… {:error, error_message} -> Logger.error("Request failed due to: #{error_message}") end "https://google.com" |> HTTPoison.get() |> process.()
  • 85.
    # Let thefun begin!
  • 86.
  • 88.
    # 1. Fibonacci! defmoduleFib do def of(0), do: 1 def of(1), do: 1 def of(n) when n > 1, do: of(n-1) + of(n-2) end Fib.of(5) Fib.of(-6)
  • 90.
  • 91.
    # 2. map M.map([],fn x -> x * x end) # => [] M.map([1, 2, 3], fn x -> x * x end) # => [1, 4, 9]
  • 92.
    # 2. map defmoduleM do def map(list, fun), do: do_map(list, fun, []) defp do_map([], _fun, acc), do: Enum.reverse(acc) defp do_map([head | tail], fun, acc) do result = fun.(head) do_map(tail, fun, [result | acc]) end end M.map([], fn x -> x * x end) # => [] M.map([1, 2, 3], fn x -> x * x end) # => [1, 4, 9]
  • 94.
  • 95.
    # 3. dedup [1,1, 1, 2, 3, 3]
  • 96.
    # 3. dedup [1,1, 1, 2, 3, 3] => [{3, 1}, 2, {2, 3}]
  • 97.
  • 98.
    # 3. dedup 1 1 13 list acc
  • 99.
    # 3. dedup 1 {2,1} 3 list acc
  • 100.
    # 3. dedup {3,1} 3 list acc
  • 101.
    # 3. dedup listacc 3 {3, 1} reversed
  • 102.
    # 3. dedup defmoduleD do end D.dedup([1, 2, 3, 4]) # => [1, 2, 3, 4] D.dedup([1, 1, 1, 2, 3, 3]) # => [{3, 1}, 2, {2, 3}]
  • 103.
    # 3. dedup defmoduleD do def dedup(list), do: do_dedup(list, []) end D.dedup([1, 2, 3, 4]) # => [1, 2, 3, 4] D.dedup([1, 1, 1, 2, 3, 3]) # => [{3, 1}, 2, {2, 3}]
  • 104.
    # 3. dedup defmoduleD do def dedup(list), do: do_dedup(list, []) defp do_dedup([], acc), do: Enum.reverse(acc) end D.dedup([1, 2, 3, 4]) # => [1, 2, 3, 4] D.dedup([1, 1, 1, 2, 3, 3]) # => [{3, 1}, 2, {2, 3}]
  • 105.
    # 3. dedup defmoduleD do def dedup(list), do: do_dedup(list, []) defp do_dedup([], acc), do: Enum.reverse(acc) defp do_dedup([num, num | tail], acc) do do_dedup(tail, [{2, num} | acc]) end end D.dedup([1, 2, 3, 4]) # => [1, 2, 3, 4] D.dedup([1, 1, 1, 2, 3, 3]) # => [{3, 1}, 2, {2, 3}]
  • 106.
    # 3. dedup defmoduleD do def dedup(list), do: do_dedup(list, []) defp do_dedup([], acc), do: Enum.reverse(acc) defp do_dedup([num | tail], [{cnt, num} | acc_tail]) do do_dedup(tail, [{cnt + 1, num} | acc_tail]) end defp do_dedup([num, num | tail], acc) do do_dedup(tail, [{2, num} | acc]) end end D.dedup([1, 2, 3, 4]) # => [1, 2, 3, 4] D.dedup([1, 1, 1, 2, 3, 3]) # => [{3, 1}, 2, {2, 3}]
  • 107.
    # 3. dedup defmoduleD do def dedup(list), do: do_dedup(list, []) defp do_dedup([], acc), do: Enum.reverse(acc) defp do_dedup([num | tail], [{cnt, num} | acc_tail]) do do_dedup(tail, [{cnt + 1, num} | acc_tail]) end defp do_dedup([num, num | tail], acc) do do_dedup(tail, [{2, num} | acc]) end defp do_dedup([head | tail], acc), do: do_dedup(tail, [head | acc]) end D.dedup([1, 2, 3, 4]) # => [1, 2, 3, 4] D.dedup([1, 1, 1, 2, 3, 3]) # => [{3, 1}, 2, {2, 3}]
  • 108.
    # 3. dedup defmoduleD do def dedup(list), do: do_dedup(list, []) defp do_dedup([], acc), do: Enum.reverse(acc) defp do_dedup([num | tail], [{cnt, num} | acc_tail]) do do_dedup(tail, [{cnt + 1, num} | acc_tail]) end defp do_dedup([num, num | tail], acc) do do_dedup(tail, [{2, num} | acc]) end defp do_dedup([head | tail], acc), do: do_dedup(tail, [head | acc]) end D.dedup([1, 2, 3, 4]) # => [1, 2, 3, 4] D.dedup([1, 1, 1, 2, 3, 3]) # => [{3, 1}, 2, {2, 3}]
  • 110.
    # 4. 5most common words - eager approach
  • 111.
    # 4. 5most common words - eager approach defmodule Eager do def answer do File.read!("moby.txt") |> String.split("n") |> Enum.map(%String.trim/1) |> Enum.filter(&(&1 != "")) |> Enum.flat_map(&String.split/1) |> Enum.filter(&(String.length(&1) > 3)) |> Enum.reduce(%{}, fn word, words -> Map.update(words, word, 1, fn curr -> curr + 1 end) end) |> Enum.sort_by(fn {_word, count} -> count end, &>=/2) |> Enum.take(5) end end Eager.answer() # => [{"that", 2692}, {"with", 1695}, {"this", 1169}, {"from", 1072}, {"have", 752}]
  • 112.
    # 5. 5most common words - lazy approach defmodule Lazy do def answer do File.stream!("moby.txt") |> Stream.map(&String.trim/1) |> Stream.filter(&(&1 != "")) |> Stream.flat_map(&String.split/1) |> Stream.filter(&(String.length(&1) > 3)) |> Enum.reduce(%{}, fn word, words -> Map.update(words, word, 1, fn curr -> curr + 1 end) end) |> Enum.sort_by(fn {_word, count} -> count end, &>=/2) |> Enum.take(5) end end Lazy.answer() # => [{"that", 2692}, {"with", 1695}, {"this", 1169}, {"from", 1072}, {"have", 752}]
  • 113.
    # Let’s talkabout `mix`
  • 150.
    # Let’s talkabout dependencies
  • 167.
    # Where togo next?
  • 168.
  • 169.
  • 170.
  • 171.
  • 172.
  • 173.
  • 174.
  • 175.