seven-languages-in-seven-weeks/slides/erlang.org
2015-08-12 17:17:48 -04:00

21 KiB
Raw Blame History

Seven Languages in Seven Weeks

Introduction

Introduction

Erlang/OTP   BMCOL

Created
1986
Author
Joe Armstrong, Robert Virding, and Mike Williams

A functional language built for concurrency and reliability.

Agent Smith   BMCOL

/github/seven-languages-in-seven-weeks/media/commit/b149caee47d5cd4f46d33879621e04e72c720d15/slides/Agent_Smith2.jpg

Getting Erlang/OTP

Concurrency

  • Lightweight processes
  • No shared state
  • Transparently distributed message passing
  • Processes as actors

Reliability   B_frame

  • "Let it crash" approach to error handling
  • Process monitoring and restarting
  • Hot code loading

The whole notion of “nondefensive” programming and “Let It Crash,” which is the mantra of Erlang programming, is completely the opposite of conventional practice, but it leads to really short and beautiful programs.

Dr. Joe Armstrong

Day 1

Day 1: Appearing Human

Agent Smith   BMCOL

/github/seven-languages-in-seven-weeks/media/commit/b149caee47d5cd4f46d33879621e04e72c720d15/slides/agent_smith.jpg

Functional Concepts   BMCOL

Erlang is the first of our functional languages:

  • Your programs are going to be built entirely out of functions, with no objects anywhere.
  • Those functions will usually return the same values, given the same inputs.
  • Those functions will not usually have side effects, meaning they will not modify program state.
  • You will only be able to assign any variable once.

Comments and Expressions

  % This is a comment

  2 + 2.                            % 4
  2 + 2.0.                          % 4
  "string".                         % "string"
  [1, 2, 3].                        % [1,2,3]
  [72, 97, 32, 72, 97, 32, 72, 97]. % "Ha Ha Ha"

So, a String is really a List, and Agent Smith just laughed at your mamma.

Variables

  variable = 4.
  %% ** exception error: no match of right hand side value 4

This error message is really a reference to Erlang's pattern matching. It's breaking because variable is an atom. Variables must start with an uppercase letter.

  Var = 1.
  %% 1

  Var = 2.
  %% ** exception error: no match of right hand side value 2

As you can see, variables begin with a capital letter, and they are immutable. You can assign each value only once.

Atoms, Lists, and Tuples

:BMCOL:

Atoms   B_example
  red.
  %% red
  Pill = blue.
  %% blue
  Pill.
  %% blue
Lists   B_example
  [1, 2, 3].
  %% [1,2,3]
  [1, 2, "three"].
  %% [1,2,"three"]
  List = [1, 2, 3].
  %% [1,2,3]

:BMCOL:

Tuples   B_example
  {one, two, three}.
  %% {one,two,three}
  Origin = {0, 0}.
  %% {0,0}
Tuples as hashes   B_example
  {comic_strip,
   {name, "Calvin and Hobbes"},
   {character, "Spaceman Spiff"}}.

Day 1 Interlude: Data Structures

\setbeamercolor{frametitle}{fg=red} \setbeamercolor{sidebar}{fg=darkred} \setbeamercolor{title in head/foot}{fg=red}

Data Structures

\fontspec{Antonio-Bold}\color{red} \fontsize{80}{80}\selectfont ADDENDUM

\huge \color{darkred} Data Structures

Records and Maps

Records
Provide structure and syntax around named tuples (tuples where the first element is an atom describing the contents of the tuple, e.g. {alias, "Thomas A. Anderson", "Neo"}).
Maps
A new mapping type with its own syntax, added in Erlang/OTP 17.0. Allows keys of any type.

Record and Map Examples

Record   B_example BMCOL

  -record(comic_strip,
          {name, character}).

  Strip = #comic_strip{
             name = "Calvin and Hobbes",
             character = "Spaceman Spiff"}.
  %% {comic_strip, "Calvin and Hobbes",
  %%               "Spaceman Spiff"}

  Strip#comic_strip.name.
  %% "Calvin and Hobbes"

Map   B_example BMCOL

  Strip = #{name => "Calvin and Hobbes",
            character => "Spaceman Spiff"}.
  %% #{name => "Calvin and Hobbes",
  %%   character => "Spaceman Spiff"}.

  maps:get(name, Strip).
  %% "Calvin and Hobbes"

Property Lists and Dictionaries

Property Lists
Ordinary lists containing entries in the form of either tuples, whose first elements are keys used for lookup and insertion, or atoms, which work as shorthand for tuples {Atom, true}. Property lists are useful for representing inherited properties, such as options passed to a function where a user may specify options overriding the default settings, object properties, annotations, etc.
Dictionaries
Implements a Key - Value dictionary. The representation of a dictionary is not defined.

