Contents
- Installation & basics
- Philosophy
- Types
- Functions
- Modules
- Basic concepts
- Collections
- Strings and Binaries
- Control Flow
- Exceptions
mix
brew update && brew install elixir
iex
for REPL- Use
h(...)
to get help, you can pass it a function to get help about that function
- Use
- Applications have
.ex
extension, wheres tests have.exs
extension- Use
.ex
to compile to bytecode - Use
.exs
to interpret on code level
- Use
- Compilation/execution:
elixir file.exs
iex
, thenc "file.exs"
- Object orientation: * Class is king, abstract hierarchies of classes * Classes encapsulate state * A lot of what you do revolves around data-hiding
- vs. functional programming:
- Data transformation (not hiding)
- Data is pushed through pipelines with multiple transformation steps
- Functions do the data transformation (-> no side-effects)
- Threads and processes are very lightweight, use them deliberately
- Metaprogramming is fundamental part of the language
- All datastructures are immutable
- Atoms (Symbols in ruby)
- Name = value
- Literal:
:fred
,:"something else"
,:is_good?
- String
- Literal:
"Hello"
- Are really UTF8 binaries (see below)
- Interplation: "1 + 1 = #{1+1}"
- Literal:
- Integer
- Literal:
1
,0xcafe
,0o765
,0b10110
,1_000_000
- Literal:
- Floating point
- Literal
1.23
- Literal
- Range:
- Literal
1..3
- Literal
- Regular expression
- Perl compatible (same as ruby / should be able to use rubular.com)
- Literal:
~r{[aeiou]}
- Tuple
- Use them for return values, then use pattern matching for control flow
- Literal:
{ 1, 2 }
- List
- Not arrays, linked datastructure instead
- Random order access is expensive
- Literal:
[ 1, 2, 3 ]
- Keyword lists:
[a: 1, b: 2]
is shorthand for[{a: 1}, {b: 2}]
- Concatenation:
[1,2] ++ [3,4] #=> [1,2,3,4]
- Intersection:
[1,2] -- [2,3] #=> [1]
- Inclusion:
1 in [1,2,3] #=> true
- Map
- Only one key per entry (keyword lists allow duplication)
- Literal:
m = %{ :a => 1 }
(same as%{ a: 1 }
) - Access with
m[:a]
orm.a
(latter one only works with atom keys) - Update with
m1 = %{ m | a: "one" }
- Will blow up if key
a
does not exist - Use
Dict.put_new/3
instead
- Will blow up if key
- HashDict
- Use Dict behavior -> Let's you swap Map for HashDict
- No literal, use
HashDict.new
andEnum.into
- Struct
- Module that wraps a (limited) Map
- Typesafe maps
- Literal is similar to a
Map
- May define methods in the User module, these are class level methods however
- Modify with
put_in
,update_in
,get_in
,get_and_update_in
defmodule User do defstruct name: "", admin: false end u = %User{name: "Sebastian"} u1 = %User{ u | admin: false}
- Set
- Only implementation:
HashSet
- Only implementation:
- Binaries
- Sequence of bits
- Literal:
<< 1,2 >>
- Parentheses around function arguments are optional
cd/1
means that the functioncd
takes one argument- If keyword list is last argument, you can leave off the
[]
sum = fn (a,b) -> a + b end # Anonymous function
sum.(1,2) # . needed for anonymous function
swap = fn { a, b } -> { b, a } end
swap.({6,8}) # arguments are pattern matched
- Modules and functions are separated by a
.
(IO.puts
) - = collection of functions grouped by a purpose
- Example:
Enum
has functions that deal with collections
- Example:
- Works in both directions, will fail if it can't be solved (
MatchError
) - Similar to algebraic
=
- Variable to be declared needs to be on left side (
[1,2] = list
won't work) - Can ignore a value with
_
list = [1,2,3]
[a, 2, b] = list # works
[a, _, b] = list # works
[a, 1, b] = list # won't work
- Variable only binds once per match,
- Use
^
to prevent reassignment:[^a,2] = [1,2]
does not work ifa = 2
+
:1 + 2 # => 3
|
:[1 | [2, 3]] # => [1,2,3]
div
:div(2,3) #=> 0
rem
:rem(2,3) #=> 2
- Identifiers:
/[a-z|A-Z|0-9|_|\?|!]+/
- Module, record, protocol and behavior: BumpyCase
- Everything else: snake_case
- Comments start with
#
true
,false
,nil
false
andnil
are treated as falsy in boolean contexts, everything else is true
- Strict
===
and value==
1 === 1.0 # => false
1 !== 1.0 # => true
1 == 1.0 # => true
1 != 1.0 # => false
and
,or
andnot
expecttrue
orfalse
as first argument&&
,||
,!
use truthiness
- Immutable datastructures make it easier to get parallelism right
- Can still rebind variables
- Create a copy with the changes if you need to create a new value
- Problems:
- Copying: don't copy everything into a new datastructure, instead link the elements in a shallow manner
- Garbage collection: Is faster because the heap is spread out across multiple processes and hence smaller which also means faster
- = interfaces in other languages
Access
is one
Enumerable
is a protocolCollectable
is a protocol as well, it allows insertion of elements into a collectionEnum
andStream
contain functions that handle collectionsEnum
contains more standard functionsStream
is for lazy collections
Enum.to_list
Enum.concat
Enum.map
/Enum.filter
/Enum.sort
/Enum.reject
Enum.max
/Enum.max_by
- ...
Streams
are composable Enumerators- Can be infinite
- They don't create multiple intermediary collections, instead the elements are pushed through one-by-one
- Libraries support/emit Streams:
- IO.stream
- File.stream!
- ...
- Implementing Streams:
Stream.cycle
: Infinite Stream from an EnumerableStream.repeatedly
: Execute fn every time you're asked for an elementStream.iterate
: Takes start_value and fn to produce next_value, next_value is start_value of next iterationStream.unfold
: Similar toiterate
but start_value doesn't have to be next_value --> Allows you to implement fibonnaciStream.resource
: Builds onunfold
to work with Files/Network- Takes
fn
that deliversstart_value
- Takes another
fn
to close the resource
- Takes
result = for $generator_or_filter [, into: $value ], do: $expression
- Generator consists of 1 or more
pattern <- list
statements, they are nested:for x <- [1,2], y <- [3,4], do: {x,y} #=> [{1, 3}, {1, 4}, {2, 3}, {2, 4}]
- Works on binaries as well, but generator needs to be enclosed in
<< >>
- Called Character Lists
- Libraries designed to work on Strings won't work
- Check with
is_list
- Called Strings or Binaries
[ head | tail ]
pattern matching won't work- Check with
is_binary
- Pattern matching:
<< head :: utf8, tail :: binary >> = "asd"
- Look for
<<>>
instead of[]
when recursing
- Heredocs:
'''
or"""
- Sigils: Example:
~w"A list of words"
- Possible letters:
S,s,W,w,C,c,R,r
- Possible deleimiters:
<>,{},[],(),||,//,"",''
- Possible letters:
- Can be mostly avoided by using pattern matching
if
/unless
cond
case
- Use for things that should never happen
- Used very sparingly
- Should propagate up to an external supervising process
raise "Giving Up"
is same asraise RuntimeError, message: "Giving Up"
- Return Tupels are used where other languages raise exceptions (e.g. File cannot be opened)
- By convention methods that end in
!
usually raise exceptions
mix new $name
allows you to create a new project that follows standard conventions