LiveBook, functions, modules, pattern matching, maps & structs
LiveBook
- https://livebook.dev/
- https://github.com/livebook-dev/livebook
- Livebook v0.5: flowcharts, chat apps, multiplayer games, and custom widgets!
- https://github.com/livebook-dev/kino
- https://mermaid-js.github.io/mermaid/#
asdf: Extendable version manager with support for Ruby, Node.js, Elixir, Erlang & more
- https://github.com/asdf-vm/asdf
- https://asdf-vm.com/guide/getting-started.html
- http://asdf-vm.com/manage/commands.html
- Linux + Mac… NOT Windows
It’s Markdown
It’s markdown, so you can…
italicize or bold text or even bold and italicize text
Mark text as code
Create links:
Create bullet lists:
> And create blockquotes > > > And nest blockquotes, like this
> You can put bullet lists inside blockquotes: > > #1 > #2
Create code blocks:
10 PRINT "Hello, World"
20 GOTO 10
and
Because it’s Markdown, you can also view it as a text file.
THIS file is viewable at (the 2nd link being a raw file .livemd):
I’ve made use of these being Markdown files to diff version1.livemd version2.livemd
They also work great with version control systems, like Git.
Livebook.dev
Mermaid-JS
https://mermaid-js.github.io/mermaid/#/flowchart
flowchart TD
A[Start] --> B{Is it?};
B -->|Yes| C[OK];
C --> D[Rethink];
D --> B;
B ---->|No| E[End];
https://mermaid-js.github.io/mermaid/#/sequenceDiagram
sequenceDiagram
par Alice to Bob
Alice->>Bob: Hello guys!
and Alice to John
Alice->>John: Hello guys!
end
Bob-->>Alice: Hi Alice!
John-->>Alice: Hi Alice!
https://mermaid-js.github.io/mermaid/#/entityRelationshipDiagram
erDiagram
CUSTOMER ||--o{ ORDER : places
CUSTOMER {
string name
string custNumber
string sector
}
ORDER ||--|{ LINE-ITEM : contains
ORDER {
int orderNumber
string deliveryAddress
}
LINE-ITEM {
string productCode
int quantity
float pricePerUnit
}
Show keyboard shortcuts
Anonymous functions
- https://elixirschool.com/en/lessons/basics/functions/
- https://elixir-lang.org/getting-started/modules-and-functions.html
- https://elixircasts.io/intro-to-elixir-functions
- https://devato.com/post/getting-started-with-elixir-functions
fn num -> num + 7 end
(fn num -> num + 7 end).(10)
&(&1 + 7)
(&(&1 + 7)).(10)
(fn a, b, c -> a + b + c end).(30, 30, 40)
(&(&1 + &2 + &3)).(300, 300, 400)
add_7 = fn num -> num + 7 end
add_7.(8)
another_add_7 = &(&1 + 7)
another_add_7.(8)
Modules & named functions
defmodule MyModule do
end
A module’s name is its identity. Module name does NOT need to conform to any specific file name, directory hierarchy, or namespacing convention!
defmodule MyModule do
def add_2(num), do: 2 + num
def add_3(num) do
3 + num
end
def add_3(num, num2) do
3 + num + num2
end
end
MyModule.add_2(4)
MyModule.add_3(9)
alias MyModule, as: M
M.add_3(5)
import MyModule
add_3(7)
The pipeline operator, |>
7 |> add_3() |> add_2()
7 |> (&add_3/1).() |> (&add_2/1).()
7 |> (&M.add_3/1).() |> (&M.add_2/1).()
7 |> (&MyModule.add_3/1).() |> (&MyModule.add_2/1).()
7 |> (&(&1 + 3)).() |> (&(&1 + 2)).()
30 |> (&M.add_3/2).(20)
7
|> (&(&1 + 3)).()
|> (&(&1 + 2)).()
7
|> add_3()
|> add_2()
Multi-clause functions with pattern-matching
# Greeting.greet/1 has three function clauses
defmodule Greeting do
def greet(:professor_williams), do: "Hello, Professor Williams"
def greet(:mom), do: "Hi, Mom!"
def greet(:best_friend), do: "Yo! Wassup?!?!"
def greet(something_else), do: "Sorry, I don't know you."
end
alias Greeting, as: G
G.greet(:professor_williams) |> IO.inspect()
G.greet(:mom) |> IO.inspect()
G.greet(:best_friend) |> IO.inspect()
G.greet("Yo, wassup?") |> IO.inspect()
Guard Clauses
defmodule MyPrint do
def print(thing) when is_binary(thing) do
thing |> IO.puts()
end
def print(thing) when is_atom(thing) do
thing |> Atom.to_string() |> IO.puts()
end
def print(thing) when is_map(thing) do
# thing |> Enum.map_join(", ", fn {k, v} -> "#{k}, #{v}" end) |> IO.puts()
thing |> Enum.map(fn {k, v} -> "#{k}, #{v}" end) |> Enum.join(" ,") |> IO.puts()
end
end
MyPrint.print("Yay!")
MyPrint.print(:Hooray!)
MyPrint.print(%{Hello: "world"})
Maps & Structs
defmodule CelticsPlayers do
def bird, do: %{fname: "Larry", lname: "Bird", number: 33, position: :forward}
def parish, do: %{fname: "Robert", lname: "Parish", number: 0, position: :center}
def ainge, do: %{fname: "Danny", lname: "Ainge", number: 44, position: :guard}
def johnson, do: %{fname: "Dennis", lname: "Johnson", number: 3, position: :guard}
def mchale, do: %{fname: "Kevin", lname: "McHale", number: 32, position: :forward}
def walton, do: %{fname: "Bill", lname: "Walton", number: 5, position: :center}
def players, do: [bird(), parish(), ainge(), johnson(), mchale(), walton()]
end
CelticsPlayers.bird()
CelticsPlayers.players()
defmodule Celtics do
import CelticsPlayers
def team,
do: %{
forwards: players() |> Enum.filter(&(&1.position == :forward)),
centers: players() |> Enum.filter(&(&1.position == :center)),
guards: players() |> Enum.filter(&(&1.position == :guard))
}
end
Celtics.team()
# https://elixir-lang.org/getting-started/structs.html
# https://elixircasts.io/intro-to-structs
defmodule Player do
@enforce_keys [:fname, :lname, :number, :position]
defstruct [:fname, :lname, :number, :position, :team]
end
Player.__struct__()
Player.__struct__().__struct__()
defimpl Inspect, for: Player do
def inspect(player, _opts) do
""
end
end
defmodule StructCeltics do
import CelticsPlayers
# @players players()
@struct_players players() |> Enum.map(&struct(Player, &1))
def team,
do: %{
forwards: @struct_players |> Enum.filter(&(&1.position == :forward)),
centers: @struct_players |> Enum.filter(&(&1.position == :center)),
guards: @struct_players |> Enum.filter(&(&1.position == :guard))
}
end
StructCeltics.team()
# This will fail because bob is missing keys required by the Player struct
bob = %Player{fname: "Bob"}
# https://hexdocs.pm/elixir/1.13/Map.html
defmodule StructCeltics do
import CelticsPlayers
# @struct_players players() |> Enum.map(&(struct(Player, &1) |> Map.put(:team, :celtics)))
@struct_players players()
|> Enum.map(&struct(Player, &1))
|> Enum.map(&Map.put(&1, :team, :celtics))
def team,
do: %{
forwards: @struct_players |> Enum.filter(&(&1.position == :forward)),
centers: @struct_players |> Enum.filter(&(&1.position == :center)),
guards: @struct_players |> Enum.filter(&(&1.position == :guard))
}
end
# StructCeltics.team() |> inspect(structs: false)
StructCeltics.team() |> inspect()
# StructCeltics.team() |> IO.inspect([{:structs, false}])
StructCeltics.team() |> IO.inspect(structs: false)
Testing
ExUnit.start(autorun: false)
defmodule TestCeltics do
use ExUnit.Case, async: true
require StructCeltics
test "McHale is an '85-86 Celtic" do
celtics = StructCeltics.team()
players = celtics[:forwards] ++ celtics[:centers] ++ celtics[:guards]
# players |> IO.inspect(structs: false)
# TEST FAILS:
# kevin = %Player{lname: "McHale", fname: "Kevin", number: 32, position: :forward}
# TEST PASSES:
kevin = %Player{
lname: "McHale",
fname: "Kevin",
number: 32,
position: :forward,
team: :celtics
}
# kevin |> IO.inspect(structs: false)
assert kevin in players
# refute kevin not in players
end
end
ExUnit.run()
Mix.install([{:recursive_selective_match, "~> 0.2.6"}])
ExUnit.start(autorun: false)
defmodule TestCeltics2 do
use ExUnit.Case, async: true
require StructCeltics
alias RecursiveSelectiveMatch, as: RSM
test "McHale is an '85-86 Celtic" do
celtics = StructCeltics.team()
players = celtics[:forwards] ++ celtics[:centers] ++ celtics[:guards]
kevin = %Player{lname: "McHale", fname: "Kevin", number: 32, position: :forward}
# kevin = %Player{
# lname: "McHale",
# fname: "Kevin",
# number: 32,
# position: :forward,
# team: :celtics
# }
assert RSM.includes?(kevin, players)
end
end
ExUnit.run()