Choice
scala - JVM JAVA
clojure - lisp
elixir - erlang, ruby
haskell - (not so popular)
elixir: high traffic, Distributed / clustered systems, asynchronously, high-availability, server-side push and real-time app
Install
brew install elixir
Hello world
IO.puts "hello world"
Keywords
- fn, end
- (types)
- Atom
- BitString
- Float
- Function
- Integer
- List
- Map
- PID
- Port
- Reference
- Tuple
- PID
- (data structure)
- (keyword lists)
- (struct)
- MapSet(set based on maps)
- defmodule, do, end
- and, or
- def, when, and, do
- ===, !==, !=, <=
- ++, <>
- case, do, end
- cond, do, end
- if/unless, do, else, end
- spawn/
spawn_link
/spawn_monitor
, fn, end - receive, do, after, end
- raise
for n <- [1, 2, 3, 4], do: n * n
- alias, require, import, use
- defstruct
- defprotocol, do, end
- defimpl, for:, do, end
- defexception
- try do, throw, catch, rescue, after, else, end
- with, do
- defp, do
KEYWORD LISTS vs MAP
keyword list
-
Keys must be atoms.
-
Keys are ordered, as specified by the developer.
-
Keys can be given more than once.
[{:a, 1}, {:b, 2}] [a: 1, b: 2, c: 3]
map
-
Maps allow any value as a key.
-
Maps’ keys do not follow any ordering.
map = %{:a => 1, 2 => :b}
Built-in functions
is_number
,is_atom
,is_string
,is_binary
,is_function
,is_nil
,is_list
,is_reference
,is_port
,is_tuple
,is_bitstring
,is_boolean
String.upcase
byte_size
( bytes in string )String.length
(characters in string)tuple_size
( for tuple )length
( for list )- i
- hd, tl
- round, trunc
- elem,
put_elem
- send
put_in
,update_in
,get_and_update_in
- inspect
- flush
Process.alive?
Task.start
Special Atom
- :stdio
- :write
- :utf8
Built-in libraries
- Enum
- IO, File, Path, StringIO
Built-in protocols
- String.Chars
- Collectable
- List.Chars
- IEx.Info
- Enumerable
- Inspect
Syntax sugar
- “hello #{:world}”
- Comprehensions
Enum.map [1, 2, 3, 4], &(&1 * 2)
#=> [2, 4, 6, 8]
List.foldl [1, 2, 3, 4], 0, &(&1 + &2)
#=> 10
defmodule Math do
def square(x) do
x * x
end
end
Enum.map [1, 2, 3], &Math.square/1
#=> [1, 4, 9]
Module attributes
@vsn
@moduledoc
@doc
@behavious
@before_compile
@enforce_keys
@spec
@type
@typedoc
@callback
@behaviour
Debug in elixir
IO.inspect(item, opts \\ [])
IO.inspect(label: "name")
IO.inspect binding()
require IEx; IEx.pry
:debugger
:observer.start()
runtime_info
- more
Notes
- Elixir runs on the Erlang VM giving developers complete access to Erlang’s ecosystem
- Single quotes are charlists, double quotes are strings.
- Elixir data structures are immutable
- Lists are stored in memory as linked lists
- Tuples, on the other hand, are stored contiguously in memory
- When you update a tuple, all entries are shared between the old and the new tuple, except for the entry that has been replaced. In other words, tuples and lists in Elixir are capable of sharing their contents. This reduces the amount of memory allocation the language needs to perform
- All of the Erlang built-ins reside in the :erlang module
- Elixir allows for default values for arguments (
\\
) - everything is truthy except nil and false
- we can compare different data types: number < atom < reference < function < port < pid < tuple < map < list < bitstring
- when using do/end blocks is they are always bound to the outermost function call
- A binary is a sequence of bytes; A charlist is a list of code points.
- Recursion and tail call optimization are an important part of Elixir and are commonly used to create loops. However, when programming in Elixir you will rarely use recursion as above to manipulate lists.
- The
|>
symbol used in the snippet above is the pipe operator: it takes the output from the expression on its left side and passes it as the first argument to the function call on its right side. - All the functions in the Enum module are eager. Many functions expect an enumerable and return a list back.
- As an alternative to Enum, Elixir provides the Stream module which supports lazy operations. Instead of generating intermediate lists, streams build a series of computations that are invoked only when we pass the underlying stream to the Enum module. Streams are useful when working with large, possibly infinite, collections.
- Using processes to maintain state and name registration are very common patterns in Elixir applications. For example, Elixir provides
agents
, which are simple abstractions around state. - In Elixir, we avoid using try/rescue because we don’t use errors for control flow. We take errors literally: they are reserved for unexpected and/or exceptional situations. Instead of rescuing an error, we’d rather “fail fast” since the supervision tree will guarantee our application will go back to a known initial state after the error.
- Elixir is a dynamically typed language, so all types in Elixir are inferred by the runtime.
- The Erlang tool Dialyzer, for example, uses typespecs in order to perform static analysis of code.
- Atoms are not garbage collected. Once an atom is created, it is never reclaimed.
Ecosystem
- OTP (Open Telecom Platform): is a set of libraries that ships with Erlang. Erlang developers use OTP to build robust, fault-tolerant applications. In this chapter we will explore how many aspects from OTP integrate with Elixir, including supervision trees, event managers and more
- Mix: is a build tool that ships with Elixir that provides tasks for creating, compiling, testing your application, managing its dependencies and much more
- ExUnit: is a test-unit based framework that ships with Elixir
- phoenixframework/phoenix: Productive. Reliable. Fast.
file formats
- ebin - contains the compiled bytecode
- lib - contains elixir code (usually .ex files)
- test - contains tests (usually .exs files)
Advanced content
- Mix and OTP: Elixir application with its own supervision tree, configuration, tests, and more
- Meta-programming
- Learning Erlang