En introduksjon til Erlang


onsdag 21. april 2010 Erlang Actor model

Dette er min første artikkel om Erlang, men det blir ikke den siste. Jeg har nettopp lært meg dette språket, og dermed begynt min reise innen funksjonsbasert programmering. Mine blogposter om Erlang er ikke skrevet for dem som kan dette fra før, men retter seg i stedet mot utviklere som meg selv – som fra tidligere bare har erfaring med objektorientering, men som er nysjerrige på teknikker og teknologier som kan gjøre dem til bedre utviklere.

erlang-logo

For litt over en måned siden opplevde jeg noe jeg nesten må kalle en religiøs opplevelse. Jeg hadde nok sovet litt for lite, og pub-turen kvelden før hadde vært veldig bra. Den totale mengden mentalt stimuli de siste dagene hadde også vært enorm. Men da jeg fikk oppleve Joe Armstrong på scenen på QCon London ble jeg uansett totalt overveldet.

Det jeg fikk se var et vakkert programmeringsspråk. Det var overraksende elegant i form av hvor enkelt det var, og sjokkerte meg gjennom å påvirket mitt syn på både programmering og verden forøvrig. Erlang fikk meg til å se sammenhenger jeg ikke var klar over eksisterte.

Jeg vet ikke hvordan jeg skal formidle min opplevelse, eller om det i det hele tatt er mulig. Gjennom denne og kommende blogposter vil jeg beskrive Erlang slik jeg opplever det, og så håper jeg bare at noe av min åpnebaring skinner gjennom.

En parallell verden

Erlang kategoriseres som et funksjonsbasert språk (på lik linje med f.eks. Haskell, OCaml og Scheme). Utviklerne av språket hentet derimot mye av inspirasjonen fra Prolog, et logisk programmeringsspråk, og det bærer det preg av. Første versjon av Erlang var faktisk implementert i Prolog.

Joe Armstrong, en av utviklerne av Erlang, kaller det for samtidsorientert programmering (Concurrency-oriented programming, COP). Erlang er nemlig basert på Actor model, et prinsipp for samtidighet/parallellitet. Et typisk program består av en hel rekke – kanskje flere tusen – lettvekts-prosesser. Prosessene kommuniserer ved å sende meldinger til hverandre, eller mer presist til hverandres mailbox. Joe forklarer nærmere:

"The world is parallel. If we want to write programs that behave as other objects behave in the real world, then these programs will have a concurrent structure. Use a language that was designed for writing concurrent applications, and development becomes a lot easier. Erlang programs model how we think and interact."

Dette synes jeg er så viktig at jeg skal våge meg på en aldri så liten, visuell illustrasjon..

its_an_erlang_world

Samtidighet og parallellitet er veldig populære tema for tiden, og de fleste har fått med seg at dette bare kommer til å bli viktigere fremover (The Free Lunch Is Over). Erlang kan være én vei å gå for å møte denne utfordringen:

"In Erlang, there is no mutable state, there is no shared memory, and there are no locks. This makes it easy to parallelize our programs." – Joe Armstrong

En smakebit

Det er på høy tid med en liten smakebit. Her er en Erlang-funksjon for å kalkulere arealet av et rektangel, et kvadrat, eller en sirkel.


1 area({rectangle, Width, Height}) -> Width * Height;
2 area({square, Side})             -> Side * Side;
3 area({circle, Radius})           -> 3.14159 * Radius * Radius.