Proplist and Dict Examples

Proplist   B_example BMCOL

  Strip = [{name, "Calvin and Hobbes"},
           {character, "Spaceman Spiff"}].
  %% [{name, "Calvin and Hobbes"},
  %%  {character, "Spaceman Spiff"}]

  proplists:get_value(name, Strip).
  %% "Calvin and Hobbes"

Dict   B_example BMCOL

  Strip = dict:from_list(
            [{name, "Calvin and Hobbes"},
             {character, "Spaceman Spiff"}]).
  %% ...

  dict:fetch(name, Strip).
  %% "Calvin and Hobbes"

Day 1: Continued

\setbeamercolor{frametitle}{fg=trek@lightorange} \setbeamercolor{sidebar}{fg=trek@darkorange} \setbeamercolor{title in head/foot}{fg=trek@lightorange}

Pattern Matching

  Person = {person,
            {name, "Agent Smith"},
            {profession, "Killing Programs"}}.

  {person, {name, Name}, {profession, Profession}} = Person.

  Name.
  %% "Agent Smith"

  Profession.
  %% "Killing Programs"

Erlang will match up the data structures, assigning variables to the values in the tuples.

Pattern Matching (Lists)

  [Head | Tail] = [1, 2, 3].
  %% Head = 1
  %% Tail = [2,3]

  [One, Two|Rest] = [1, 2, 3].
  %% One = 1
  %% Two = 2
  %% Rest = [3]

  [X|Rest] = [].
  %% ** exception error: no match of right hand side value []

Binary Pattern Matching

Packing   B_example BMCOL

  W = 1.
  X = 2.
  Y = 3.
  Z = 4.
  All = <<W:3, X: 3, Y:5, Z:5>>.
  %% <<"(d">>

Unpacking   B_example BMCOL

  <<A:3, B:3, C:5, D:5>> = All.
  %% A = 1
  %% B = 2
  %% C = 3
  %% D = 4

Functions

Simple   BMCOL

  -module(basic).
  -export([mirror/1]).

  mirror(Anything) -> Anything.
basic.erl
  -module(matching_function).
  -export([number/1]).

  number(one)   -> 1;
  number(two)   -> 2;
  number(three) -> 3.
matching_function.erl

Factorial   BMCOL

  -module(yet_again).
  -export([another_factorial/1,
           another_fib/1]).

  another_factorial(0) ->
      1;
  another_factorial(N) ->
      N * another_factorial(N - 1).

  another_fib(0) ->
      1;
  another_fib(1) ->
      1;
  another_fib(N) ->
      another_fib(N - 1) + another_fib(N - 2).
yet_again.erl

Day 2

Day 2: Changing Forms

/github/seven-languages-in-seven-weeks/media/commit/b149caee47d5cd4f46d33879621e04e72c720d15/slides/agent-transform-1.png

/github/seven-languages-in-seven-weeks/media/commit/b149caee47d5cd4f46d33879621e04e72c720d15/slides/agent-transform-2.png

/github/seven-languages-in-seven-weeks/media/commit/b149caee47d5cd4f46d33879621e04e72c720d15/slides/agent-transform-3.png

Youre going to learn to apply functions to lists that can quickly shape the list into exactly what you need. Do you want to turn a shopping list into a list of prices? What about turning a list of URLs into tuples containing content and URLs? These are the problems that functional languages simply devour.

Control Structures: Case

  Animal = "dog".
  case Animal of
      "dog" -> underdog;
      "cat" -> thundercat
  end.
  %% underdog

  case Animal of
      "elephant" -> dumbo;
      _ -> something_else
  end.
  %% something_else

Control Structures: If

  X = 0.

  if
      X > 0 -> positive;
      X < 0 -> negative
  end.
  %% ** exception error: no true branch found when evaluating an if expression

  if
      X > 0 -> positive;
      X < 0 -> negative;
      true  -> zero
  end.
  %% zero

Anonymous Functions

  Negate = fun(I) -> -I end.
  %% #Fun<erl_eval.6.13229925>

  Negate(1).
  %% -1
  Negate(-1).
  %% 1

Lists and Higher-Order Functions

Left   BMCOL

  Numbers = [1, 2, 3, 4].
  Print = fun(X) -> io:format("~p~n", [X]).

  lists:foreach(Print, Numbers).
  %% 1
  %% 2
  %% 3
  %% 4
  %% ok

  lists:map(fun(X) -> X + 1 end, Numbers).
  %% [2,3,4,5]

  Small = fun(X) -> X < 3 end.
  lists:filter(Small, Numbers).
  %% [1,2]
  lists:all(Small, [0, 1, 2]).
  %% true
  lists:all(Small, [0, 1, 2, 3]).
  %% false

Right   BMCOL

  lists:any(Small, [0, 1, 2, 3]).
  %% true
  lists:any(Small, [3, 4, 5]).
  %% false

  lists:any(Small, []).
  %% false
  lists:all(Small, []).
  %% true

  lists:takewhile(Small, Numbers).
  %% [1,2]
  lists:dropwhile(Small, Numbers).
  %% [3,4]
  lists:takewhile(Small, [1, 2, 1, 4, 1]).
  %% [1,2,1]
  lists:dropwhile(Small, [1, 2, 1, 4, 1]).
  %% [4,1]

Foldl

  Numbers.
  %% [1,2,3,4]

  Adder = fun(ListItem, SumSoFar) -> ListItem + SumSoFar end.
  InitialSum = 0.

  lists:foldl(Adder, InitialSum, Numbers).
  %% 10

List Construction

  double_all([]) -> [];
  double_all([First|Rest]) -> [First + First|double_all(Rest)].

Examples   B_ignoreheading

Erlang   B_example BMCOL
  [1 | [2, 3]].
  %% [1,2,3]

  [[2, 3] | 1].
  %% [[2,3]|1]

  [[] | [2, 3]].
  %% [[],2,3]

  [1 | []].
  %% [1]
Box-and-pointer Diagrams   B_example BMCOL
  [1 | [2, 3]]
  +-----+-----+    +-----+-----+    +-----+-----+
  |     |cAAA |    |     |cAAA |    |     |cBLK |
  |  1  |     +--> |  2  |     +--> |  3  |     |
  |     |     |    |     |     |    |     |     |
  +-----+-----+    +-----+-----+    +-----+-----+

  -----------------------------------------------

  [[2, 3] | 1]
  +-----+-----+    +-----+-----+
  |cAAA |cAAA |    |     |cBLK |
  |     |     +--> |  1  |     |
  |     |     |    |     |     |
  +--+--+-----+    +-----+-----+
     |                 
     v                 
  +-----+-----+    +-----+-----+
  |     |cAAA |    |     |cBLK |
  |  2  |     +--> |  3  |     |
  |     |     |    |     |     |
  +-----+-----+    +-----+-----+

  -----------------------------------------------

  [[] | [2, 3]]
  +-----+-----+    +-----+-----+
  |cBLK |cAAA |    |cAAA |cBLK |
  |     |     +--> |     |     |
  |     |     |    |     |     |
  +-----+-----+    +--+--+-----+
                      |
                      v
                   +-----+-----+    +-----+-----+
                   |     |cAAA |    |     |cBLK |
                   |  2  |     +--> |  3  |     |
                   |     |     |    |     |     |
                   +-----+-----+    +-----+-----+
          
  -----------------------------------------------

  [1 | []]    
  +-----+-----+
  |     |cBLK |
  |  1  |     |
  |     |     |
  +-----+-----+

/github/seven-languages-in-seven-weeks/media/commit/b149caee47d5cd4f46d33879621e04e72c720d15/slides/erlang-boxandpointer.png

List Comprehensions

List comprehensions provide a succinct syntax combining mapping, filtering, and pattern matching.

  • Take the form [Expression || Clause1, Clause2, ..., ClauseN].
  • List comprehensions can have an arbitrary number of clauses.
  • The clauses can be generators or filters.

    • A filter can be a boolean expression or a function returning a boolean.
    • A generator, of the form Match <- List, matches a pattern on the left to the elements on the right.

List Comprehension Examples

  Fibs = [1, 1, 2, 3, 5].
  Double = fun(X) -> X * 2 end.
  [Double(X) || X <- Fibs].
  %% [2,2,4,6,10]

  Cart = [{pencil, 4, 0.25}, {pen, 1, 1.20}, {paper, 2, 0.20}].
  WithTax = [{Product, Quantity, Price, Price * Quantity * 0.08} ||
                {Product, Quantity, Price} <- Cart].
  %% [{pencil,4,0.25,0.08},{pen,1,1.2,0.096},{paper,2,0.2,0.032}]
  Cat = [{Product, Price} || {Product, _, Price} <- Cart].
  %% [{pencil,0.25},{pen,1.2},{paper,0.2}]

  [X || X <- [1, 2, 3, 4], X < 4, X > 1].
  %% [2,3]

  [{X, Y} || X <- [1, 2, 3, 4], X < 3, Y <- [5, 6]].
  %% [{1,5},{1,6},{2,5},{2,6}]

Day 3

Day 3: The Red Pill

/github/seven-languages-in-seven-weeks/media/commit/b149caee47d5cd4f46d33879621e04e72c720d15/slides/morpheus-red-pill.jpg

