Homepage / Notes / Computer Science / Programming Languages / Elixir
1 + 1
2
1..10 |> Enum.map(&(&1))
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
Starting from Elixir 1.12:
1..10//2 |> Enum.map(&(&1))
[1, 3, 5, 7, 9]
"1" |> String.to_integer
1
1 |> Integer.to_string
"1"
= "world"
name "Hello, #{name}!"
"Hello, world!"
"This is a " <> "concatenated string"
"This is a concatenated string"
String.split("header1,header2,header3", ",")
["header1", "header2", "header3"]
String.split("header1,header2,header3", ",", parts: 2)
["header1", "header2,header3"]
String.ends_with?("bonjour", "jour")
true
String.ends_with?("bonsoir", "jour")
false
= """
multiline this is a
multiline
string
"""
"this is a\nmultiline\nstring\n"
= "2021-10-28T08:55:01"
s |> String.replace("T", " ") s
"2021-10-28 08:55:01"
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:
{:ok, "hello"}
{:ok, "hello"} tuple_size
2
0-indexed
({:ok, "hello"}, 1) elem
"hello"
({:ok, "hello"}, 1, "world") put_elem
{:ok, "world"}
["a", "b", "c"] |> Enum.at(1)
"b"
[1, 2, 3] |> length
3
= [1, 2, 3]
list [0 | list]
[0, 1, 2, 3]
= [1, 2, 3]
list ++ [4] list
[1, 2, 3, 4]
[1, 2] ++ [3, 4]
[1, 2, 3, 4]
(["a", "b", "c", "d", "e"]) hd
"a"
(["a", "b", "c", "d", "e"]) tl
["b", "c", "d", "e"]
[head | tail] = ["a", "b", "c", "d", "e"]
["a", "b", "c", "d", "e"]
head
"a"
tail
["b", "c", "d", "e"]
["a", "b", "c"] |> List.first
"a"
["a", "b", "c"] |> List.last
"c"
["a", "b", "c"] |> Enum.member?("c")
true
["a", "b", "c"] |> Enum.member?("d")
false
["a", "b", "c"] |> List.delete_at(1)
["a", "c"]
[1, 2, 3] |> Enum.empty?
false
[] |> Enum.empty?
true
Enum.filter([1, 2, 3, 4], fn(x) -> x > 2 end)
[3, 4]
The "inverse" of Filter
Enum.reject([1, 2, 3, 4], fn(x) -> x > 2 end)
[1, 2]
Enum.sort([4, 2, 3, 1])
[1, 2, 3, 4]
Enum.sort([4, 2, 3, 1], :desc)
[4, 3, 2, 1]
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"}
]
Enum.uniq([1, 1, 2, 2, 2, 3])
[1, 2, 3]
Enum.map([1, 2, 3], fn(x) -> x*2 end)
[2, 4, 6]
Enum.reduce([1, 2, 3], 0, fn(x, acc) -> x + acc end)
6
/!\ Elixir v1.12 only
Enum.with_index(["a", "b", "c"], fn(x, index) -> {index, x} end)
[{0, "a"}, {1, "b"}, {2, "c"}]
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]
Return list except n first elements
[1, 2, 3] |> Enum.drop(1)
[2, 3]
[1, 2, 3] |> Enum.drop(2)
[3]
[1, 2, 3, 4, 5] |> Enum.join(";")
"1;2;3;4;5"
[%{id: 1, name: "a"}, %{id: 2, name: "b"}, %{id: 3, name: "c"}] |> Enum.find(&(&1.id == 3))
%{id: 3, name: "c"}
Fetch value, returns {:ok, value}
or :error
if key
is not in map
:
= %{:foo => "bar"}
map Map.fetch(map, :foo)
{:ok, "bar"}
= %{:foo => "bar"}
map Map.fetch(map, :fooz)
:error
To directly get the value, without error handling:
= %{:foo => "bar"}
map Map.get(map, :foo)
"bar"
Syntactic sugar:
= %{:foo => "bar"}
map [:foo] map
"bar"
To provide a default in case key
is not present in map
:
= %{:foo => "bar"}
map Map.get(map, :bar, "default value")
"default value"
= %{name: "John", age: 28}
user (user.age, 29) put_in
%{age: 29, name: "John"}
= %{"key1" => "value1", "key2" => "value2"}
map |> Map.keys map
["key1", "key2"]
= %{"key1" => "value1", "key2" => "value2"}
map |> Map.put("key3", "value3") map
%{"key1" => "value1", "key2" => "value2", "key3" => "value3"}
= %{"key1" => "value1", "key2" => "value2"}
map {map | "key2" => "new value 2"} %
%{"key1" => "value1", "key2" => "new value 2"}
Delete a single key:
= %{"key1" => "value1", "key2" => "value2"}
map |> Map.delete("key2") map
%{"key1" => "value1"}
Drop a list of keys:
= %{"key1" => "value1", "key2" => "value2", "key3" => "value3"}
map |> Map.drop(["key1", "key2"]) map
%{"key3" => "value3"}
Must be defined with def
inside a module
defmodule Greeter do
def hello(name) do
"Hello, #{name}!"
end
end
Greeter.hello("Damien")
"Hello, Damien!"
defmodule ShortGreeter do
def hello(name), do: "Hello, #{name}!"
end
ShortGreeter.hello("Damien")
"Hello, Damien!"
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}!"
end
Greeter2.hello()
"Hello, anonymous!"
Greeter2.hello("Damien")
"Hello, Damien!"
Greeter2.hello("Damien", "Jean-Jacques")
"Hello, Damien and Jean-Jacques!"
https://elixirschool.com/en/lessons/basics/functions/#private-functions
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"}
https://elixirschool.com/en/lessons/basics/functions/#default-arguments
= fn(a, b) -> a + b end
sum .(2, 3) sum
5
Using the & shorthand:
= &(&1 + &2)
sum .(2, 3) sum
5
https://blog.lelonek.me/3-tricks-of-anonymous-elixir-functions-a81d1b1e049c
No elseif!
if true do
"this is true"
else
"this is false"
end
"this is true"
unless false do
"this is true"
else
"this is false"
end
"this is true"
cond do
1 == 1 -> true
1 == 2 -> false
true -> "else"
end
true
cond do
1 == 0 -> true
1 == 2 -> false
true -> "else"
end
"else"
[1, 2, 3] |> Enum.map(&(&1*2))
[2, 4, 6]
"Elixir rocks" |> String.upcase() |> String.split()
["ELIXIR", "ROCKS"]
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"]]
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.sum
initial list: [1, 2, 3]
after x2: [2, 4, 6]
12
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
https://elixirschool.com/en/lessons/basics/pattern-matching/
case {:ok, "Hello World"} do
{:ok, result} -> result
{:error, _} -> "Uh oh!"
-> "Catch all"
_ end
"Hello World"
case 9 do
when n > 5 -> "over than 5"
n -> "anything else"
_ end
"over than 5"
case 4 do
when n > 5 -> "over than 5"
n -> "anything else"
_ end
"anything else"
= [1, 2, 3, 4, 5]
list for x <- list, do: x*x
[1, 4, 9, 16, 25]
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
Mix.install([:jason])
"{\"key\": \"value\"}" |> Jason.decode!
%{"key" => "value"}
Dependencies manager!
mix new example
iex -S mix
mix compile
mix deps.get
https://elixirschool.com/en/lessons/ecto/basics/
user |> Repo.preload(:integrations)
https://hexdocs.pm/ecto/Ecto.Query.API.html#fragment/1
Process migrations: mix ecto.migrate
Rollback previous migration: mix ecto.rollback
Rollback all migrations: mix ecto.rollback --all
https://bartoszgorka.com/dynamic-queries-in-ecto
https://hexdocs.pm/gettext/Gettext.html Manages translations for i18n
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
https://elixirschool.com/en/lessons/basics/testing/
https://hexdocs.pm/ex_unit/1.12/ExUnit.html
https://inquisitivedeveloper.com/lwm-elixir-66/ https://elixir-lang.org/getting-started/mix-otp/docs-tests-and-with.html
https://elixirschool.com/en/lessons/testing/mox
https://elixir-lang.org/getting-started/mix-otp/agent.html
https://github.com/elixir-lang/ex_doc
ExDoc is a tool to generate documentation for your Elixir projects.
https://hexdocs.pm/ex_doc/cheatsheet.html
Strings are binary!
= "hello"
string (string) is_binary
true
Reveal a character code point:
?a
97
In hexadecimal:
0x0061
97
String.codepoints("π¨βπ»")
["π¨", "β", "π»"]
String.graphemes("π¨βπ»")
["π¨βπ»"]
IO.inspect("abc", binaries: :as_binaries)
<<97, 98, 99>>
"abc"
A binary is a bitstring where the number of bits is divisible by 8.
Elixir can be used for scripting. Script files have a .exs
file extension. Recommended for small tasks that don't require a Mix project.
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, with config.exs
being the main entry point and dev.exs
, prod.exs
being for environment specific configdeps
- holds all the Mix dependencies. Exclude from version control.lib
- the juicy part! All the application source code. lib/project_name
holds all the "backend" stuff like interaction with database (the "M" in MVC), while lib/project_name_web
is 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 testsStart the Phoenix app: mix phx.server
Run the Phoenix app inside IEx: iex -S mix phx.server
https://hexdocs.pm/phoenix/request_lifecycle.html#content
https://hexdocs.pm/phoenix/views.html
https://hexdocs.pm/phoenix/Phoenix.Presence.html
https://hexdocs.pm/phoenix/deployment.html
https://github.com/phoenixframework/phoenix_live_view Send HTML over the wire
https://hexdocs.pm/phoenix_live_view/Phoenix.LiveView.html
https://hexdocs.pm/phoenix_live_view/form-bindings.html
https://github.com/surface-ui/surface
JS trick: https://mobile.twitter.com/josevalim/status/1486996785914650625
https://stephenbussey.com/2022/04/13/react-in-liveview-how-and-why.html
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
https://fly.io/phoenix-files/tailwind-standalone/
File upload tutorial: https://www.yodiw.com/simple-upload-file-local-phoenix-elixir/
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
https://github.com/sorentwo/oban
Oban is a robust job processing library which uses PostgreSQL for storage and coordination.
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"}])
= Nx.tensor([[1, 2], [3, 4]])
t 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]
>
https://github.com/elixir-nx/axon
Nx-powered Neural Networks
https://seanmoriarity.com/2021/04/08/axon-deep-learning-in-elixir/
https://github.com/elixir-nx/livebook
Similar to Jupyter notebooks but for Elixir
https://github.com/elixir-nx/explorer
Series (one-dimensional) and dataframes (two-dimensional) for fast data exploration in Elixir
https://papercups.io/blog/genserver
https://github.com/thoughtbot/bamboo
Testable, composable, and adapter based Elixir email library for devs that love piping.
https://github.com/slab/delta-elixir
Simple yet expressive format to describe documents' contents and changes
https://github.com/dashbitco/broadway
Concurrent and multi-stage data ingestion and data processing with Elixir
https://getlumen.org/ An alternative BEAM implementation, designed for WebAssembly => Elixir on the front-end?
https://github.com/burrito-elixir/burrito
Wrap your application in a BEAM Burrito!
BEAM wrapped in Zig
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
https://github.com/mhanberg/temple
An HTML DSL for Elixir and Phoenix
https://github.com/elixir-wallaby/wallaby
Concurrent browser tests for your Elixir web apps
Build APIs in minutes. Ash is a declarative, resource-oriented application framework for Elixir.
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.
https://github.com/doomspork/cobblestone
A better path to data
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.
https://github.com/functional-rewire/dune
A sandbox for Elixir to safely evaluate untrusted code from user input.
https://github.com/sabiwara/enumancer
Elixir macros to effortlessly define highly optimized Enum pipelines.
https://github.com/code-corps/stripity_stripe
https://blog.appsignal.com/2021/09/07/an-introduction-to-metaprogramming-in-elixir.html
https://github.com/devonestes/fast-elixir
https://felt.com/blog/elixir-configuration