CPL 2016, week 8 Erlang functional core and agents Oleg Batrashev - - PowerPoint PPT Presentation

cpl 2016 week 8
SMART_READER_LITE
LIVE PREVIEW

CPL 2016, week 8 Erlang functional core and agents Oleg Batrashev - - PowerPoint PPT Presentation

CPL 2016, week 8 Erlang functional core and agents Oleg Batrashev Institute of Computer Science, Tartu, Estonia March 28, 2016 Overview Today Erlang: functional core and agents Next week Erlang fault tolerance and distributed


slide-1
SLIDE 1

CPL 2016, week 8

Erlang functional core and agents Oleg Batrashev

Institute of Computer Science, Tartu, Estonia

March 28, 2016

slide-2
SLIDE 2

Overview

Today

◮ Erlang: functional core and agents

Next week

◮ Erlang fault tolerance and distributed programming

slide-3
SLIDE 3

Functional core 3/26

  • Shell and statements

◮ statements must end with a dot X=3+5. ◮ Erlang shell erl

N> user input

  • utput

1> 5 + 4. 9 2> 7 + 8*4. 39

slide-4
SLIDE 4

Functional core 4/26

  • Erlang variables

◮ start with uppercase letter Var=5. ◮ single assignment: X=5. X=4. → exception

◮ bound/unbound ◮ X=2+2. X=4. is ok

◮ = is a pattern matching operator (Lhs = Rhs)

◮ X and Y are assigned in {X, _, Y}= Z

◮ almost everything in this lecture is declarative: structures are

immutable, etc, except:

◮ io:format is not because it has side effects ◮ mailboxes in the next part of the lecture introduce

non-determinism

slide-5
SLIDE 5

Functional core 5/26

  • Types and values

◮ atoms (symbolic constants): X=monday. Ret=ret_success.

◮ start lowercase or within single quotes: 'Monday'

◮ tuples: Point={point, 10, 45}.

◮ extracting {point, _, Y}=Point.

◮ lists: L=[10,6,4].

◮ extracting [H|T]=L. results in H=10. L=[6,4].

◮ strings are lists of integers: [104,97,112|_]="happy".

◮ may use dollar sign 104=$h.

slide-6
SLIDE 6

Functional core 6/26

  • Records

◮ declare, only in .hrl file:

  • record(todo, {status=remind,who="oleg",text}).

◮ label, {key1 = default1, key2, . . . }

◮ load declaration to shell: rr("myrecords.hrl"). ◮ create: X1=#todo{text="lecture slides"}. ◮ update: X2=X1#todo{status=done}.

◮ declarative - new record is created

◮ extract: #todo{who=W, text=Txt}=X2. ◮ records are syntactic convinience - tuples are behind the scene

◮ rf(todo).X2. shows it as {todo,done,"oleg","lecture slides"}

slide-7
SLIDE 7

Functional core 7/26

  • Modules
  • module(geometry ).
  • export([area /1]).

area({rectangle , Width , Hgt}) -> Width*Hgt; area({circle , R}) -> 3.14159*R*R. ◮ function consists of clauses, separated by semicolon

◮ case with matched pattern is executed (like in Haskell)

◮ compile (geometry.erl) and run 77> c(geometry ). {ok ,geometry} 78> geometry:area({rectangle ,10 ,5}). 50 79> geometry:area({circle ,1.4}). 6.157516399999999

slide-8
SLIDE 8

Functional core 8/26

  • Functions

◮ functions with same name but different arity (number of

arguments) are different (cost/1, cost/2,..)

  • module(shop ).
  • export([cost /1]).

cost(oranges) -> 5; cost(newspaper) -> Cost = 8, io:format("newspaper cost is ~p~n", [Cost]), Cost; cost(apples) -> 2. ◮ clauses are matched top-down

◮ semicolon (;) separates clauses ◮ period (.) separates functions (and statements in erlang shell) ◮ comma (,) separates statements inside function

slide-9
SLIDE 9

Functional core 9/26

  • Iteration

◮ Like in other functional languages its recursion

  • module(shop ).
  • export([cost/1, total /1]).

... total([]) -> 0; total([H|T]) -> cost(H)+ total(T). ◮ test it in shell (f() – forgets all definitions) 98> f().

  • k

99> c(shop ). {ok ,shop} 100> ShopList=[oranges ,newspaper]. [oranges ,newspaper] 101> shop:total(ShopList ). newspaper cost is 8 13

slide-10
SLIDE 10

Functional core 10/26

  • Funs and list processing

◮ Funs (anonymous functions) IsEven=fun(X) -> (X rem 2) =:= 0 end.

◮ stored to IsEven variable ◮ called by IsEven(23).

◮ map, filter, reduce 104> C = lists:map(fun(X) -> shop:cost(X) end , ShopList ). newspaper cost is 8 105> lists:filter(IsEven ,C). "\b" 106> lists:foldl(fun(S,X)->S+X end , 0, C). 13

slide-11
SLIDE 11

Functional core 11/26

  • Defining abstractions

◮ there is no for loop in Erlang, in lib_misc.erl

  • module(lib_misc ).
  • export([for /3]).

for(Max , Max , F) -> [F(Max)]; for(I, Max , F) -> [F(I)| for(I+1, Max , F)].

◮ two arguments Max must be equal for the first match to

succeed

◮ test it in shell 119> c(lib_misc ). {ok ,lib_misc} 120> lib_misc:for (1,10, fun(I)->I*I end ). [1 ,4 ,9 ,16 ,25 ,36 ,49 ,64 ,81 ,100]

slide-12
SLIDE 12

Functional core 12/26

  • List comprehension

◮ make programs shorter by sugaring funs, maps and filters ◮ lets L=[1,2,3,4,5].

◮ map: [2*X || X <- L]. ◮ with filter: [2*X || X <- L, X<3].

◮ Example: qsort qsort([]) -> []; qsort([Pivot|T]) -> qsort([X || X <- T, X < Pivot]) ++ [Pivot] ++ qsort([X || X <- T, X >= Pivot]).

slide-13
SLIDE 13

Functional core 13/26

  • Guards

◮ increase the power of pattern matching max(X,Y) when X > Y -> X; max(X,Y) -> Y. ◮ guard sequence is a series of guards separated by semicolon

◮ at least one must be true

◮ guard is a series of guard expressions separated by comma

◮ all must be true

◮ valid guard expressions are restricted to a subset of Erlang

expressions!

◮ because we want them to be side effect free

slide-14
SLIDE 14

Functional core 14/26

  • Case and if expressions

◮ case has the following syntax case Expression

  • f

Pattern1 [when Guard1] -> Expr_seq1; Pattern2 [when Guard2] -> Expr_seq2; .... end ◮ if expression syntax if Guard1

  • > Expr_seq1;

Guard2

  • > Expr_seq2;

... end

slide-15
SLIDE 15

Functional core 15/26

  • Accumulators

◮ carry values in function arguments trough recursive calls ◮ Example: split a list into odd and even values

  • dds_evens_acc (L) ->
  • dds_evens_acc (L, [], []).
  • dds_evens_acc ([H|T], Odds , Evens) ->

case (H rem 2) of 1 -> odds_evens_acc (T, [H|Odds], Evens ); 0 -> odds_evens_acc (T, Odds , [H|Evens]) end;

  • dds_evens_acc ([], Odds , Evens) ->

{Odds , Evens}. ◮ test 136> lib_misc: odds_evens_acc ([1,4,5,4,5,4]). {[5,5,1],[4,4,4]}

slide-16
SLIDE 16

Agents 16/26

  • Concurrency primitives

◮ declarative Erlang + recursive functions with mailboxes ◮ primitives

◮ create a new concurrent process that runs Fun

Pid = spawn(Fun)

◮ send Message to the process with identifier Pid

Pid ! Message

◮ receive a message that has been sent to a process

receive Pattern1 [when Guard1] -> Expressions1 ; Pattern2 [when Guard] -> Expressions2 ; ... end

slide-17
SLIDE 17

Agents 17/26

  • Processes and mailboxes

◮ every process has one mailbox ◮ when a message arrives and the process is waiting in receive

it tries to match it against patterns

◮ if none succeeds ◮ the message is saved for later processing ◮ next message is tried

◮ it behaves like reading queue with sophisticated receive routine

◮ one thread processing messages, one at a time ◮ Pid !

Message is a source of non-determinism

◮ standard queue does not save anything for later processing

slide-18
SLIDE 18

Agents 18/26

  • Simple example

−module ( area_server0 ) . −export ( [ loop /0 ] ) . loop () − > r e c e i v e { r e c t a n g l e , Width , Ht} − > i o : format ( " Area

  • f

r e c t a n g l e i s ~p~n" , [ Width ∗ Ht ] ) , loop ( ) ; { c i r c l e , R} − > i o : format ( " Area

  • f

c i r c l e i s ~p~n" , [ 3.14159 ∗ R ∗ R] ) , loop ( ) ; Other − > i o : format ( " I don ' t know about ~p ~n" , [ Other ] ) , loop () end .

slide-19
SLIDE 19

Agents 19/26

  • Test simple example

137> c( area_server0 ). {ok , area_server0 } 138> Pid = spawn(fun area_server0 :loop /0). <0.299.0 > 139> Pid ! {rectangle , 6, 10}. Area of rectangle is 60 {rectangle ,6 ,10} 140> Pid ! {circle , 23}. Area of circle is 1661.90111 {circle ,23} 141> Pid ! {triangle ,2,4,5}. I don 't know about {triangle ,2,4,5} {triangle ,2,4,5}

slide-20
SLIDE 20

Agents 20/26

  • Client-server example

◮ lets send extended message to the server Pid ! {self(), {rectangle ,6 ,10}}

◮ self() is the PID of the client process

◮ the server code is now loop () -> receive {From , {rectangle , Width , Ht}} -> From ! Width*Ht , loop (); ...

◮ i.e. we respond with the answer – the client must wait for a

response (this is like Remote Procedure Call)

slide-21
SLIDE 21

Agents 21/26

  • RPC (1)

◮ RPC: a client may execute receive just after it sends a

message and wait for the answer

rpc(Pid , Request) -> Pid ! {self(), Request}, receive Response

  • > Response

end.

◮ the problem: any incoming message may be wrongly

considered as a response

slide-22
SLIDE 22

Agents 22/26

  • RPC (2)

◮ solution: may pattern match on server Pid to filter out other

messages (notice Pid is already bound)

rpc(Pid , Request) -> Pid ! {self(), Request}, receive {Pid ,Response} -> Response end.

◮ matching succeeds if server sends the same Pid

◮ server code: loop () -> receive {From , {rectangle , Width , Ht}} -> From ! {self(), Width*Ht}, loop (); ...

slide-23
SLIDE 23

Agents 23/26

  • Receive with timeout

◮ a message may never come, e.g. because of error, break after

Time has passed

receive Pattern1 [when Guard1] -> Expressions1 ; ... after Time

  • >

Expressions end ◮ receive with timeout 0 is like polling

◮ before terminating checks the mailbox ◮ may use for priority receive

receive {alarm , X} -> ... after 0 -> receive Any

  • > ... end

end.

slide-24
SLIDE 24

Agents 24/26

  • How “receive” works
  • 1. upon enter a receive statement timer is started (if after section

is present)

  • 2. take the first message, try to match all patterns

◮ on success remove the message, execute statements

  • 3. if none matches put it to “save queue”, try next, etc.
  • 4. if no more messages that match then suspend until

◮ only new/arrived messages are tried

  • 5. as soon as one matches:

◮ all “save queue” is reentered to the mailbox ◮ timer is cleared

  • 6. if timer elapses then execute corresponding section:

◮ all “save queue” is reentered to the mailbox

slide-25
SLIDE 25

Agents 25/26

  • Registered processes

◮ it may be more convenient to register a process globally

instead of passing Pid around

◮ the functions:

◮ register(AnAtom,Pid) – register with the name AnAtom ◮ unregister(AnAtom) ◮ whereis(AnAtom) -> Pid | undefined ◮ registered() -> [AnAtom::atom()] – return the list of all

registered processes

slide-26
SLIDE 26

Agents 26/26

  • Tail recursion and performance

◮ all loop() calls come as the last expression

◮ if not then stack grows and indefinite looping (agent life) is

not possible

◮ processes are “user space threads”, i.e. not native threads

◮ cheap without much overhead ◮ Erlang has a limit of 32767 processes but it may be

extended from command line