Created
December 25, 2018 22:34
-
-
Save cristian-sr/abec6c5f55cea079d9196cf219580276 to your computer and use it in GitHub Desktop.
Elixir
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Hello world | |
# hello.exs | |
defmodule Greeter do | |
def greet(name) do | |
message = "Hello, " <> name <> "!" | |
IO.puts message | |
end | |
end | |
Greeter.greet("world") | |
elixir hello.exs | |
# Hello, world! | |
Variables | |
age = 23 | |
Maps | |
user = %{ | |
name: "John", | |
city: "Melbourne" | |
} | |
IO.puts "Hello, " <> user.name | |
Lists | |
users = [ "Tom", "Dick", "Harry" ] | |
Enum.map(users, fn user -> | |
IO.puts "Hello " <> user | |
end) | |
Piping | |
source | |
|> transform(:hello) | |
|> print() | |
# Same as: | |
print(transform(source, :hello)) | |
These two are equivalent. | |
Pattern matching | |
user = %{name: "Tom", age: 23} | |
%{name: username} = user | |
This sets username to "Tom". | |
Pattern matching in functions | |
def greet(%{name: username}) do | |
IO.puts "Hello, " <> username | |
end | |
user = %{name: "Tom", age: 23} | |
Pattern matching works in function parameters too. | |
#Control flow | |
If | |
if false do | |
"This will never be seen" | |
else | |
"This will" | |
end | |
Case | |
case {1, 2, 3} do | |
{4, 5, 6} -> | |
"This clause won't match" | |
{1, x, 3} -> | |
"This will match and bind x to 2" | |
_ -> | |
"This will match any value" | |
end | |
Cond | |
cond do | |
1 + 1 == 3 -> | |
"I will never be seen" | |
2 * 5 == 12 -> | |
"Me neither" | |
true -> | |
"But I will (this is essentially an else)" | |
end | |
Errors | |
try do | |
throw(:hello) | |
catch | |
message -> "Got #{message}." | |
after | |
IO.puts("I'm the after clause.") | |
end | |
#Types | |
Primitives | |
nil Nil/null | |
true / false Boolean | |
?a Integer (ASCII) | |
23 Integer | |
3.14 Float | |
'hello' Charlist | |
<<2, 3>> Binary | |
"hello" Binary string | |
:hello Atom | |
[a, b] List | |
{a, b} Tuple | |
%{a: "hello"} Map | |
%MyStruct{a: "hello"} Struct | |
fn -> ... end Function | |
Type checks | |
is_atom/1 | |
is_bitstring/1 | |
is_boolean/1 | |
is_function/1 | |
is_function/2 | |
is_integer/1 | |
is_float/1 | |
is_binary/1 | |
is_list/1 | |
is_map/1 | |
is_tuple/1 | |
is_nil/1 | |
is_number/1 | |
is_pid/1 | |
is_port/1 | |
is_reference/1 | |
Operators | |
left != right # equal | |
left !== right # match | |
left ++ right # concat lists | |
left <> right # concat string/binary | |
left =~ right # regexp | |
#Modules | |
Importing | |
require Redux # compiles a module | |
import Redux # compiles, and you can use without the `Redux.` prefix | |
use Redux # compiles, and runs Redux.__using__/1 | |
use Redux, async: true | |
import Redux, only: [duplicate: 2] | |
import Redux, only: :functions | |
import Redux, only: :macros | |
import Foo.{Bar, Baz} | |
Aliases | |
alias Foo.Bar, as: Bar | |
alias Foo.Bar # same as above | |
alias Foo.{Bar, Baz} | |
#String | |
Functions | |
import String | |
str = "hello" | |
str |> length() # → 5 | |
str |> codepoints() # → ["h", "e", "l", "l", "o"] | |
str |> slice(2..-1) # → "llo" | |
str |> split(" ") # → ["hello"] | |
str |> capitalize() # → "Hello" | |
str |> match(regex) | |
Inspecting objects | |
inspect(object, opts \\ []) | |
value |> IO.inspect() | |
value |> IO.inspect(label: "value") | |
#Numbers | |
Operations | |
abs(n) | |
round(n) | |
rem(a, b) # remainder (modulo) | |
div(a, b) # integer division | |
Float | |
import Float | |
n = 10.3 | |
n |> ceil() # → 11.0 | |
n |> ceil(2) # → 11.30 | |
n |> to_string() # → "1.030000+e01" | |
n |> to_string([decimals: 2, compact: true]) | |
Float.parse("34") # → { 34.0, "" } | |
Integer | |
import Integer | |
n = 12 | |
n |> digits() # → [1, 2] | |
n |> to_charlist() # → '12' | |
n |> to_string() # → "12" | |
n |> is_even() | |
n |> is_odd() | |
# Different base: | |
n |> digits(2) # → [1, 1, 0, 0] | |
n |> to_charlist(2) # → '1100' | |
n |> to_string(2) # → "1100" | |
parse("12") # → {12, ""} | |
undigits([1, 2]) # → 12 | |
Type casting | |
Float.parse("34.1") # → {34.1, ""} | |
Integer.parse("34") # → {34, ""} | |
Float.to_string(34.1) # → "3.4100e+01" | |
Float.to_string(34.1, [decimals: 2, compact: true]) # → "34.1" | |
#Map | |
Defining | |
m = %{name: "hi"} # atom keys (:name) | |
m = %{"name" => "hi"} # string keys ("name") | |
Updating | |
import Map | |
m = %{m | name: "yo"} # key must exist | |
m |> put(:id, 2) # → %{id: 2, name: "hi"} | |
m |> put_new(:id, 2) # only if `id` doesn't exist (`||=`) | |
m |> put(:b, "Banana") | |
m |> merge(%{b: "Banana"}) | |
m |> update(:a, &(&1 + 1)) | |
m |> update(:a, fun a -> a + 1 end) | |
m |> get_and_update(:a, &(&1 || "default")) | |
# → {old, new} | |
Deleting | |
m |> delete(:name) # → %{} | |
m |> pop(:name) # → {"John", %{}} | |
Reading | |
m |> get(:id) # → 1 | |
m |> keys() # → [:id, :name] | |
m |> values() # → [1, "hi"] | |
m |> to_list() # → [id: 1, name: "hi"] | |
# → [{:id, 1}, {:name, "hi"}] | |
Deep | |
put_in(map, [:b, :c], "Banana") | |
put_in(map[:b][:c], "Banana") # via macros | |
get_and_update_in(users, ["john", :age], &{&1, &1 + 1}) | |
Constructing from lists | |
Map.new([{:b, 1}, {:a, 2}]) | |
Map.new([a: 1, b: 2]) | |
Map.new([:a, :b], fn x -> {x, x} end) # → %{a: :a, b: :b} | |
#List | |
import List | |
l = [ 1, 2, 3, 4 ] | |
l = l ++ [5] # push (append) | |
l = [ 0 | list ] # unshift (prepend) | |
l |> first() | |
l |> last() | |
l |> flatten() | |
l |> flatten(tail) | |
Also see Enum. | |
#Enum | |
Usage | |
import Enum | |
list = [:a, :b, :c] | |
list |> at(0) # → :a | |
list |> count() # → 3 | |
list |> empty?() # → false | |
list |> any?() # → true | |
list |> concat([:d]) # → [:a, :b, :c, :d] | |
Also, consider streams instead. | |
Map/reduce | |
list |> reduce(fn) | |
list |> reduce(acc, fn) | |
list |> map(fn) | |
list |> reject(fn) | |
list |> any?(fn) | |
list |> empty?(fn) | |
[1, 2, 3, 4] | |
|> Enum.reduce(0, fn(x, acc) -> x + acc end) | |
#Tuple | |
Tuples | |
import Tuple | |
t = { :a, :b } | |
t |> elem(1) # like tuple[1] | |
t |> put_elem(index, value) | |
t |> tuple_size() | |
Keyword lists | |
list = [{ :name, "John" }, { :age, 15 }] | |
list[:name] | |
# For string-keyed keyword lists | |
list = [{"size", 2}, {"type", "shoe"}] | |
List.keyfind(list, "size", 0) # → {"size", 2} | |
#Functions | |
Lambdas | |
square = fn n -> n*n end | |
square.(20) | |
& syntax | |
square = &(&1 * &1) | |
square.(20) | |
square = &Math.square/1 | |
Running | |
fun.(args) | |
apply(fun, args) | |
apply(module, fun, args) | |
Function heads | |
def join(a, b \\ nil) | |
def join(a, b) when is_nil(b) do: a | |
def join(a, b) do: a <> b | |
#Structs | |
Structs | |
defmodule User do | |
defstruct name: "", age: nil | |
end | |
%User{name: "John", age: 20} | |
%User{}.struct # → User | |
See: Structs | |
#Protocols | |
Defining protocols | |
defprotocol Blank do | |
@doc "Returns true if data is considered blank/empty" | |
def blank?(data) | |
end | |
defimpl Blank, for: List do | |
def blank?([]), do: true | |
def blank?(_), do: false | |
end | |
Blank.blank?([]) # → true | |
Any | |
defimpl Blank, for: Any do ... end | |
defmodule User do | |
@derive Blank # Falls back to Any | |
defstruct name: "" | |
end | |
Examples | |
Enumerable and Enum.map() | |
Inspect and inspect() | |
#Comprehensions | |
For | |
for n <- [1, 2, 3, 4], do: n * n | |
for n <- 1..4, do: n * n | |
for {key, val} <- %{a: 10, b: 20}, do: val | |
# → [10, 20] | |
for {key, val} <- %{a: 10, b: 20}, into: %{}, do: {key, val*val} | |
Conditions | |
for n <- 1..10, rem(n, 2) == 0, do: n | |
# → [2, 4, 6, 8, 10] | |
Complex | |
for dir <- dirs, | |
file <- File.ls!(dir), # nested comprehension | |
path = Path.join(dir, file), # invoked | |
File.regular?(path) do # condition | |
IO.puts(file) | |
end | |
#Misc | |
Metaprogramming | |
__MODULE__ | |
__MODULE__.__info__ | |
@after_compile __MODULE__ | |
def __before_compile__(env) | |
def __after_compile__(env, _bytecode) | |
def __using__(opts) # invoked on `use` | |
@on_definition {__MODULE__, :on_def} | |
def on_def(_env, kind, name, args, guards, body) | |
@on_load :load_check | |
def load_check | |
Regexp | |
exp = ~r/hello/ | |
exp = ~r/hello/i | |
"hello world" =~ exp | |
Sigils | |
~r/regexp/ | |
~w(list of strings) | |
~s|strings with #{interpolation} and \x20 escape codes| | |
~S|no interpolation and no escapes| | |
~c(charlist) | |
Allowed chars: / | " ' ( [ { < """. See: Sigils | |
Type specs | |
@spec round(number) :: integer | |
@type number_with_remark :: {number, String.t} | |
@spec add(number, number) :: number_with_remark | |
Useful for dialyzer. See: Typespecs | |
Behaviours | |
defmodule Parser do | |
@callback parse(String.t) :: any | |
@callback extensions() :: [String.t] | |
end | |
defmodule JSONParser do | |
@behaviour Parser | |
def parse(str), do: # ... parse JSON | |
def extensions, do: ["json"] | |
end | |
See: Module |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment