Elixir
Homepage / Notes / Computer Science / Programming Languages / Elixir
Language Features
Numbers
1 + 12
Ranges
1..10 |> Enum.map(&(&1))[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
Stepped range
Starting from Elixir 1.12:
1..10//2 |> Enum.map(&(&1))[1, 3, 5, 7, 9]
Parse int from string
"1" |> String.to_integer1
Parse int to string
1 |> Integer.to_string"1"
Strings
Interpolation
name = "world"
"Hello, #{name}!""Hello, world!"
Concatenation
"This is a " <> "concatenated string""This is a concatenated string"
Split
String.split("header1,header2,header3", ",")["header1", "header2", "header3"]
Parts
String.split("header1,header2,header3", ",", parts: 2)["header1", "header2,header3"]
Ends With?
String.ends_with?("bonjour", "jour")true
String.ends_with?("bonsoir", "jour")false
Multiline string
multiline = """
this is a
multiline
string
""""this is a\nmultiline\nstring\n"
Replace
s = "2021-10-28T08:55:01"
s |> String.replace("T", " ")"2021-10-28 08:55:01"
Contains?
String.contains?("key=value", "=")true
String.contains?("key=value", "x")false
String.contains?("key=value", ["key", "value"])true
String.contains?("key=value", ["key", "xyz"])true
String.contains?("key=value", ["abc", "xyz"])false
Tuples
Tuples:
- can hold any value
- store elements contiguously in memory
{:ok, "hello"}tuple_size {:ok, "hello"}2
0-indexed
elem({:ok, "hello"}, 1)"hello"
put_elem({:ok, "hello"}, 1, "world"){:ok, "world"}
Lists / Enums
Access element by index
["a", "b", "c"] |> Enum.at(1)"b"
Length
[1, 2, 3] |> length3
Prepending
list = [1, 2, 3]
[0 | list][0, 1, 2, 3]
Appending
list = [1, 2, 3]
list ++ [4][1, 2, 3, 4]
Concatenation
[1, 2] ++ [3, 4][1, 2, 3, 4]
Head / Tail
hd(["a", "b", "c", "d", "e"])"a"
tl(["a", "b", "c", "d", "e"])["b", "c", "d", "e"]
[head | tail] = ["a", "b", "c", "d", "e"]["a", "b", "c", "d", "e"]
head"a"
tail["b", "c", "d", "e"]
First element
["a", "b", "c"] |> List.first"a"
Last element
["a", "b", "c"] |> List.last"c"
Element in List?
["a", "b", "c"] |> Enum.member?("c")true
["a", "b", "c"] |> Enum.member?("d")false
Delete element at index
["a", "b", "c"] |> List.delete_at(1)["a", "c"]
Empty?
[1, 2, 3] |> Enum.empty?false
[] |> Enum.empty?true
Filter
Enum.filter([1, 2, 3, 4], fn(x) -> x > 2 end)[3, 4]
Reject
The "inverse" of Filter
Enum.reject([1, 2, 3, 4], fn(x) -> x > 2 end)[1, 2]
Sort
Enum.sort([4, 2, 3, 1])[1, 2, 3, 4]
Enum.sort([4, 2, 3, 1], :desc)[4, 3, 2, 1]
Sort by
Enum.sort_by([%{id: 1, value: "Georges"}, %{id: 3, value: "Damien"}, %{id: 2, value: "Jacques"}], &(&1.id), :asc)[
%{id: 1, value: "Georges"},
%{id: 2, value: "Jacques"},
%{id: 3, value: "Damien"}
]
Uniq
Enum.uniq([1, 1, 2, 2, 2, 3])[1, 2, 3]
Map
Enum.map([1, 2, 3], fn(x) -> x*2 end)[2, 4, 6]
Reduce
Enum.reduce([1, 2, 3], 0, fn(x, acc) -> x + acc end)6
With index
/!\ Elixir v1.12 only
Enum.with_index(["a", "b", "c"], fn(x, index) -> {index, x} end)[{0, "a"}, {1, "b"}, {2, "c"}]
Take
Return first nth elements of list
[1, 2, 3, 4, 5] |> Enum.take(3)[1, 2, 3]
Works with negative numbers too to return last nth elements
[1, 2, 3, 4, 5] |> Enum.take(-2)[4, 5]
Drop
Return list except n first elements
[1, 2, 3] |> Enum.drop(1)[2, 3]
[1, 2, 3] |> Enum.drop(2)[3]
Join
[1, 2, 3, 4, 5] |> Enum.join(";")"1;2;3;4;5"
Find
[%{id: 1, name: "a"}, %{id: 2, name: "b"}, %{id: 3, name: "c"}] |> Enum.find(&(&1.id == 3))%{id: 3, name: "c"}
Maps
Fetch value, returns {:ok, value} or :error if key is not in map:
map = %{:foo => "bar"}
Map.fetch(map, :foo){:ok, "bar"}
map = %{:foo => "bar"}
Map.fetch(map, :fooz):error
To directly get the value, without error handling:
map = %{:foo => "bar"}
Map.get(map, :foo)"bar"
Syntactic sugar:
map = %{:foo => "bar"}
map[:foo]"bar"
To provide a default in case key is not present in map:
map = %{:foo => "bar"}
Map.get(map, :bar, "default value")"default value"
putin
user = %{name: "John", age: 28}
put_in(user.age, 29)%{age: 29, name: "John"}
Keys
map = %{"key1" => "value1", "key2" => "value2"}
map |> Map.keys["key1", "key2"]
Put (add new key/value)
map = %{"key1" => "value1", "key2" => "value2"}
map |> Map.put("key3", "value3")%{"key1" => "value1", "key2" => "value2", "key3" => "value3"}
Update existing key to new value
map = %{"key1" => "value1", "key2" => "value2"}
%{map | "key2" => "new value 2"}%{"key1" => "value1", "key2" => "new value 2"}
Delete / Drop
Delete a single key:
map = %{"key1" => "value1", "key2" => "value2"}
map |> Map.delete("key2")%{"key1" => "value1"}
Drop a list of keys:
map = %{"key1" => "value1", "key2" => "value2", "key3" => "value3"}
map |> Map.drop(["key1", "key2"])%{"key3" => "value3"}
Functions
Named Functions
Regular
Must be defined with def inside a module
defmodule Greeter do
def hello(name) do
"Hello, #{name}!"
end
end
Greeter.hello("Damien")"Hello, Damien!"
One-liner
defmodule ShortGreeter do
def hello(name), do: "Hello, #{name}!"
end
ShortGreeter.hello("Damien")"Hello, Damien!"
Function Naming and Arity
Functions are defined by name AND arity (number of parameters)
defmodule Greeter2 do
def hello(), do: "Hello, anonymous!"
def hello(name), do: "Hello, #{name}!"
def hello(name1, name2), do: "Hello, #{name1} and #{name2}!"
endGreeter2.hello()"Hello, anonymous!"
Greeter2.hello("Damien")"Hello, Damien!"
Greeter2.hello("Damien", "Jean-Jacques")"Hello, Damien and Jean-Jacques!"
Private Functions
https://elixirschool.com/en/lessons/basics/functions/#private-functions
Guards
https://elixirschool.com/en/lessons/basics/functions/#guards
defmodule Guards do
def combine(x, y) when is_number(x) and is_number(y), do: x + y
def combine(x, y), do: x <> y
end
{Guards.combine(4, 5), Guards.combine("abc", "xyz")}{9, "abcxyz"}
Default Arguments
https://elixirschool.com/en/lessons/basics/functions/#default-arguments
Anonymous Functions
sum = fn(a, b) -> a + b end
sum.(2, 3)5
Using the & shorthand:
sum = &(&1 + &2)
sum.(2, 3)5
https://blog.lelonek.me/3-tricks-of-anonymous-elixir-functions-a81d1b1e049c
If/else, cond
No elseif!
if
if true do
"this is true"
else
"this is false"
end"this is true"
unless
unless false do
"this is true"
else
"this is false"
end"this is true"
cond
cond do
1 == 1 -> true
1 == 2 -> false
true -> "else"
endtrue
cond do
1 == 0 -> true
1 == 2 -> false
true -> "else"
end"else"
Pipe Operator
[1, 2, 3] |> Enum.map(&(&1*2))[2, 4, 6]
"Elixir rocks" |> String.upcase() |> String.split()["ELIXIR", "ROCKS"]
Tap & Then
Starting from Elixir 1.12: tap passes value to fun and returns value Useful for functions that have side-effects but you still want to pass down the value down the pipe (or for logging for example)
then passes value to fun, basically invokes fun with value as an argument, meaning you can place it as 2nd or 3rd argumentβ¦
Here in that example, we're able to pipe down a string and log it using tap, then use Regex.scan with the string as the 2nd (not first) argument of the function
"hello world"
|> tap(&IO.puts/1)
|> then(&Regex.scan(~r/\w+/, &1))hello world
[["hello"], ["world"]]
Debugging
IO.inspect
IO.inspect can be piped!
[1, 2, 3]
|> IO.inspect
|> Enum.map(&(&1*2))
|> IO.inspect
|> Enum.sum[1, 2, 3]
[2, 4, 6]
12
Outputs can labeled
[1, 2, 3]
|> IO.inspect(label: "initial list")
|> Enum.map(&(&1*2))
|> IO.inspect(label: "after x2")
|> Enum.suminitial list: [1, 2, 3]
after x2: [2, 4, 6]
12
Kernel.dbg
Starting from Elixir 1.14
elixir -e '[1, 2, 3] |> Enum.map(&(&1*2)) |> dbg() |> Enum.sum |> dbg()'[nofile:1: (file)]
value #=> [2, 4, 6]
[nofile:1: (file)]
[1, 2, 3] #=> [1, 2, 3]
|> Enum.map(&(&1 * 2)) #=> [2, 4, 6]
|> dbg() #=> [2, 4, 6]
|> Enum.sum() #=> 12
Pattern Matching
https://elixirschool.com/en/lessons/basics/pattern-matching/
case {:ok, "Hello World"} do
{:ok, result} -> result
{:error, _} -> "Uh oh!"
_ -> "Catch all"
end"Hello World"
Using Guards
case 9 do
n when n > 5 -> "over than 5"
_ -> "anything else"
end"over than 5"
case 4 do
n when n > 5 -> "over than 5"
_ -> "anything else"
end"anything else"
Comprehensions
list = [1, 2, 3, 4, 5]
for x <- list, do: x*x[1, 4, 9, 16, 25]
Packages
Starting from Elixir 1.12:
IO.puts(Jason.encode!(%{hello: :world}))** (UndefinedFunctionError) function Jason.encode!/1 is undefined (module Jason is not available)
Jason.encode!(%{hello: :world})
Can install required packages using Mix.install()
Mix.install([:jason])
IO.puts(Jason.encode!(%{hello: :world})){"hello":"world"}
:ok
JSON Parsing
Mix.install([:jason])
"{\"key\": \"value\"}" |> Jason.decode!%{"key" => "value"}
Mix
Dependencies manager!
- Create a new project:
mix new example - Interactive shell:
iex -S mix - Compile project:
mix compile - Fetch dependencies:
mix deps.get
Ecto
https://elixirschool.com/en/lessons/ecto/basics/
Query
Preload
user |> Repo.preload(:integrations)
Query Fragment
https://hexdocs.pm/ecto/Ecto.Query.API.html#fragment/1
Migrations
Process migrations: mix ecto.migrate Rollback previous migration: mix ecto.rollback Rollback all migrations: mix ecto.rollback --all
Resources
https://bartoszgorka.com/dynamic-queries-in-ecto
Gettext
https://hexdocs.pm/gettext/Gettext.html Manages translations for i18n
Attributes
https://hexdocs.pm/elixir/master/Module.html
@impl
@impl notes that the function is a callback method https://elixir-lang.org/blog/2017/07/25/elixir-v1-5-0-released/ https://elixircasts.io/%40impl-attribute
Testing
https://elixirschool.com/en/lessons/basics/testing/
ExUnit
https://hexdocs.pm/ex_unit/1.12/ExUnit.html
Doctests
https://inquisitivedeveloper.com/lwm-elixir-66/ https://elixir-lang.org/getting-started/mix-otp/docs-tests-and-with.html
Mox
https://elixirschool.com/en/lessons/testing/mox
Agents
https://elixir-lang.org/getting-started/mix-otp/agent.html
Documentation (ExDoc)
https://github.com/elixir-lang/ex_doc
ExDoc is a tool to generate documentation for your Elixir projects.
Cheatsheets
https://hexdocs.pm/ex_doc/cheatsheet.html
Binaries
Strings are binary!
string = "hello"
is_binary(string)true
Code Points
Reveal a character code point:
?a97
In hexadecimal:
0x006197
String.codepoints("π¨βπ»")["π¨", "β", "π»"]
String.graphemes("π¨βπ»")["π¨βπ»"]
Bitstrings
IO.inspect("abc", binaries: :as_binaries)<<97, 98, 99>>
"abc"
Binaries
A binary is a bitstring where the number of bits is divisible by 8.
Elixir Script
Elixir can be used for scripting. Script files have a .exs file extension. Recommended for small tasks that don't require a Mix project.
Phoenix
Directory Structure
https://hexdocs.pm/phoenix/directory_structure.html
_build- everything that's compiled. Exclude from version control.assets- everything related to front-end (CSS, JSβ¦)config- holds project configuration, withconfig.exsbeing the main entry point anddev.exs,prod.exsbeing for environment specific configdeps- holds all the Mix dependencies. Exclude from version control.lib- the juicy part! All the application source code.lib/project_nameholds all the "backend" stuff like interaction with database (the "M" in MVC), whilelib/project_name_webis for the "frontend" ("V" and "C" in MVC).priv- stuff that's necessary for prod but not part of the source code, like database scriptstest- all the tests
Mix Commands
Start the Phoenix app: mix phx.server Run the Phoenix app inside IEx: iex -S mix phx.server
Adding a new route
https://hexdocs.pm/phoenix/request_lifecycle.html#content
Views
https://hexdocs.pm/phoenix/views.html
Presence
https://hexdocs.pm/phoenix/Phoenix.Presence.html
Deployment
https://hexdocs.pm/phoenix/deployment.html
Libraries
LiveView
https://github.com/phoenixframework/phoenix_live_view Send HTML over the wire
https://hexdocs.pm/phoenix_live_view/Phoenix.LiveView.html
Forms
https://hexdocs.pm/phoenix_live_view/form-bindings.html
Surface
https://github.com/surface-ui/surface
JS
JS trick: https://mobile.twitter.com/josevalim/status/1486996785914650625
React in LiveView
https://stephenbussey.com/2022/04/13/react-in-liveview-how-and-why.html
phx.gen.auth
https://github.com/aaronrenner/phx_gen_auth Will be built in Phoenix starting from 1.6
No emails are actually sent on sign up and password reset, have to built that ourselves
Tailwind
https://fly.io/phoenix-files/tailwind-standalone/
Resources
File upload tutorial: https://www.yodiw.com/simple-upload-file-local-phoenix-elixir/
esbuild
https://www.mitchellhanberg.com/how-i-handle-static-assets-in-my-phoenix-apps/ https://cloudless.studio/wrapping-your-head-around-assets-in-phoenix-1-6
Petal
Packages
Oban
https://github.com/sorentwo/oban
Oban is a robust job processing library which uses PostgreSQL for storage and coordination.
Nx
https://github.com/elixir-nx/nx/tree/main/nx
Nx is a multi-dimensional tensors library for Elixir with multi-staged compilation to the CPU/GPU.
Mix.install([{:nx, github: "elixir-nx/nx", branch: "main", sparse: "nx"}])
t = Nx.tensor([[1, 2], [3, 4]])
Nx.shape(t){2, 2}
Nx.sum(Nx.tensor([1, 2, 3]))#Nx.Tensor<
s64
6
>
Nx.mean(Nx.tensor([1, 2, 3]))#Nx.Tensor<
f32
2.0
>
Nx.add(Nx.tensor([10, 10, 10]), Nx.tensor([100, 200, 300]))#Nx.Tensor<
s64[3]
[110, 210, 310]
>
Axon
https://github.com/elixir-nx/axon
Nx-powered Neural Networks
Resources
https://seanmoriarity.com/2021/04/08/axon-deep-learning-in-elixir/
Livebook
https://github.com/elixir-nx/livebook
Similar to Jupyter notebooks but for Elixir
Explorer
https://github.com/elixir-nx/explorer
Series (one-dimensional) and dataframes (two-dimensional) for fast data exploration in Elixir
GenServers
https://papercups.io/blog/genserver
Bamboo (Email library)
https://github.com/thoughtbot/bamboo
Testable, composable, and adapter based Elixir email library for devs that love piping.
Delta
https://github.com/slab/delta-elixir
Simple yet expressive format to describe documents' contents and changes
Broadway
https://github.com/dashbitco/broadway
Concurrent and multi-stage data ingestion and data processing with Elixir
Lumen
https://getlumen.org/ An alternative BEAM implementation, designed for WebAssembly => Elixir on the front-end?
Burrito
https://github.com/burrito-elixir/burrito
Wrap your application in a BEAM Burrito!
BEAM wrapped in Zig
Witchcraft
https://github.com/witchcrafters/witchcraft
Monads and other dark magic for Elixir
https://blog.appsignal.com/2022/02/08/functional-programming-in-elixir-with-witchcraft.html
Temple
https://github.com/mhanberg/temple
An HTML DSL for Elixir and Phoenix
Wallaby
https://github.com/elixir-wallaby/wallaby
Concurrent browser tests for your Elixir web apps
Ash Framework
Build APIs in minutes. Ash is a declarative, resource-oriented application framework for Elixir.
Ash Authentication
https://alembic.com.au/blog/announcing-ash-authentication
Ash Authentication is a drop-in authentication solution for users of the Ash framework who want to provide password-based or social sign-in via OAuth 2.0.
Cobblestone
https://github.com/doomspork/cobblestone
A better path to data
Bumblebee
https://github.com/elixir-nx/bumblebee
Bumblebee provides pre-trained and transformer Neural Network models on top of Axon. It includes integration with π€ Models, allowing anyone to download and perform Machine Learning tasks with few lines of code.
Dune
https://github.com/functional-rewire/dune
A sandbox for Elixir to safely evaluate untrusted code from user input.
Enumancer
https://github.com/sabiwara/enumancer
Elixir macros to effortlessly define highly optimized Enum pipelines.
Resources
Elixir School
Elixir Casts
Stripe library
https://github.com/code-corps/stripity_stripe
Blog about Elixir
An Introduction to Metaprogramming in Elixir
https://blog.appsignal.com/2021/09/07/an-introduction-to-metaprogramming-in-elixir.html
Fast Elixir
https://github.com/devonestes/fast-elixir
Tips for Improving Your Elixir Configuration
https://felt.com/blog/elixir-configuration