marp | headingDivider | style | slides |
---|---|---|---|
true |
3 |
section {
font-size: 1.75rem
}
|
Elixir's syntax essentials for Python tourists
Help pythonistas navigate, read and understand Elixir codebases.
- can be used as lookup reference, just like a vocabulary
- each section contains an Elixir syntax feature and its python translation*
- provides links to official documentation
* Warn: translations are closest as possible and therefore not idiomatic
- it's not a comprehensive tour of elixir features
- it's not a way to learn how to write Elixir code
- doesn't explain how things work in Elixir
- doesn't explain why Elixir is designed in such a way
If you are interested in those topics jump to "Further Readings" section
# this is a comment
"hello" # string
[1, 2, 3] # list
x = 1 # variable assignement
foo(bar) # function call
1 + 1 == 2 # comparison operators
not is_nil(x) and x > 0 # boolean operators
:hello
HELLO = "hello" # ~ global literal constant
Ref: https://hexdocs.pm/elixir/Atom.html
{1, "b", :c}
(1, "b", C)
Ref: https://hexdocs.pm/elixir/Tuple.html
[1, "b", :c] # linked list [1 -> ["b" -> [:c]]]
[1, "b", C] # array list
Ref: https://hexdocs.pm/elixir/List.html
x = [1, 2] ++ [3, 4] # (1) concatenate lists
y = [0 | x] # (2) prepend single element
x = [1, 2] + [3, 4] # (1)
y = [0] + x # (2)
Ref: https://hexdocs.pm/elixir/List.html
[{:a, 1}, {:b, 2}] # list of pairs (atom, value)
[a: 1, b: 2] # same as above with syntactic sugar
[("a", 1), ("b", 2)]
Ref: https://hexdocs.pm/elixir/Keyword.html
%{"a" => 1, "b" => 2} # (1) string keys
%{:a => 1, :b => 2} # (2) atom keys
%{a: 1, b: 2} # (3) same as (2) with syntactic sugar
{"a": 1, "b": 2} # (1), (2) and (3)
Ref: https://hexdocs.pm/elixir/Map.html
x = Map.put(%{a: 1, b: 2}, :c, 3) # (1) add/update an element
x = %{x | c: 3} # (2) update an element (syntactic sugar)
y = Map.take(x, [:a, :b]) # (3) take a subset
x = {**{"a": 1, "b": 2}, "c": 3} # (1) and (2)
y = {k: x[k] for k in x if k in ["a", "b"]} # (3)
Ref: https://hexdocs.pm/elixir/Map.html#summary
if x >= 0 do
:positive
else
:negative
end
if x >= 0, do: :positive, else: :negative # shorthand
"positive" if x >= 0 else "negative"
Ref: https://hexdocs.pm/elixir/Kernel.html#if/2
cond do # first true condition is entered
x > 9000 -> "over 9000"
x > 0 -> "meh"
_ -> "do you even lift bro" # default branch
end
if x > 9000:
return "over 9000"
if x > 0:
return "meh"
return "do you even lift bro"
Ref: https://hexdocs.pm/elixir/Kernel.SpecialForms.html#cond/1
{a, b, _} = {1, 2, 3} # (1) a = 1, b = 2, don't care about third elem.
[a, ^b] = [1, 2] # (2) a = 1 if second element == value of b (pin)
[head | tail] = [1, 2, 3] # (3) head = 1, tail = [2, 3]
%{a: a} = %{a: 1, b: 2} # (4) a = 1 (~ destructuring)
(a, b, _) = (1, 2, 3) # (1)
[a, temp] = [1, 2]; if temp != b: raise Exception("Match error!") # (2)
head, tail = lst[0], lst[1:] # (3)
a = {"a": 1, "b": 2}["a"] # (4)
Ref: getting-started#pattern-matching
case result do # first pattern matched is entered
{:ok, x} when rem(x, 2) -> {:odd, x}
{:ok, x} -> {:even, x}
_ -> :error
end
if not isinstance(result, tuple): return "error"
if len(result) != 2: return "error"
if result[0] != "ok": return "error"
x = result[1]
return ("odd", x) if x % 2 else ("even", x)
Ref: https://hexdocs.pm/elixir/Kernel.SpecialForms.html#case/2
with {:ok, string} <- Map.fetch(map, :key),
{value, _} <- Integer.parse(string) do
value
else # first failed match is sent here
mismatch -> mismatch
end
if "key" not in map: return "error"
string = map["key"]
if not string.isnumeric(): return "error"
return int(string)
Ref: https://hexdocs.pm/elixir/Kernel.SpecialForms.html#with/1
def add(x, y) do # (1) named function
x + y # last expression is the return value
end
def add(x, y), do: x + y # (2) shorthand, same as (1)
defp add(x, y), do: x + y # (3) private function
def add(x, y): # (1) and (2)
return x + y
def _add(x, y): # (3)
return x + y
Ref: https://hexdocs.pm/elixir/Kernel.html#def/2
# function definition can be splitted in branches
def op(:add, x, y), do: x + y
def op(:sub, x, y), do: x - y
def op(name, _, _), do: raise "Unknown operation #{name}"
def op(name, x, y):
if name == "add":
return x + y
if name == "sub":
return x - y
raise Exception(f"Unknown operation {name}")
Ref: https://hexdocs.pm/elixir/Kernel.html#def/2
def increment(x, delta \\ 1), do: x + delta
def increment(x, delta = 1):
return x + delta
Ref: https://hexdocs.pm/elixir/Kernel.html#def/2
# named parameters are (often) passed as keywords
String.split("a,b,c", ",", [{:parts, 2}]) # (1)
String.split("a,b,c", ",", parts: 2) # (2) same as (1) syntactic sugar
"a,b,c".split(",", maxsplit=1) # (1) and (2)
Ref: https://hexdocs.pm/elixir/Kernel.html#def/2
add = fn a, b -> a + b end
add.(1, 2) # note the "."!
add = lambda x, y: x + y
add(1, 2)
Ref: https://hexdocs.pm/elixir/Kernel.SpecialForms.html#fn/1
add = &(&1 + &2) # (1) reference input parameters by positional index
add = &Kernel.+/2 # (2) reference an existing function (name / arity)
add_one = &Kernel.+(&1, 1) # (3) ~ partial application
add = lambda x, y: x + y # (1)
import operator
add = operator.add # (2)
add_one = lambda x: operator.add(x, 1) # (3)
Ref: https://hexdocs.pm/elixir/Kernel.SpecialForms.html#&/1
x # the previous expression
|> foo() # is passed as first parameter
|> bar(y) # of the next one
temp = foo(x)
bar(temp, y)
Ref: https://hexdocs.pm/elixir/Kernel.html#%7C%3E/2
Enum.map(lst, &(&1 * &1)) # (1)
Enum.filter(lst, &(&1 > 0)) # (2)
[x * x for x in lst] # (1)
[x for x in lst if x > 0] # (2)
Ref: https://hexdocs.pm/elixir/Enum.html
Enum.reduce(lst, 0, fn x, acc -> x + acc end)
acc = 0
for x in lst:
acc = x + acc
Ref: https://hexdocs.pm/elixir/Enum.html
defmodule Foo.Bar do # in file foo/bar.ex
alias Other.Baz # allows to refer to Baz without "Other."
@delta 1 # define constant delta = 1
def increment(x), do: Baz.f(x) + @delta
end
# in file foo/bar.py
from other import baz
DELTA = 1
def increment(x):
return baz.f(x) + DELTA
Ref: https://hexdocs.pm/elixir/Kernel.html#defmodule/2
defmodule User do
defstruct [:name, :age]
end
john = %User{name: "John"} # basically equivalent to a map
class User:
def __init__(self, name=None, age=None):
self.name = name
self.age = age
john = User(name="John")
Ref: https://hexdocs.pm/elixir/Kernel.html#defstruct/1
@type name :: String.t() | nil
@spec greet(name()) :: String.t() | :error
def greet(nil), do: :error
def greet(name), do: "Hello #{name}"
Name = Optional[str]
def greet(name: Name) -> Union[str, Literal["error"]]:
return f"Hello {name}" if name is not None else "error"