Erlang

42
Erlang Functional programming for real-world applications

description

Erlang. Functional programming for real-world applications. functions lists recursion. “ yeah whatever ” runtime datatype. imperative compute by modifying state x = x + 2; overwrite memory “x” functional transform f( x )  x + 2 produces new value. Functions. - PowerPoint PPT Presentation

Transcript of Erlang

Page 1: Erlang

Erlang

Functional programming for

real-world applications

Page 2: Erlang

• functions

• lists

•recursion•“yeah whatever”

– runtime datatype

Page 3: Erlang

• imperative– compute by modifying state

• x = x + 2;• overwrite memory “x”

• functional– transform

• f(x) x + 2• produces new value

Page 4: Erlang

Functions• are “first class citizens”

• can return functions

• can take functions as arguments

• can be assigned to variables

Necessary precondition, NOT sufficient!* javascript, glish (brrrrr!), python, ...

Page 5: Erlang

One function call, multiple bodies• look for name + number of arguments

• notation: “functionname/2”

– not found => EXPLODE!• patternmatch each clause (“body”)

– seems to match?• [arguments satisfy contstraints?]

– do evaluate that clause!• no more clauses?

– EXPLODE!

• sometimes you don’t care– _ is the wildcard; matches anything

QuickTime™ and a decompressor

are needed to see this picture.

Page 6: Erlang

Functionbehaviour = mathematical• result depends on arguments alone• same arguments => same result

– user input? file I/O?

• every statement always yields something• maybe wrong but predictable• less side-effects = more pure

– haskell: very pure• I/O special treatment

– Erlang: pragmatically pure• I/O as you’d expect

A function or expression is said to have a

side effect if, in addition to producing a

value, it also modifies some state or has an

observable interaction with calling functions

or the outside world.

int counter = 0;

int plustwo( int x ) {

counter++;

return x+2;

}

Page 7: Erlang

Lists

Mylist = [1,2,3].

Yourlist = [ ].

Things = [ “me”, 1, aap ].

Newlist = [ -1 | Things ]. = [ -1, “me”, 1, aap ].

Page 8: Erlang

map(Func, List) ListOfResults• transform a list of things into a list of other things

• ingredients:

• 1 List of things

• 1 Function: F(x) y

• produces: [ F(x0), F(x1), ... ], x List

• in fact ... identical to list comprehension

• [ F(X) || X List]

Page 9: Erlang

fold(Func, Init, List) Result• a.k.a “reduce”; reduce a list of things to one value

• ingredients:

• 1 List of things

• 1 Initial value

• 1 Function F(x, y) z

• yields: F(Init, F(x0, F(x1, ..))), x List (leftfold)

• or: F(F(F(..., xn-1), xn), Init), x List (rightfold)

Page 10: Erlang

F(x,y) x + y, Init = 0, List=[1,2,3]

Init [x0, x1, x2]

F(Init, x0) F0

F0 [x1, x2]

F(F0, x1) F1

F1 [x2]

F(F1, x2) F2

0 [1, 2, 3]

F(0, 1) (0+1)

F0 [2, 3]

F(F0, 2) ((0+1) + 2)

F1 [3]

F(F1, 3) ((0+1)+2)+3)

Page 11: Erlang

F(x,y) [x | y], Init = [], List=[1,2,3]

[1, 2, 3] []

F(3, []) [3 | [ ] ]

[1, 2] F0

F(2, F0) [ 2 | [ 3 | [ ] ] ]

[1] F1

F(1, F1) [ 1 | [ 2 | [ 3 | [ ] ] ] ]

[x0, x1, x2] Init

F(x2, Init) F0

[x0, x1] F0

F(x1, F0) F1

[x0] F1

F(x0, F1) F2

A list can be recursively described as:

“a list is an element followed by another list”

[ Element | List ] [ Head | Tail ]

Page 12: Erlang

(tail)recursion• typical ingredients:

• 1 List of things

• 1 recursive function

• sometimes secret ingredients:

• helper function having extra argument (accumulator)

• never runs out of stackspace

• example:

• summing the values of all elements in a list

sum( [ ] ) 0;

sum( [Head|Tail] ) Head + sum(Tail).

sum( List ) sum(List, 0).

sum( [ ], Acc ) Acc;

sum( [Head|Tail], Acc ) sum(Tail, Head+Acc).

Page 13: Erlang

Variables aren’t!• mathematical sense• single “assignment”

– ‘=‘ matches, not assign

• placeholders, not memorylocations– bind to expression

• exclusively functionscope• 60% (or more) bug reduction!

– most variables are transient, you don’t really need them

• no global state– even more bug reduction– lockfree multithreading

1. x = 42

2. y = x2

3. x = 3

4. y = x2 ?!?

Page 14: Erlang

WTF?! No loops?• recursion is the new for/while/do• stackspace?!• tail recursion + side-effect free = stackframe reuse

QuickTime™ and a decompressor

are needed to see this picture.

Page 15: Erlang

Datatypes or rather: terms

• 0-9eE+-. (numbers)

• [ <term>, ... ] (list)

• { <term>, ... } (tuple)

• fun( ... ) <expression> end. (functions)

• “ :alphanum: “ (strings)

• lowercase or ‘:alphanum:’ (atom)

• Uppercase (‘variable’)

• << V:n >> (n bits representing V)

Page 16: Erlang

Common idioms• somefunction(...)

{ok, <succesreturnvalue> }error

• otherfunction( X, Y )

case somefunction(X+Y) of error ‘oh noes!’; {ok, Value} 42 + Value

end.

• {ok, Result} = somefunction( ... ), otherfunction(Result)

Page 17: Erlang

A pragmatic excercise

Shopping list

3 oranges

2.6 l milk

2 bananas

250 cookies

Unit price

uniboard 20000

apples 0.26

bananas 3.14

oranges 0.65

milk 1.15

cookies 0.06

Total amount = ?

Page 18: Erlang

... typically you do this:

* a struct/class with members

• type: enum {orange, apple, banana, ...}

• maybe ‘char*’ or std::string

• price/amount: float? double? (template?!)

• for shopping list, unit price list:

• use array []? vector? list?

need initializing

• array/vector - how did it work again?

• struct itemdesc shoplist[] = { (banana, 2.0), ... };

• vector<itemdesc> vd( shoplist, (sizeof(shoplist)/sizeof(shoplist[0]))+1);

• etc.

• iterate over the vector/ for(i=0; i<num_items; i++) ...

• find unit price in pricelist (need to write function)

• multiply by amount, add to sum

• oh yeah, do not forget

• double sum = 0.0;

Page 19: Erlang

what you want is this:%% Both the shopping + pricelist are lists

%% of 2-element tuples: an atom + a number

ShoppingList = [ {3, oranges}, {2.5, milk}, ...].

PriceList = [ {uniboard, 20000}, {apples, 0.26}, .... ].For each item in the shoppinglist multiply the amount by the unit

price and sum the results.

Page 20: Erlang

Erlang specific• pure enough, yet pragmatic

– sockets/file/user I/O simple– database connectivity (MySQL)

• pattern matching on binary data– protocol analysis– decoding data

• working with binary data per se– arbitrary N-bit values– splitting/joining chunks of binary data– conversion to/from other datatypes

• distributed fault-tolerant systems

Page 21: Erlang

Binary pattern matching%% clause 1: binary block which

%% is at least 2*32bits long AND

%% first 32bits read 0xABADDEED

%% clause 2: binary block starting with 0xFFFFFFFF

decode( << 16#abaddeed:32, SeqNo:32/unsigned-little-integer, _/binary >> )

io:format(“Mark5B DiskFrameHeader, SeqNr ~w~n”, [SeqNo]);

decode( << 16#ffffffff:32, _/binary >> )

io:format(“MarkIV tapeframeheader syncword~n”).

%% 14 bit color-space with 5 bits Red, 5 bits Green and 4 bits Blue

%% match-and-extract in one operation

<< Red:5, Green:5, Blue: 4, _/bitstring >> ) = Blob,

%% The symbols “Red” “Green” and “Blue” are usable as numbers here!

io:format(“RGB[~p, ~p, ~p]~n”, [Red, Green, Blue]).

Page 22: Erlang

Processes• very lightweight “threads” of execution

– lockfree– may map onto O/S threads– extremely cheap to create

• come for free– no side-effects– no global/shared state

• interact with outside world/each other– only via message passing– e.g. sockets send messages to their owner

• tell apart by Process-ID– so fundamental it’s a built-in type– Erlang knows where a process is running

Page 23: Erlang

Distributed Erlang system

• processes run inside Erlang nodes• nodes run on hosts• nodes are identified by their nodename • 1 host may run multiple nodes

– nodename unique ONLY per host: nodename@host

• the set of connected nodes is the system• “registered processes”

– bind a name to a process on a node– send messages to that name

• you don’t have to know where the process is; Erlang does!

• unbelievably trivial to use/build!

Page 24: Erlang

In graphical form

HostA HostB

node2

node2

node1

node1

Supervisor process

Worker process

Page 25: Erlang

Fault tolerant• processes can be linked• crash? ‘EXIT’ signal propagates along links• ‘EXIT’ signal is just a message• allows one process to monitor one or more other processes

QuickTime™ and a decompressor

are needed to see this picture.

A

C

B

D

QuickTime™ and a decompressor

are needed to see this picture.

Page 26: Erlang

UniBoard

QuickTime™ and a decompressor

are needed to see this picture.

4 frontnode

4 backnode

1Gbit/s ethernet

16x 10Gbps

16x 10Gbps

>20 Gbps/link

Page 27: Erlang

Memorymapped I/O via UDP/IP

FPGA

1Gbit PHY

VHDLHardwareSoftware Polyphase Filterbank

registers

Delay Module

registers

Nios2CPU

registers

registers

Page 28: Erlang

QuickTime™ and a decompressor

are needed to see this picture.

Page 29: Erlang

FPGA client library

Registermap:

• type of the register (details follow)

• name of the register (symbolic)

• address of the register in the memory map

How to communicate with the FPGA:

• UDP/IP

• needs IP-address

• emulator (for testing/playing)

one controller one fpga

Page 30: Erlang

FPGA client library

fpga:bit( <name>, <bit #>, <address> )

fpga:bitrange( <name>, <startbit>, <nbit>, <address> )

fpga:word( <name>, <address> )

fpga:range( <name>, <# words>, <address> )

Available registerdefinition types for the controller

Page 31: Erlang

FPGA client library

fpga:read( <name> )

fpga:write( <name>, <value> )

fpga:or_( <name>, <value> )

fpga:and_( <name>, <value> )

fpga:xor_( <name>, <value> )

Available register commands

Page 32: Erlang

FPGA client library

fpga:config_read( <address> )

fpga:config_erase( <address> )

fpga:config_write( <address>, <<256bytes>> )

FLASH-memory commands

Page 33: Erlang

FPGA client library

Controller = fpga:fpga(Personality, Protocol, [Options]),

fpga:execute(

Controller,

[ fpga:write(pps_enable, 0),

fpga:write(config_n_sta, 8),

fpga:write(pps_enable, 1) ]

).

Actually executing FPGA commands

Page 34: Erlang

Example: firfilter 3 2 1 0 ADDRESS 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0

+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+0x00000024 | P N N N | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

P = PPS_ENABLE (1bit) NNN = NUM_TAP (3bit)

+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+0x0000abcc | CONTROL/STATUS REGISTER | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

Page 35: Erlang

File: firfilter.erl-module(firfilter). %% this module is called firfilter, driving one of those-import(fpga). %% enables us to use functions from the client library-behaviour(fpga.personality). %% this module implements an FPGA personality

%% Return the list of registers defined for this personality.%% Called automatically by the FPGA control frameworkregisters() -> {ok, [

%% a 3 bit field starting at bit 5 in the word at 0x24fpga:bitrange(num_tap, 5, 3, 16#24),%% another bit in that same register/wordfpga:bit(pps_enable, 28, 16#24),%% one word (32bits) at location 0xabccfpga:word(control_status, 16#abcc),

] }.

%% define a high-level command for this personalitystart_filter(FPGA, NumTap) ->

%% disable the PPSfpga:execute(FPGA, fpga:write(pps_enable, 0)),%% read the control status registercase fpga:execute(FPGA, fpga:read(control_status)) of

%% if the control_status register is “1” (zero)%% we must first XOR it with “42” ...1 -> fpga:execute(FPGA, fpga:xor(control_status, 42));%% otherwise do nothing_ -> ok

end,%% now write the number of taps into the register for that and immediately%% after that start the PPS again. So far we only used single commands,%% however, you can easily execute a number of commands in one go:fpga:execute(FPGA, [fpga:write(num_tap, NumTap),

fpga:write(pps_enable, 1)]).

-module(firfilter). %% this module is called firfilter, driving one of those-import(fpga). %% enables us to use functions from the client library-behaviour(fpga.personality). %% this module implements an FPGA personality

%% Return the list of registers defined for this personality.%% Called automatically by the FPGA control frameworkregisters() -> {ok, [

%% a 3 bit field starting at bit 5 in the word at 0x24fpga:bitrange(num_tap, 5, 3, 16#24),%% another bit in that same register/wordfpga:bit(pps_enable, 28, 16#24),%% one word (32bits) at location 0xabccfpga:word(control_status, 16#abcc),

] }.

%% define a high-level command for this personalitystart_filter(FPGA, NumTap) ->

%% disable the PPSfpga:execute(FPGA, fpga:write(pps_enable, 0)),%% read the control status registercase fpga:execute(FPGA, fpga:read(control_status)) of

%% if the control_status register is “1” (zero)%% we must first XOR it with “42” ...1 -> fpga:execute(FPGA, fpga:xor(control_status, 42));%% otherwise do nothing_ -> ok

end,%% now write the number of taps into the register for that and immediately%% after that start the PPS again. So far we only used single commands,%% however, you can easily execute a number of commands in one go:fpga:execute(FPGA, [fpga:write(num_tap, NumTap),

fpga:write(pps_enable, 1)]).

Page 36: Erlang

int main() { clock_t nof_ticks_report; TUInt32 aTest[4] = {0, 0, 0, 0}; // prepare for entering mainloop nof_ticks_report = clock(); while( 1 ) { clock_t now = clock(); // Task: Ethernet traffic if( (ptPacket=NET_Receive())!=NULL ) handlePacket(ptPacket);

// Report if( (now-nof_ticks_report)>=2000 ) { printf("aTest[0x%08lx] %08lx %08lx\n", &aTest[0], aTest[0], aTest[1]); nof_ticks_report = now; } }}

Page 37: Erlang

1. Ctrl = uniboard:start(“10.99.0.0”).

2. uniboard:execute(Ctrl, fn0, fpga:read(pps_enable)).

1

3. uniboard:execute(Ctrl, [1,6], fpga:read(pps_enable)).

[{fn1, 0} , {bn2, 0}]

1. uniboard:read_sensors(Ctrl, all, fpga_temp).

[{fn0, 28}, {fn1, 30}, {fn2, timeout}, .... {bn3, 32}]

2. uniboard:execute(Ctrl, fn, fpga:read(pps_enable)).

[{fn0, 1}, {fn1, 0}, {fn2, timeout}, {fn3, 1}]

3. uniboard:execute(Ctrl, [fn1, bn2], fpga:write(pps_enable, 1)).

[{fn1, ok}, {bn2, ok}]

4. uniboard:execute(Ctrl, [{fn,1}, {bn,1}], fpga:read(pps_enable)).

[{fn1, 1}, {bn1, 0}]

Page 38: Erlang

File: uniboard/i2c.erlread_sensors(FPGA, Sensors) -> %% From the sensorslist, build the contents of the I2C %% protocol region (the commands to read them) I2CProto = mk_i2cproto(Sensors), %% We must write that to the i2c-command region of the memory-mapped %% i2c master on the fpga. then we must write the activate bit %% in the command register. Actually, we must make that bit go %% through an 0 -> 1 transition, which we will force by successively %% writing a 0 and then a 1. Force all actions to return "ok". %% (or rather, explode if they don't) I2CResult = case fpga:execute(FPGA, [fpga:write(i2c_proto, I2CProto), fpga:write(i2c_activate, 0), fpga:write(i2c_activate, 1)]) of [ok, ok, ok] -> %% Having done that, now, in theory, %% we should wait for the i2c interrupt %% to appear. Let's wait 1/5th of a second ... timer:sleep(200), %% Now read the full result region and extract the %% values the i2c cruft has given us fpga:execute(FPGA, fpga:read_binary(i2c_result)); [timeout,timeout,timeout] -> %% Bollox. A timeout. Let's pass this on timeout %% No other cases - the code should explode in this case %% since something happened that we don't expect. %% The FPGA commands should either succeed or timeout. end, decode_i2c_result(I2CResult, Sensors).

decode_i2c_result([Word|MoreWords], [_Sensor|MoreSensors], Acc) -> %% The i2c result is returned as 1 byte of value and %% one byte status. %% Status==0 => read succeeded, %% Status==1 => read failed {Value, Remain} = case split_binary(Word, 2) of {<<_:8, 1:8>>, R1} -> {fail, R1}; {<<V:8/unsigned-little, 0:8>>, R2} -> {V, R2} end, decode_i2c_result([Remain|MoreWords], MoreSensors, [Value|Acc]).

Page 39: Erlang

UniBoard commandpacketPacketformat

Generic instruction format

Wait-for-1PPS instruction prefix

Write config data [program FLASH image]

Page 40: Erlang

Available commandsRead N 32bit words starting from START ADDRESS

(Over)Write N 32bit words from the packet to START ADDRESS

Read/Modify/Write N 32bit words from START ADDRESS + packet to START ADDRESS

32 bits 32 bits 32 bits 0x01 N START ADDRESS

32 bits N * 32 bits | 0 bits

(!)START ADDRESS DATA

32 bits 32 bits 32 bits N*32 bits 0x02 N START ADDRESS N 32 bit words of DATA

32 bits

(!)START ADDRESS

32 bits 32 bits 32 bits N*32 bits OPCODE N START ADDRESS N 32bit words of MASK

OPCODE 0x03 0x04 0x05

BITWISE OPERATION AND OR XOR

32 bits

(!)START ADDRESS

Page 41: Erlang

empty

Page 42: Erlang