Funksjonen har tre ulike "innganger", og Erlang bruker mønstergjenkjenning til å avgjøre hvilken den skal bruke. Input til funksjonen er i dette tilfellet tupler (touple), en vanlig datatype i funksjonsbaserte språk. Hvis det første elementet er atomet "rectangle", etterfulgt av to vilkårlige variabler, så vil den første inngangen til funksjonen matche, og derfor benyttes (atom er en annen datatype i erlang, og minner mye om symbol i Ruby, og brukes mye på samme måte som enums i C#).

Sammenligner vi de tre linjene Erlang-kode med en typisk objektorientert implementasjon av det samme så kan vi begynne å få et inntrykk av hvor elegang det er.

En objektorientert tilnærming i Java:
1 abstract class Chape {
2   abstract double area();
3 }
4
5 class Circle extends Shape {
6   final double radius;
7   Circle(double radius) { this.radius = radius; }
8   double area() { return Math.PI * radius * radius; }
9 }
10
11 class Rectangle extends Shape {
12   final double height;
13   final double width;
14   Rectangle(double width, double height) {
15     this.height = height;
16     this.width = width;
17   }
18   double area() { return width * height; }
19 }
20
21 class Square extends Shape {
22   final double side;
23   Square(double side) {
24     this.side = side;
25   }
26   double area() { return side * side; }
27 }

Legg merke til hvordan Erlang samler definisjonen av areal – uansett type – i én funskjon, mens vi i en typisk OO-løsning splitter dette opp i ulike objekter. Og hvis noen har lyst å påstå at jeg sammenligner epler og poteter (igjen), så er jeg villig til å ta den diskusjonen..

Et mer reelt eksempel

erlang_actor

Her følger en helt grunnleggende "actor" modul. Modulen eksponerer to funksjoner: start og area. Merk at koden for både klientsiden og serversiden befinner seg i den samme filen.

Start (linje 4) "spawner" en ny prosess som vil kjøre funksjonen loop (linje 15). Loop står og lytter etter innkommende meldinger den kan håndtere. Kaller man så area-funksjonen så sender man en melding til loop-prosessen (via rpc-funksjonen). Loop-prosessen mottar meldingen, bruker mønstergjenkjenning til å beregne riktig areal, og sender en ny melding tilbake til den prosessen forespørselen kom fra. Til slutt ankommer den nye meldingen i receive-blokken i rpc-metoden, og svaret kan returneres i area-funksjonen.


1 -module(area_server).
2 -export([start/0, area/2]).
3
4 start() -> spawn(fun loop/0).
5
6 area(Pid, What) -> rpc(Pid, What).
7
8 rpc(Pid, Request) ->
9   Pid ! {self(), Request},
10   receive
11     {Pid, Response} ->
12       Response
13   end.
14
15 loop() ->
16   receive
17     {From, {rectangle, Width, Height}} ->
18       From ! {self(), Width * Height},
19       loop();
20     {From, {circle, Radius}} ->
21       From ! {self(), 3.14159 * Radius * Radius},
22       loop();
23     {From, Other} ->
24       From ! {self(), {error, Other}},
25       loop()
26   end.

Det er ikke så rart om du ikke skjønner det med en gang (forklaringen min var helt overfladisk). Men studer koden litt likevel, for her ser du selve fundamentet for hvordan Erlang fungerer.

What else..?

Jeg kunne ha fortalt mye mer om hvordan språket er bygget opp – for eksempel om hvordan det ikke har løkker, normale if-else-blokker, eller at det ikke engang har en egen datatype for strenger, men klarer seg helt fint uten – men jeg tror det rekker nå. Det kommer som sagt flere blogposter med mere detaljer etterhvert. La meg bare få avslutte med å si at jeg absolutt vil anbefale utviklere å lære seg Erlang for det unike perspektivet språket vil kunne gi deg.

Og i forhold til utvikling av løsninger med høy grad av samtidighet og store krav til stabilitet kan Erlang være et uvurderlig verktøy.

Bonus: Møt utviklerne

CropperCapture[2] Utviklings-teamet bak Erlang lagde på 80-tallet en video for å demonstrere og reklamere for det fantastiske språket de hadde laget. Den er utrolig morsom – og det er helt ufrivillig. Jeg anbefaler alle å ta en titt, men må advare om at man kan miste all respekt for de involverte i løpet av de drøye 10 minuttene filmen varer. Erlang: The Movie.

Linker: Offisiell webside | wikipedia | www.trapexit.org (community site)


comments powered by Disqus