I didn't say that it would be easy. I just said that it would be the truth. You have to let it all go. Fear, doubt, and disbelief. Free your mind.

Basic Concurrency Primitives

  • Spawning a process with spawn
  • Sending a message with !
  • Receiving a message with receive

Code   B_example BMCOL

  -module(translate).
  -export([loop/0]).

  loop() ->
      receive
          "casa" ->
              io:format("house~n"),
              loop();
          "blanca" ->
              io:format("white~n"),
              loop();
          _ ->
              io:format("I don't understand.~n"),
              loop()
      end.

Usage   B_example BMCOL

Pid = spawn(fun translate:loop/0).
Pid ! "casa".
%% "house"
%% "casa"

Synchronous Messaging

Code   BMCOL

  -module(translate_service).
  -export([loop/0, translate/2]).

  loop() ->
      receive
          {From, "casa"} -> 
              From ! "house", 
              loop();
          {From, "blanca"} -> 
              From ! "white", 
              loop();
          {From, _} -> 
              From ! "I don't understand.", 
              loop()       
  end.

  translate(To, Word) ->
      To ! {self(), Word}, 
      receive
          Translation -> Translation
      end.

Usage   B_example BMCOL

  Translator = spawn(fun translate_service:loop/0).
  %% <0.38.0>>
  translate_service:translate(Translator, "blanca").
  %% "white"
  translate_service:translate(Translator, "casa").
  %% "house"

Linking a Process for Reliability

Linking   B_example BMCOL

  skinparam sequenceArrowFontColor white
  Console -> Roulette : spawn()
  activate Roulette
  Console -> Roulette : 1
  Roulette -> Roulette : "click"
  Console -> Roulette : 3
  Roulette -> Roulette: "bang"
  destroy Roulette

/github/seven-languages-in-seven-weeks/src/commit/b149caee47d5cd4f46d33879621e04e72c720d15/slides/erlang-linking.eps

Coroner   B_example BMCOL

  skinparam sequenceArrowFontColor white
  Console -> Roulette : spawn()
  activate Roulette
  Coroner -> Roulette : link()
  Console -> Roulette : 1
  Roulette -> Roulette : "click"
  Console -> Roulette : 3
  Roulette -> Roulette : "bang"
  Roulette -> Coroner : {'EXIT', self(), Reason}
  destroy Roulette

/github/seven-languages-in-seven-weeks/src/commit/b149caee47d5cd4f46d33879621e04e72c720d15/slides/erlang-coroner.eps

Doctor   B_example BMCOL

  skinparam sequenceArrowFontColor white
  Console -> Doctor : spawn()
  Console -> Doctor : new
  Doctor -> Roulette : spawn_link()
  Console -> Roulette : 1
  Roulette -> Roulette : "click"
  Console -> Roulette : 3
  Roulette -> Roulette : "bang"
  Roulette -> Doctor : {'EXIT', self(), Reason}
  destroy Roulette
  Doctor -> Doctor : new
  Doctor -> Roulette : spawn_link()
  Console -> Roulette : 2
  Roulette -> Roulette : "click"

/github/seven-languages-in-seven-weeks/src/commit/b149caee47d5cd4f46d33879621e04e72c720d15/slides/erlang-doctor.eps

Day ∞

\setbeamercolor{frametitle}{fg=red} \setbeamercolor{sidebar}{fg=darkred} \setbeamercolor{title in head/foot}{fg=red}

Day ∞: OTP

\fontspec{Antonio-Bold}\color{red} \fontsize{120}{120}\selectfont OTP

\huge \color{darkred} The Open Telecom Platform

OTP

/github/seven-languages-in-seven-weeks/media/commit/b149caee47d5cd4f46d33879621e04e72c720d15/slides/agent_code.jpg

There's way too much information to decode in the Matrix. You get used to it, though. OTP does the translating. I don't even see the code. All I see is supervisor, gen_server, release…

Wrapping Up

\setbeamercolor{frametitle}{fg=trek@lightorange} \setbeamercolor{sidebar}{fg=trek@darkorange} \setbeamercolor{title in head/foot}{fg=trek@lightorange}

Wrapping up Erlang/OTP: Strengths

  • Reliable
  • Lightweight, share-nothing processes
  • OTP, the enterprise libraries
  • Let It Crash

Wrapping up Erlang/OTP: Weaknesses

Left   BMCOL

  • Niche
  • Syntax
  • Integration

Right   BMCOL

/github/seven-languages-in-seven-weeks/media/commit/b149caee47d5cd4f46d33879621e04e72c720d15/slides/Troll_Face_small.png

Final Thoughts

Erlang does seem to be gathering momentum because it solves the right problems in the right way at the right time.