The get_in
function can be used to retrieve a nested value in nested maps using a list of keys.
nested = %{ one: %{ two: 3 } }
3 = get_in ( nested , [ :one , :two ])
# Returns nil for missing value
nil = get_in ( nested , [ :one , :three ])
Documentation:
The cond
keyword can be used like else if
found in other languages. See cond statement
Elixir uses the keyword case
instead of switch
. See case statement
This example shows how to sum a list, map, range, or other enumerable to calculate a total value.
# Enum.sum/1 for numeric values
6 == Enum . sum ([ 1 , 2 , 3 ])
6 == Enum . sum ( 1 .. 3 )
# reduce to extract/transform a value during sum
6 = Enum . reduce (%{ a: 1 , b: 2 , c: 3 }, 0 , fn ({ _k , v }, acc ) -> v + acc end )
Documentation:
This example shows how to get a value from a struct field.
# Define a struct for this example
defmodule User do
defstruct email: nil
end
# dot syntax
"c@c.com" = % User { email: "c@c.com" } . email
# Underlying implementation is a map
# So Map methods work
"c@c.com" = Map . get (% User { email: "c@c.com" }, :email )
# Pattern match to get a value
%{ email: email } = % User { email: "c@c.com" }
% User { email: email } = % User { email: "c@c.com" }
# Access protocol not available by default
% User { email: "c@c.com" }[ :email ]
#** (UndefinedFunctionError) undefined function User.fetch/2 (User does not implement the Access behaviour)
# User.fetch(%User{email: "c@c.com"}, :email)
# (elixir) lib/access.ex:118: Access.fetch/2
# (elixir) lib/access.ex:149: Access.get/3
# Enumerable protocol not available by default
Enum . filter ( % User { email: "c@c.com" }, fn ({ key , _ }) -> key == :email end )
#** (Protocol.UndefinedError) protocol Enumerable not implemented for %User{email: "c@c.com"}
# (elixir) lib/enum.ex:1: Enumerable.impl_for!/1
# (elixir) lib/enum.ex:116: Enumerable.reduce/3
# (elixir) lib/enum.ex:1477: Enum.reduce/3
# (elixir) lib/enum.ex:742: Enum.filter/2
Documentation:
This example shows how to update a struct field.
# Define a struct for this example
defmodule User do
defstruct email: nil
end
% User { email: "c@c.com" } = struct (% User {}, email: "c@c.com" )
# Structs are based on maps
# so map update methods and syntax are valid
% User { email: "a@a.co" } = %{ % User {} | email: "a@a.co" }
% User { email: "b@b.com" } = Map . put (% User {}, :email , "b@b.com" )
Documentation:
This example shows different ways to get values from a keyword list.
# [] can be used, first match returned
1 = [ a: 1 , b: 2 , a: 3 ][ :a ]
# [] missing value is nil
nil = [ a: 1 , b: 2 , a: 3 ][ :c ]
# Keyword get also works
1 = Keyword . get ([ a: 1 , b: 2 , a: 3 ], :a )
# missing value is nil
nil = Keyword . get ([ a: 1 , b: 2 , a: 3 ], :c )
# an optional default value can be specified
# for missing keys
"missing" = Keyword . get ([ a: 1 , b: 2 , a: 3 ], :c , "missing" )
# Keyword.take returns a list of matching pairs
[ a: 1 , a: 3 ] = Keyword . take ([ a: 1 , b: 2 , a: 3 ], [ :a ])
[] = Keyword . take ([ a: 1 , b: 2 , a: 3 ], [ :c ])
# dot syntax does NOT work
# results in compile error
[ a: 1 , b: 2 , a: 3 ] . a
Documentation: Keyword
Add a key and value to a map.
Map . put (%{ a: 1 }, :b , 2 )
%{ a: 1 , b: 2 }
Elixir provides short-circuiting logical boolean operators and
, or
, &&
, and ||
. The and
and or
operators are said to be strict because they only accept booleans and return a boolean result. The pipes ||
and ampersands &&
are non-strict/relaxed and can take any value. The values false
and nil
are the only falsey values and everything else is true.
Use and
or or
when you have boolean inputs and want a boolean result.
false = false and true
true = true and true
true = true or false
false = false or false
# A non boolean argument results in an ArgumentError
"hello" and true
# || can be used to assign fallback/default values
"default" = nil || "default"
# short-circuted || result since left side being
# true makes it true
"first" = "first" || "second"
"second" = "first" && "second"
# short-circuted && result, left-side false value returned
false = false && "second"
Documentation: Basic Operators
Behaviours provide a way to define an interface which a module can implement. A module declares that it implements the Behaviour with the @behaviour
annotation. The functions in the modules implementing the behaviour will be checked at compile time to see if they match the function specifications in the behavior.
# The @callback annotations below define function specifications that a
# module needs to implement the behaviour. The @callback parameter and
# return types must be specified or a compile error will occur.
defmodule Greeter do
@callback say_hello ( String . t ) :: any
@callback say_goodbye ( String . t ) :: any
end
# A module uses the @behaviour annotation to indicate
# that it implements a behaviour
defmodule NormalGreeter do
@behaviour Greeter
def say_hello ( name ), do : IO . puts "Hello, #{ name } "
def say_goodbye ( name ), do : IO . puts "Goodbye, #{ name } "
end
defmodule ExcitedGreeter do
@behaviour Greeter
def say_hello ( name ), do : IO . puts "Hello, #{ name } !!"
def say_goodbye ( name ), do : IO . puts "Goodbye, #{ name } !!"
end
# Since the following module does not implement say_goodbye/1
# a compile time warning will occur:
# "warning: undefined behaviour function say_goodbye/1 (for behaviour Greeter)"
defmodule InvalidGreeter do
@behaviour Greeter
def say_hello ( name ), do : IO . puts "Hello, #{ name } ."
end
Documentation: Behaviours
This post describes the steps that a HTTP request/response takes in a Phoenix Framework application. The example application described here was generated using the command mix phoenix.new hello_world
.
The steps in the HTTP request/response cycle are outlined as follows:
HTTP Request
Cowboy
Plug
Phoenix Endpoint
Phoenix Router
Phoenix Controller
Phoenix View
Phoenix Template
HTTP Response
HTTP Request
A browser user sends an HTTP GET request to http://localhost:4000/
.
The request is received by the Cowboy server.
Cowboy
Cowboy is an Erlang based HTTP server which is currently the only server with a Plug adapter implemented.
Cowboy will parse the HTTP request and the first Plug Connection will be created in Plug’s Cowboy adapter.
Plug
Plug is a web server interface for Elixir that provides a way to compose modules in a sequence during a request/response cycle.
Each plug module receives the HTTP request which is called a Plug Connection and often referred to as the variable conn
. The plug may transform and update the connection and then return an HTTP response immediately or pass the connection to the next plug in the sequence.
The Plug library has built in plugs that provide CSRF protection, sessions, logging, serving static files, and more.
Phoenix applications themselves are built by composing a series of plugs which are defined in a Phoenix Endpoint.
Phoenix Endpoint
The Phoenix Endpoint is found in the project file lib/hello_world/endpoint.ex
. The endpoint defines the plugs that will make up your application. It is the entry and exit point to the Phoenix application.
Each plug will be called in order as defined in the HelloWorld.Endpoint
. A plug such as the Plug.Static
plug may send a response before the connection is seen by other plugs. Note that the last plug defined is the router.
defmodule HelloWorld . Endpoint do
use Phoenix . Endpoint , otp_app: :hello_world
# Serve at "/" the static files from "priv/static" directory.
#
# You should set gzip to true if you are running phoenix.digest
# when deploying your static files in production.
plug Plug . Static ,
at: "/" , from: :hello_world , gzip: false ,
only: ~w(css images js favicon.ico robots.txt)
# Code reloading can be explicitly enabled under the
# :code_reloader configuration of your endpoint.
if code_reloading? do
plug Phoenix . LiveReloader
plug Phoenix . CodeReloader
end
plug Plug . Logger
plug Plug . Parsers ,
parsers: [ :urlencoded , :multipart , :json ],
pass: [ "*/*" ],
json_decoder: Poison
plug Plug . MethodOverride
plug Plug . Head
plug Plug . Session ,
store: :cookie ,
key: "_hello_world_key" ,
signing_salt: "0yg9mHDO"
plug :router , HelloWorld . Router
end
Phoenix Router
A Router defines an application’s pipelines and paths. If the given URL path matches based on the HTTP verb and path, the indicated controller action will be run.
Pipelines are defined using the pipeline
macro which describes a sequence of plugs to run on the connection. The scope
macros define which pipelines to run using the pipe_through
macro.
In this example, our GET request to http://localhost:4000/
will match get "/"
definition so the PageController.index
function will be called after the :browser
pipeline plugs are called with the connection. The :browser
pipeline is called because the get "/"
is defined inside a scope that specifies a pipeline using pipe_through
.
The Phoenix Router is analogous to the routes.rb
file in Ruby on Rails. The mix phoenix.routes
command will list the routes and path helpers defined by the router.
defmodule HelloWorld . Router do
use HelloWorld . Web , :router
pipeline :browser do
plug :accepts , [ "html" ]
plug :fetch_session
plug :fetch_flash
plug :protect_from_forgery
end
pipeline :api do
plug :accepts , [ "json" ]
end
scope "/" , HelloWorld do
pipe_through :browser # Use the default browser stack
get "/" , PageController , :index
end
# Other scopes may use custom stacks.
# scope "/api", HelloWorld do
# pipe_through :api
# end
end
Phoenix Controller
The controller is where application logic is done. Often this will include running SQL queries using the Ecto
database library which is not covered in this article.
The params variable will contain a string keyed map of request parameters. Next, Phoenix.Controller.render/2
is called with the conn
and template name index.html
. Often, Phoenix.Controller.render/3
will be called with additional data to render like this: render conn, "index.html", [data: "data"]
.
The Phoenix.Controller.render/2
method will lookup the template file which by convention should be located in web/templates/page/index.html.eex
and will then call the Phoenix.View.render_to_iodata/3
function.
defmodule HelloWorld . PageController do
use HelloWorld . Web , :controller
plug :action
def index ( conn , _params ) do
render conn , "index.html"
end
end
Phoenix View
Aside from rendering the template, the view also can provide helper functions and path helpers which will automatically be available to the template.
defmodule HelloWorld . PageView do
use HelloWorld . Web , :view
end
Phoenix Template
By default, an html formatted EEx template was generated. EEx means Embedded Elixir which allows Elixir code to be included in template files.
The index.html.eex
file that was generated only contains html by default. So, an alternative template example is shown here.
<div>
<p> <%= "This is Elixir code" %> </p>
</div>
HTTP Response
After the view renders the template, the controller will then call the Plug send_resp
method with the rendered template data to return the HTTP response.
The user receives the rendered template in the browser.
Links
This example shows how to write to a file using File.write
.
:ok = File . write ( "example.txt" , "Hello File!" )
# Error tuple for failure
{ :error , reason } = File . write ( "example.txt" , "Hello File!" )
# write!/3 Raises exception
:ok = File . write! ( "example2.txt" , "Hello File!" )
Documentation: File.write/3
This example shows examples of how a list can be pattern matched.
[ head | tail ] = [ 1 , 2 , 3 ]
head = 1
tail = [ 2 , 3 ]
[ head | tail ] = [ 1 ]
head = 1
tail = []
[] = []
# This does not match, no value for head
[ head | tail ] = []
# match head value
[ 1 | tail ] = [ 1 , 2 , 3 ]
tail = [ 2 , 3 ]
# use underscore to ignore a variable
[ head | _ ] = [ 1 , 2 , 3 ]
The pipe operator |>
is used to chain together a sequence of function calls. The result of the expression on the left side of the operator is passed as the first argument to the function in the right side of the operator.
[ "A" , "B" , "C" ] = "a,b,c"
|> String . split ( "," ) # split takes 2 arguments but here
# the first argument is omitted
# in the parentheses and
# the left side of the |> operator
# will be the first argument implicitly
|> Enum . map ( & String . upcase / 1 )
# This is equivalent to:
Enum . map ( String . split ( "a,b,c" , "," ), & String . upcase / 1 )
The special forms alias
, use
, import
, and require
are ways of accessing functions or macros outside of the current module. The forms alias
and import
are used to be able to refer to functions without having to use their fully qualified names. The form use
is used to add functionality to the current module by running a macro from another module. When macros are used from an external module, require
is needed to make the macros available to the compiler at compile time.
Alias
Used to shorten the references to a specific module.
# Fully qualified User struct
% Application . User {}
# alias is used to shorten the fully qualified name
alias Application . User , as: User
# After aliasing
% User {}
# alias without `:as` option will automatically use the last
# part of the module name after the last period
alias Application . User
# is the same as
alias Application . User , as: User
Use
Adds functionality to the current module by calling another module’s __using__
macro.
defmodule Hello do
defmacro __using__ ( _opts ) do
quote do
def say_hello do
IO . puts "Hello"
end
end
end
end
defmodule MyModule do
use Hello
end
# prints "Hello"
MyModule . say_hello
Import
Imports specific functions into the current module so they can be called without using their module name.
# Without import functions need to be called
# by their full name including the module
"ABC" = String . upcase ( "abc" )
# Import a single function with the form
# import Module, only: [function_name: arity]
import String , only: [ upcase: 1 ]
# upcase can now be used without the module name
"ABC" = upcase ( "abc" )
# Imports all functions in the String module.
# It is recommend to use only option above to
# only import the functions you need.
import String
Require
Makes a macro from an external module available to the compiler at compile time.
defmodule Hello do
# Example macro to add say_hello function to the module
defmacro hello_macro do
quote do
def say_hello do
IO . puts "Hello"
end
end
end
end
defmodule MyModule do
# Without require here results in the following error:
# (CompileError) iex:37: you must require Hello before invoking the macro Hello.hello_macro/0
require Hello
Hello . hello_macro
end
# Prints Hello
MyModule . say_hello
See documentation for more information: Alias, require and import
The mix help
command can be used to list all available commands. Available commands will vary depending if there is a mix.exs
file in the current directory.
$ mix help
mix # Runs the default task (current: "mix run")
mix app.start # Starts all registered apps
mix archive # Lists all archives
mix archive.build # Archives this project into a .ez file
mix archive.install # Installs an archive locally
mix archive.uninstall # Uninstalls archives
mix clean # Deletes generated application files
mix cmd # Executes the given command
mix compile # Compiles source files
mix deps # Lists dependencies and their status
mix deps.clean # Deletes the given dependencies' files
mix deps.compile # Compiles dependencies
mix deps.get # Gets all out of date dependencies
mix deps.unlock # Unlocks the given dependencies
mix deps.update # Updates the given dependencies
mix do # Executes the tasks separated by comma
mix escript.build # Builds an escript for the project
mix help # Prints help information for tasks
mix hex # Prints Hex help information
mix hex.build # Builds a new package version locally
mix hex.config # Reads or updates Hex config
mix hex.docs # Publishes docs for package
mix hex.info # Prints Hex information
mix hex.key # Hex API key tasks
mix hex.outdated # Shows outdated Hex deps for the current project
mix hex.owner # Hex package ownership tasks
mix hex.publish # Publishes a new package version
mix hex.registry # Hex registry tasks
mix hex.search # Searches for package names
mix hex.user # Hex user tasks
mix loadconfig # Loads and persists the given configuration
mix local # Lists local tasks
mix local.hex # Installs Hex locally
mix local.public_keys # Manages public keys
mix local.rebar # Installs rebar locally
mix new # Creates a new Elixir project
mix phoenix.new # Create a new Phoenix v0.13.1 application
mix profile.fprof # Profiles the given file or expression with fprof
mix run # Runs the given file or expression
mix test # Runs a project's tests
iex -S mix # Starts IEx and run the default task
This example shows how to get the last item in a list. Keep in mind that it is more effecient to get the head of a list than the last item when processing lists.
:c = List . last ([ :a , :b , :c ])
# nil is returned for an empty list
nil = List . last ([])
Documentation: List.last/1
The normal behavior of the match operator is to rebind variables on the left side of the operator if there is a match. The pin operator ^
is used on the left side of the match operator when you don’t wish to rebind and would like to match against the value of the pinned variable.
a = 1
# rebind a to 2, then 3
a = 2
a = 3
# Match error because a is pinned to 3
^ a = 4
Documentation: Pin Operator
Elixir often refers to functions in the format Module.function/arity
. For example the to_atom
function in the String
module which takes one argument would be referred to as String.to_atom/1
in the documentation. When a reference to a function is needed, we can use the function capture syntax, using the capture operator &
, as shown below to get a reference to a function.
fun_to_atom = & String . to_atom / 1
:a = fun_to_atom . ( "a" )
true = is_function ( fun_to_atom )
# Function capturing is often used to pass functions as parameters
# to another function
[ :a ] = Enum . map ([ "a" ], & String . to_atom / 1 )
The capture operator &
can also be used to create anonymous functions .
Documentation: Function Capturing
A Protocol is a way to dispatch to a particular implementation of a function based on the type of the parameter.
The macros defprotocol
and defimpl
are used to define Protocols and Protocol implementations for different types in the following example.
defprotocol Double do
def double ( input )
end
defimpl Double , for: Integer do
def double ( int ) do
int * 2
end
end
defimpl Double , for: List do
def double ( list ) do
list ++ list
end
end
4 = Double . double ( 2 )
[ 1 , 2 , 1 , 2 ] = Double . double ([ 1 , 2 ])
Documentation: Protocols
The map
function enumerates an enumerable while applying a transform function and collects the results into a list. Enum.map
can be used to map a list, map a map, or map any enumerable.
[ 2 , 4 , 6 ] = Enum . map ([ 1 , 2 , 3 ], fn ( i ) -> i * 2 end )
# map a map
[ :one , :two ] = Enum . map (%{ one: 1 , two: 2 }, fn ({ k , v }) -> k end )
# map a keyword list
[ 1 , 2 ] = Enum . map ([ c: 1 , d: 2 ], fn ({ k , v }) -> v end )
# map to a keyword list
[ a: 2 , a: 4 , a: 6 ] = Enum . map ([ 1 , 2 , 3 ], fn ( i ) -> { :a , i * 2 } end )
Documentation: Enum.map/2
The special form with
is used to chain a sequence of matches in order and finally return the result of do:
if all the clauses match. However, if one of the clauses does not match, its result is immediately returned.
6 = with { parsed , _ } <- Integer . parse ( "3.0" ),
do : parsed * 2
# if a clause doesn't match
# it's result is immediately returned
6 = with 2 <- 2 ,
1 <- 6 ,
do : 11
:error = with { parsed , _ } <- Integer . parse ( "WORD" ),
do : parsed * 2
Documentation: Kernel.SpecialForms.with/1
This example shows how to convert a String into a list of single characters. This could be used to enumerate a string.
Elixir calls each character a grapheme so we use the String.graphemes/1
method.
[ "T" , "e" , "s" , "t" ] = String . graphemes ( "Test" )
# Contrast this with codepoints which may return
# multiple codepoints for a single character
[ "ö" ] = String . graphemes ( "ö" )
[ "o" , "̈" ] = String . codepoints ( "ö" )
Documentation: String.graphemes/1
This example shows how to repeat a String a given number of times.
"AAA" = String . duplicate ( "A" , 3 )
"HELLO HELLO " = String . duplicate ( "HELLO " , 2 )
This example shows how to create a multiline string.
First line break is removed
" 1 \n 2 \n 3 \n " = """
1
2
3
"""
# Whitespace before trailing `"""` will remove
# whitespace up to the same indentation
# on each line
"1 \n 2 \n 3 \n " = """
1
2
3
"""
# Heredoc sigils can also be used
# Interpolated
~s"" "
\" #{ 1 } \"
\" #{ 2 } \"
"""
# Not Interpolated
~S"" "
" 1 "
" 2 "
"""
This example shows how to lowercase every letter in a string.
"hello world!" = String . downcase ( "Hello World!" )
Documentation: String.downcase/1
This example shows how to get the first character in a string.
"H" = String . at ( "Hello" , 0 )
Documentation: String.at/2
This example shows how to check if a string ends with a given suffix.
true = String . ends_with? ( "Period." , "." )
# True if any of list values match:
true = String . ends_with? ( "Period." , [ "." , "?" ])
false = String . ends_with? ( " !" , [ "." , "?" ])
Documentation: String.ends_with?/2
This example shows how to uppercase every letter in a string.
"HELLO WORLD!" = String . upcase ( "Hello World!" )
Documentation: String.upcase/1
This example shows how to read a file using File.stream!/3
.
stream = File . stream! ( "scratch.txt" )
# The stream is read by each line when Enumerated
Enum . each ( stream , fn ( x ) -> IO . puts x end )
[ "Line 1 \n " , "Line 2 \n " ] = Enum . into ( stream , [])
2 = Enum . reduce ( stream , 0 , fn ( x , acc ) -> acc + 1 end )
See documentation for more information: File.stream!/3
This example shows how to read a file to a string.
{ :ok , contents } = File . read ( "exists.txt" )
{ :error , reason } = File . read ( "doesnt_exist.txt" )
contents = File . read! ( "exists.txt" )
# Raises a File.Error
contents = File . read! ( "doesnt_exist.txt" )
The in
operator tests for membership using ===
within an enumerable.
true = "one" in [ "one" , "two" ]
# `in` is equivalent to calling Enum.member?/2
Enum . member? ([ "one" , "two" ], "one" )
true = { :a , 1 } in %{ a: 1 , b: 2 }
true = 1 in 1 .. 4
The File
module provides an exists?/1
to check if a file exists.
true = File . exists? ( "exists.txt" )
false = File . exists? ( "doesnt_exist.txt" )
Word lists can be created using the ~w
sigil.
[ "one" , "two" , "three" ] = ~w(one two three)
Elixir provides a number of functions to test the type of a variable.
is_atom ( variable )
is_binary ( variable )
is_bitstring ( variable )
is_boolean ( variable )
is_float ( variable )
is_function ( variable )
is_function ( variable , arity )
is_integer ( variable )
is_list ( variable )
is_map ( variable )
is_number ( variable )
is_pid ( variable )
is_port ( variable )
is_reference ( variable )
is_tuple ( variable )
There is no return
keyword so code must be organized to return early. The follow example shows how to organize code to return early in Elixir.
For example, the early return in ruby:
def hello
if some_condition
return "Goodbye"
end
do_this ()
do_something ()
end
Could look like this in Elixir:
def hello do
if some_condition do
"Goodbye"
else
do_this ()
do_something ()
end
end
Case and cond can also be used to return different values based on a condition.
See Also:
The truth table for if/cond/unless statements in Elixir is simple: only nil and false evaluate to false. All other values are true.
# Only nil and false are falsey everything else is truthy
nil
false
This example shows example nil usage. Nil is frequently used as a return value to represent no value.
nil
true = is_nil ( nil )
# nil is falsey
"ok" = unless nil do
"ok"
end
This example shows different ways to get values from a map.
# dot syntax can be used if key is atom
1 = %{ c: 1 } . c
# Raises a key error for missing key
%{ c: 1 } . a
# [] works for non-atom values
1 = %{ "a" => 1 }[ "a" ]
# [] returns nil for missing values
nil = %{ "a" => 1 }[ "b" ]
# Pattern matching can be used
%{ c: value } = %{ c: 1 }
1 = Map . get (%{ c: 1 }, :c )
nil = Map . get (%{ c: 1 }, :a )
# Default value can be specified
# for when the key is misssing
"default" = Map . get (%{ c: 1 }, :a , "default" )
{ :ok , value } = Map . fetch (%{ c: 1 }, :c )
:error = Map . fetch (%{ c: 1 }, :a )
1 = Map . fetch! (%{ c: 1 }, :c )
# Raises a key error
Map . fetch! (%{ c: 1 }, :a )
Character literals like ?a
can be used to create a range of the ASCII integers of these characters. This example could represent a range from a to z.
97 .. 122 = ?a .. ?z
This example shows the literal syntax used to create a range.
# range is inclusive start to end
1 .. 4
[ 1 , 2 , 3 , 4 ] = Enum . to_list ( 1 .. 4 )
# can be defined high to low
4 .. 1
Documentation: Range
You can define a method with default arguments using the \\
syntax. The default arguments are used when the argument is not provided.
def hello ( name \\ "Unknown" ) do
# name value is "Unknown" when name argument not provided
"Hello #{ name } "
end
This example shows a common approach to having a calculated field for a model.
defmodule User do
use Ecto . Schema
schema "users" do
field :first_name , :string
field :last_name , :string
end
# Example calculated field
def full_name ( user ) do
user . first_name <> " " <> user . last_name
end
end
The following is a guide to help compare Elixir and Ruby syntax and implementations.
Ruby
Elixir
Characteristics
Object-oriented, Imperative, Metaprogramming
Functional, Actor Concurrency, Macros
Typing
Dynamic
Dynamic
Concurrency
N/A
Lightweight Processes
Static Analysis
Not Available
Optional Typespecs
Interfaces
Duck Typing
Behaviours & Protocols
Package Manager
RubyGems
Hex
Task Runner
rake
mix
Interactive Shell
irb
iex
Testing
RSpec, test-unit, minitest
ExUnit
Web Framework
Rails
Phoenix
Virtual Machine
YARV
BEAM
Distributed Computing
N/A
Open Telecom Platform (OTP)
Define a method/function
def hello
"result"
end
def hello do
"result"
end
Variable assignment/Capture
#
a = "hello"
# match operator
a = "hello"
</tr>
Hash/Map Syntax
{ a: 1 }
%{ a: 1 }
Array/Tuple
[ 1 , 2 , 3 , 4 ]
{ 1 , 2 , 3 , 4 }
List Syntax
# Not Available
[ 1 , 2 , 3 , 4 ]
Map an Array/List
[ 1 , 2 , 3 , 4 ]. map { | x | x * x }
Enum . map ([ 1 , 2 , 3 , 4 ], & ( &1 * &1 ))
Range Syntax
1 .. 4
1 .. 4
String Interpolation
" #{ 2 + 2 } "
" #{ 2 + 2 } "
Reverse a String
"hello" . reverse
String . reverse ( "hello" )
Define a class
class Hello
end
# Not Applicable
# Modules, Structs, Protocols are used
</tr>
Anonymous Functions
square = lambda { | x | x * x }
square = fn ( x ) -> x * x end
Call Anonymous Function
square . call ( 2 )
square . ( 2 )
square . ( 2 )
</tr>
Define Module
module Example
end
defmodule Example
end
Atoms
#
:one
# not garbage collected
:one
Division
#
5 / 2 == 2
# use div/2 for integer division
5 / 2 == 2.5
If
if true
end
if true do
end
Else If
result = if 0 > 1
"No"
elsif 0 > 2
"Nope"
else
"fallback"
end
result = cond do
0 > 1 -> "No"
0 > 2 -> "Nope"
true -> "fallback"
end
#
#
Printing
puts "Hello World"
IO . puts "Hello World"
Looping
[ 1 , 2 , 3 , 4 ]. each { | i | puts i }
Enum . each ([ 1 , 2 , 3 , 4 ], & ( IO . puts &1 ))
Pattern Matching
# Not available
#
#
#
#
[ a , b ] = [ 1 , 2 ]
%{ a: value } = %{ a: 1 }
# pattern matching can match against literals
# and be used in function definitions,
# case statements, list comprehensions and more
Guard Clauses
# not available
#
def hello ( v ) when is_atom ( v ) do
end
Metaprogramming
method_missing
define_method
# et al
# Macros
#
#
Here are a few examples on how to use regular expressions in Elixir.
# A regex sigil to match 'foo'
~r/foo/
# Interpolation can be used
~r/foo/ = ~r/#{"foo"}/
# Test if a string matches
true = Regex . match? ( ~r/foo/ , "Hello foo" )
# Run returns first match
[ "foo1" , "1" ] = Regex . run ( ~r/foo([0-9])/ , "foo1 foo2" )
# Scan returns all matches
[[ "foo1" , "1" ], [ "foo2" , "2" ]] = Regex . scan ( ~r/foo([0-9])/ , "foo1 foo2" )
# Replace matches in a string
"one_two_three" = Regex . replace ( ~r/-/ , "one-two-three" , "_" )
Documentation: Regex
Unless statement
"ok" = unless false do
"ok"
end
See Also: if
Filter a list
[ 3 , 4 ] = Enum . filter ( [ 1 , 2 , 3 , 4 ], fn ( x ) -> x > 2 end )
Ternary operator
# There currently is no ternary operator like true ? "yes" : "no"
# So the following is suggested
"no" = if 1 == 0 , do : "yes" , else : "no"
Keyword list syntax
[{ :one , 1 }, { :two , 2 }] = [ one: 1 , two: 2 ]
Convert a map to keyword list
[ one: 1 , two: 2 ] = Map . to_list (%{ one: 1 , two: 2 })
Map to struct
# given the struct
defmodule User do
defstruct username: nil
end
% User { username: "test" } = struct ( User , %{ username: "test" , password: "secret" })
# struct! raises KeyError if un-matching keys provided
% User { username: "test" } = struct! ( User , %{ username: "test" , password: "secret" })
Pattern match a map
%{ b: value , d: value2 } = %{ a: 1 , b: 2 , d: 3 }
# Matches keys on the left side
# There may be more keys on the right side
%{ a: value } = %{ a: 1 , b: 2 , d: 3 }
# raises a match error if key is missing
%{ c: value } = %{ a: 1 , b: 2 }
How to define a module in Elixir:
defmodule Example do
end
# periods are allowed in module names
defmodule Example . Specific do
end
Documentation:
This example shows the literal syntax to create a tuple and also how to pattern match.
{ :ok , 1 , "a" }
# pattern match
{ :ok , result } = { :ok , "good" }
# this raises a match error
{ :ok , result } = { :ok , "good" , "one more" }
# empty tuple
{}
How to define a function/method in elixir:
# functions are defined inside Modules
defmodule Examples do
# basic defintion
def do_stuff ( params ) do
"result"
end
#shorthand syntax
def shorthand (), do : "result"
# defp is for private functions
defp private_method , do : "private"
# params can pattern match
def match_this (%{ :key => value }), do : value
# the first matching function is called (order matters)
def test ( test ), do : "matches any param"
def test ([]), do : "never matched"
end
These examples show two syntaxes available to create anonymous functions.
square = fn ( x ) -> x * x end
# calling an anonymous function uses a period
# before the parentheses
4 = square . ( 2 )
# pattern matching the arguments can be used
first = fn ([ head | rest ]) -> head end
1 = first . ([ 1 , 2 ])
# anonymous functions are commonly used as arguments
# to other functions
[ 1 , 4 , 9 ] = Enum . map ([ 1 , 2 , 3 ], square )
[ 3 , 4 , 5 ] = Enum . map ([ 1 , 2 , 3 ], fn ( x ) -> x + 2 end )
The shorthand syntax to create anonymous functions with the capture operator.
The syntax wraps an expression with &()
and each argument is indexed starting at 1 identified by &1
, &2
, and so on.
square = & ( &1 * &1 )
4 = square . ( 2 )
# This results in a compile error with
# the capture operator
# because at least one argument must be used
four = & ( 2 + 2 )
Documentation Capture operator
cond - similar to if, else if, else
in other languages
"true is always true" = cond do
0 > 1 -> "No"
0 > 2 -> "Nope"
true -> "true is always true"
end
List concatenation
[ 1 , 2 ] = [ 1 ] ++ [ 2 ]
Map syntax
# empty map
%{}
# map arrow syntax
%{ "one" => 1 , "two" => 2 }
# shorthand when keys are atoms
%{ one: 1 , two: 2 }
Concatenate strings
"Hello World" = "Hello" <> " " <> "World"
Documentation: Kernel.<>/2
Modulo/Remainder of integer division
1 = rem ( 5 , 2 )
# same sign as dividend
- 1 = rem ( - 5 , 2 )
Phoenix/Mix generate Ecto migration
mix ecto . gen . migration add_author_to_post
Documentation: Mix.Tasks.Ecto.Gen.Migration
See Also:
This mix task reverts migrations which were previously run.
mix ecto . rollback
Documentation: mix ecto.rollback
See Also:
This mix task runs any pending migrations against the repository.
mix ecto . migrate
Documentation: mix ecto.migrate
See Also:
If statement
"good" = if true do
"good"
else
"This will"
end
# nil is default when else is missing
nil = if false do
"good"
end
# alternative, one line syntax
"good" = if true , do : "good"
"else" = if false , do : "good" , else : "else"
Documentation: Kernel.if/2
See Also: unless
Case statement (similar to switch in other languages)
"Good" = case { :ok , :data } do
{ :ok , result } -> "Good"
{ :error , result } -> "Bad"
_ -> "Nothing matched"
end
Documentation: Kernel.SpecialForms.case/2
Get the size/length of a tuple
3 = tuple_size ({ 1 , 2 , 3 })
Documentation: Kernel.tuple_size/1
Get the size/length of a map
1 = map_size (%{ a: 1 })
Documentation: Kernel.map_size/1
Length of list
3 = length ([ 1 , 2 , 3 ])
Documentation: Kernel.length/1
Keyword list to map
%{ a: 1 , b: 2 , c: 3 } = Enum . into ( [ a: 1 , b: 2 , c: 3 ], %{} )
Documentation: Enum.into/2
Tuple to list
[ :a , :b , :c ] = Tuple . to_list ({ :a , :b , :c })
Documentation: Tuple.to_list/1
Range to list
[ 1 , 2 , 3 , 4 ] = Enum . to_list ( 1 .. 4 )
Documentation: Enum.to_list/1
List comprehension
[ 2 , 4 , 6 ] = for n <- [ 1 , 2 , 3 ], do : n + n
Related Documentation: Comprehensions
Prints a map
%{ key: 42 } = IO . inspect (%{ key: 42 })
# prints "%{key: 42}"
Related Documentation: Inspect Protocol
Prints a list
[ 1 , 2 , 3 ] = IO . inspect ([ 1 , 2 , 3 ])
# prints "[1, 2, 3]"
Related Documentation: Inspect Protocol
Print to console
:ok = IO . puts ( "Hello World" )
String interpolation
"Hello 42" = "Hello #{ 21 + 21 } "
Convert an atom to a string
"hello" = Atom . to_string ( :hello )
Documentation: Atom.to_string/1
Join a list of strings
"1,2,3" = Enum . join ([ "1" , "2" , "3" ], "," )
Documentation: Enum.join/2
Raise number to a power (exponent)
8.0 = :math . pow ( 2 , 3 )
Documentation: math.pow/2
Remove/trim leading and trailing whitespace from a string
"a" = String . trim ( " a " )
Documentation: String.trim/1
Replace character in a string
"123" = String . replace ( "1.2.3" , "." , "" )
Documentation: String.replace/4
This example shows how to split a string on whitespace or split on a string.
[ "1" , "2" , "3" ] = String . split ( "1,2,3" , "," )
# String.split/1 is useful to split on and strip whitespace
[ "1" , "2" ] = String . split ( " 1 \n \t 2 \n " )
Documentation:
Prepend to list
[ 1 , 2 , 3 , 4 ] = [ 1 | [ 2 , 3 , 4 ]]
Parse string to float
{ 3.14 , "" } = Float . parse ( "3.14" )
Documentation: Float.parse/1
Parse string to integer
{ 4 , "" } = Integer . parse ( "4" )
Documentation: Integer.parse/2
Integer division
2 = div ( 11 , 4 )
Get the head of a list
1 = hd ([ 1 , 2 , 3 , 4 ])
# alternatively use pattern matching
[ head | tail ] = [ 1 , 2 , 3 , 4 ]
# head contains 1
Update a map value
%{ a: "New Value" } = %{ %{ a: "Old Value" } | a: "New Value" }
# Note that this does not work to add a new key
%{ %{} | new_key: 1 }
# Raises (KeyError) key :new_key not found in: %{}
# Map.put/3 will add a key if it does not exist
%{ new_key: 1 } = Map . put (%{}, :new_key , 1 )
# or update the value if it does exist
%{ new_key: 2 } = Map . put (%{ new_key: 1 }, :new_key , 2 )
Reverse a string
"olleh" = String . reverse ( "hello" )