Skip to content

Instantly share code, notes, and snippets.

@bmitch
Last active June 17, 2017 19:05
Show Gist options
  • Save bmitch/952168bdc3ad9d57a61e5e5aeddf3374 to your computer and use it in GitHub Desktop.
Save bmitch/952168bdc3ad9d57a61e5e5aeddf3374 to your computer and use it in GitHub Desktop.
elixir-basics.md

Back

Basics

Value Types

Represent numbers, names, ranges and regular expressions.

Integers

Can be written as:

  • decimal (1234).
  • hexadecimal (0xcafe).
  • octal (0o765).
  • binary (0b1010).

May contain underscores, often used to separate groupes of three digits. So one million could be written as 1_000_000.

No fixed limit on their size.

Floats

  • Must be at least one digit before and after decimal point.
  • OPtional trailing exponent may be given.
1.0   0.2456   0.314158e1   314159.e-5

Atoms

  • Constants that represent something's name. Starts with a : which is followed by an atom word or Elixir operation.
  • Atom word is sequence of letters, digits, underscores and @ and may end with ! or ?.
  • Can also create atoms with arbitrary characters by enclosing the characters in double quotes.
  • Atoms:
:fred   :is_binary?   :var@2   :<>   :===   :"func/3"   :"long john silver"

An atom's name is its value. Two atoms with the same name will always compare as being equal.

Ranges

Rages represented as start..end where start and end are integers.

Regular Expressions

  • Written as ~r{regexp} or ~r{regexp}opts.

System Types

Resources in the underlying Erland VM.

PIDS and Ports

  • PID is a reference to a local or remote process.
  • PID of the current process by calling self.
  • New PID created when you spawn a new process.
  • Port is a reference to a resource (usually external to the app) that yuo are reading or writing to.

References

  • make_ref function creates a globally unique reference. No other reference is equal to it.

Collection Types

Can hold values of any type (including other collections)

Tuples

  • Ordered collection of values
  • Written between braces, comma separated
{1, 2}   { :ok, 42, "next"}   { :error, :enoent }

Typically have 2 to 4 elements. If you need more elements look at maps or structs.

  • Can use tuples in pattern matching:
{status, count, action} = { :ok, 42, "next"}
# {:ok, 42, "next"}

status
# :ok

count 
# 42

action
# "next"
  • Common for functions to return a tuple where the first element is the atom :ok if there are no errors.
{status, file} = File.open("mix.exs")
# {:ok, #PID<0.39.0>}

Common to write matches that assume success. The second ope fails and returns a tuple where the first element was :error. This causes the match to fail.

{:ok, file} = File.open("mix.exs")
# {:ok, #PID<0.39.0>}

{:ok, file} = File.open("non-existent-file")
** (MatchError) no match of right hand side value: {:error, :enoent}

Lists

  • Not the same as an array. (tuples are closer to an array)
  • List is a linked data structure.
  • Is either empty or has a head and a tail.
  • head contains a value and the tail is a list (like lisp)
  • Easy to traverse linearly - expensive to access in random order.

List Operators

[1, 2, 3] ++ [4, 5, 6] # concatenation
# [1, 2, 3, 4, 5, 6]

[1, 2, 3, 4] -- [2, 4] # difference
# [1, 3]

1 in [1, 2, 3, 4] # membership
# true

"wombat in [1, 2, 3, 4]
# false

Keyword Lists

Shortcut to write simple lists of key/value pairs:

[name: "Dave", city: "Dallas", likes: "Programming]

Elixir converts to list of two-value tuples:

[ {:name, "Dave"}, {:city, "Dallas"}, {:likes, "Programming}]

Can leave off square brakets if keyword list is last argument in a function call.

DB.save record, [ {:use_transaction, true}, {:logging, "HIGH"}]

can be written as:

DB.save record, use_transaction: true, logging: "HIGH"

Can also leave off the brackets if a keyword list is the last item in any context where a list is expected:

[1, fred: 1, dave: 2]
# [1, {:fred, 1}, {:dave, 2}]

{1, fred: 1, dave: 2}
# {1, [fred: 1, dave: 2]}

Maps

Collection of key/value pairs

%{ key => value, key => value }

In this example, the keys are strings.

states = %{ "AL" =< "Alabama", "WI" => "Wisconsin" }
# %{"AL" => "Alabama", "WI" => "Wisconsin"}

In this example, the keys are tuples.

responses = %{ {:error, :enoent } => :fatal, {:error, :busy} => :retry}
# {{:error, :busy} => :retry, {:error, :enoent} => :fatal}

In this example, the keys are ataoms.

colors = %{:red => 0xff0000, :green => 0x00ff00, :blue => 0x0000ff}
# %{blue: 255, green: 65280, red: 16711680}

Usually all keys are the same type but is not required.

%{"one" => 1, :two => 2, {1,1,1} => 3}
# %{:two => 2, {1, 1, 1} => 3, "one" => 1}

Can use expressions for keys:

name = "Foobar"
# "Foobar"

%{String.downcase(name} => name}
# %{"foobar" => "Foobar"}

Why does Elixir have maps and keyword lists?

  • Maps allow only one entry for a key.

  • Keyword lists allow key to be repeated.

  • Maps are efficient and can be used in pattern matching.

  • Generally use keyword lists for things like command-line paramaters and passing options

  • Use maps when you want an assoc array.

Accessing a Map

  • Extract values using the key.
states = %{ "AL" =< "Alabama", "WI" => "Wisconsin" }
# %{"AL" => "Alabama", "WI" => "Wisconsin"}

states["AL"]
# "Alabama"

states["TX"]
# nil
responses = %{ {:error, :enoent } => :fatal, {:error, :busy} => :retry}
# {{:error, :busy} => :retry, {:error, :enoent} => :fatal}

response_types[{:error, :busy}]
# :retry

If keys are atoms you can use a dot notation. If no matching key with dot notation you get a KeyError.

colors = %{:red => 0xff0000, :green => 0x00ff00, :blue => 0x0000ff}
# %{blue: 255, green: 65280, red: 16711680}

colors[:red]
# 16711680

colors.green
65280

Binaries

  • Useful for accessing data as a sequence of bits and bytes.
  • Example: reading headers in JPEG and MP3 files.
  • Binary literals enclosed between << and >>.

Syntax packs successive integers into bytes:

bin = << 1, 2 >>
# <<1, 2>>

byte_size bin
# 2

Use modifiers to controll the type and size of each individual fied. Single byte that contains three fielsd of widths 2, 4 and 2 bits.

bin = <<3 :: size(2), 5 :: size(4), 1 :: size(2)>>
# <<213>>

:io.format("~-8.2b~n", :binary.bin_to_list(bin))
# 1010101
# :ok

byte_size bin
# 1

Dates and Times

Date

  • Holds a year, month day and a reference to the ruling calendar.
d1 = Date.new(2016, 12, 25)
# {:ok, ~D[2016-12-25])

{:ok, d1} = Date.new(2016, 12, 25)
# {:ok, ~D[2016-12-25]}

d2 = ~D[2016-12-25]
# ~D[2016-12-25]

d1 == d2
# true

d1
# ~D[2016-12-25]

inspect d1, structs: false
# "%{__struct__: Date, calendar: Calendar.ISO, day: 25, month: 12, year: 2016}"

Time

  • Contains an hour, minute, second and fractions of a second.
  • Fraction stored as a tuple.
t1 = Time.new(12, 34, 56)
# {:ok, ~T[12:34:56]}

t2 = ~T[12:34:56.78]
# ~T[12:34:56.78]

t1 == t2
# false

inspect t2, structs: false
# "{:ok, %{__struct__: Time, hour: 12, microsecond: (780000, 2}, minute: 34, second: 56}}"

Names, Source Files, Conventions, Operators

  • Elixir identifiers consist of upper and lowercase ASCII characters, digits and underscores.

  • May end with a ? or !.

  • Module, record, protocol and behaviour names start with an upper case and are BumpyCase.

  • All other identifiers start with lowercase or underscore and use underscores between words.

  • If first char is _, Elixir doesn't report a warning if the variable is unused in a pattern match or func parameter list.

  • Source files are written in UTF-8 but identifiers may only use ASCII.

  • Source files use two-char indentation for nesting, uses spaces.

  • Comments start with #

  • Style Guide: https://github.com/christopheradams/elixir_style_guide

Truth

  • true, false and nil.
  • nil treated as false in Boolean contexts. (these are aliases for atoms of the same name. So true is the same as :true.

Most of the time any other value other than false or nil is treated as true. (aka truthy).

Operators

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment