1 / 42

Erlang

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 .

Télécharger la présentation

Erlang

An Image/Link below is provided (as is) to download presentation Download Policy: Content on the Website is provided to you AS IS for your information and personal use and may not be sold / licensed / shared on other websites without getting consent from its author. Content is provided to you AS IS for your information and personal use only. Download presentation by click this link. While downloading, if for some reason you are not able to download a presentation, the publisher may have deleted the file from their server. During download, if you can't get a presentation, the file might be deleted by the publisher.

E N D

Presentation Transcript


  1. Erlang Functional programming for real-world applications

  2. functions • lists • recursion • “yeah whatever” • runtime datatype

  3. imperative • compute by modifying state • x = x + 2; • overwrite memory “x” • functional • transform • f(x) x + 2 • produces new value

  4. 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, ...

  5. 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

  6. Functionbehaviour = mathematical A function or expression is said to have a side effect if, in addition to producing a value, it alsomodifies some stateorhas anobservable interaction with calling functions or the outside world. int counter = 0; int plustwo( int x ) { counter++; return x+2; } • 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

  7. Lists Mylist = [1,2,3]. Yourlist = [ ]. Things = [ “me”, 1, aap ]. Newlist = [ -1 | Things ]. = [ -1, “me”, 1, aap ].

  8. 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]

  9. 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)

  10. 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)

  11. 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 ]

  12. (tail)recursion sum( [ ] )  0; sum( [Head|Tail] )  Head + sum(Tail). sum( List )  sum(List, 0). sum( [ ], Acc )  Acc; sum( [Head|Tail], Acc )  sum(Tail, Head+Acc). • 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

  13. Variables aren’t! 1. x = 42 2. y = x2 3. x = 3 4. y = x2 ?!? • 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

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

  15. 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)

  16. 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)

  17. 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 = ?

  18. ... 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;

  19. what you want is this: For each item in the shoppinglist multiply the amount by the unit price and sum the results. %% 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}, .... ].

  20. 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

  21. 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]).

  22. 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

  23. 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 connectednodes 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!

  24. In graphical form HostA HostB node1 node1 node2 node2 Supervisor process Worker process

  25. 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 A B C D

  26. 4 frontnode 4 backnode >20 Gbps/link 16x 10Gbps 16x 10Gbps 1Gbit/s ethernet UniBoard

  27. Polyphase Filterbank Nios2 CPU registers registers registers Delay Module registers 1Gbit PHY FPGA VHDL Memorymapped I/O via UDP/IP Hardware Software

  28. FPGA client library one controller one fpga • 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)

  29. FPGA client library Available registerdefinition types for the controller fpga:bit( <name>, <bit #>, <address> ) fpga:bitrange( <name>, <startbit>, <nbit>, <address> ) fpga:word( <name>, <address> ) fpga:range( <name>, <# words>, <address> )

  30. FPGA client library Available register commands fpga:read( <name> ) fpga:write( <name>, <value> ) fpga:or_( <name>, <value> ) fpga:and_( <name>, <value> ) fpga:xor_( <name>, <value> )

  31. FPGA client library FLASH-memory commands fpga:config_read( <address> ) fpga:config_erase( <address> ) fpga:config_write( <address>, <<256bytes>> )

  32. FPGA client library Actually executing FPGA commands 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) ] ).

  33. 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 | PN N N | • +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ • P = PPS_ENABLE (1bit)NNN = NUM_TAP (3bit) • +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ • 0x0000abcc | CONTROL/STATUS REGISTER | • +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

  34. 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 framework registers() -> {ok, [ %% a 3 bit field starting at bit 5 in the word at 0x24 fpga:bitrange(num_tap, 5, 3, 16#24), %% another bit in that same register/word fpga:bit(pps_enable, 28, 16#24), %% one word (32bits) at location 0xabcc fpga:word(control_status, 16#abcc), ] }. %% define a high-level command for this personality start_filter(FPGA, NumTap) -> %% disable the PPS fpga:execute(FPGA, fpga:write(pps_enable, 0)), %% read the control status register case 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 framework registers() -> {ok, [ %% a 3 bit field starting at bit 5 in the word at 0x24 fpga:bitrange(num_tap, 5, 3, 16#24), %% another bit in that same register/word fpga:bit(pps_enable, 28, 16#24), %% one word (32bits) at location 0xabcc fpga:word(control_status, 16#abcc), ] }. %% define a high-level command for this personality start_filter(FPGA, NumTap) -> %% disable the PPS fpga:execute(FPGA, fpga:write(pps_enable, 0)), %% read the control status register case 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)]).

  35. 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; } } }

  36. Ctrl = uniboard:start(“10.99.0.0”). • uniboard:execute(Ctrl, fn0, fpga:read(pps_enable)). 1 • uniboard:execute(Ctrl, [1,6], fpga:read(pps_enable)). [{fn1, 0} , {bn2, 0}] • uniboard:read_sensors(Ctrl, all, fpga_temp). [{fn0, 28}, {fn1, 30}, {fn2, timeout}, .... {bn3, 32}] • uniboard:execute(Ctrl, fn, fpga:read(pps_enable)). [{fn0, 1}, {fn1, 0}, {fn2, timeout}, {fn3, 1}] • uniboard:execute(Ctrl, [fn1, bn2], fpga:write(pps_enable, 1)). [{fn1, ok}, {bn2, ok}] • uniboard:execute(Ctrl, [{fn,1}, {bn,1}], fpga:read(pps_enable)). [{fn1, 1}, {bn1, 0}]

  37. File: uniboard/i2c.erl read_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]).

  38. UniBoard commandpacket Packetformat Generic instruction format Wait-for-1PPS instruction prefix Write config data [program FLASH image]

  39. Available commands Read 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

  40. empty

More Related