FOP - Funksjonell objektprogrammering


søndag 14. april 2013 OOP Funksjonell programmering

For 17 år siden startet jeg med objektorientert programmering - jeg har det i blodet, og koder på den måten hver arbeidsdag (i alle fall ikke langt unna). Men for noen år siden begynte jeg også å se på funksjonell programmering, og denne bloggen har etterhvert fått mer og mer innhold om funksjonelle språk og teknikker.

Disse to paradigmene kan til en viss grad defineres ut fra forskjellen mellom dem; dvs. at de er i konflikt med hverandre, de har avvikende meninger om hvordan man skal designe løsninger. Etterhvert har jeg fått en viss forståelse for denne konflikten, eller mer presist jeg forstår mer og mer om hvilke tradeoffs de to paradigmene har i forhold til hvordan de vil jeg skal utføre jobben min.

Det er dette som får meg til å skrive denne teksten, hvor jeg vil kaste ut noen tanker om forholdet mellom paradigmene, og om hvordan de kan og bør sameksistere.

Kompleksitet

"Anyone can make the simple complicated. Creativity is making the complicated simple." – Charles Mingus (innflytelsesrik jazzmusiker)

Programmering er å administrere kompleksitet. Vi skal løse et problem. Vi kan si at problemet uansett har en viss mengde kompleksitet, og beskrivelsen av løsningen tilfører ytterligere kompleksitet. Vår jobb er å strukturere denne kompleksiteten på en slik måte at den er enklest mulig å forstå, enklest mulig å verifisere (korrekthet), og enklest mulig å endre/bygge videre på. Alt dette samtidig som at løsningen må være god nok – da tenker jeg blant annet på krav til ytelse.

Og det er her de ulike programmeringsparadigmene og teknikkene kommer inn og tilbyr ulike måter å pakke inn kompleksiteten på.

OOP

Det objektorientert programmering tilbyr er å pakke inn kompleksitet i objekter. Internt i objektene kan det være mye kompleksitet, men de presenterer et tydelig grensesnitt til dem som skal forholde seg til objektene. Det som skjer på innsiden har ingen utenforstående noe med. Dette gjør programmer enklere å forstå, endre osv. Gitt at det er gjort på den "riktige" måten.

Men i tillegg til den skjulte kompleksiteten internt i objektene uttrykkes den viktige kompleksiteten for programmet i hvordan objektene samhandler med hverandre. Altså: Objekter er noe som er enkelt å forstå i seg selv – de er tydelige på hva de gjør – men når de brukes sammen med andre skapes det et komplekst nettverk av interaksjon.

Eller som foreleseren kanskje ville ha sagt det: En bil er en bil, og den er enkel å bruke. Du behøver ikke forstå hvordan den virker. Men når alle skal kjøre hjem klokken 16:00 oppstår det stor kompleksitet som kan være vanskelig å forstå.

I OOP har vi enkle objekter, men tillater kompleksitet i hvordan de fungerer sammen. For å forstå et objektorientert program må man kjenne samhandlingen mellom objektene, og hvilke bi-effekter disse samhandlingene har.

FP

Den sentrale enheten i funksjonell programmering er funksjonen. Og den ideelle funksjonen er enkel. Blant annet fordi den er en matematisk funksjon – gitt et bestemt input gir den alltid samme output. Funksjonen henger ikke på et objekt, og er ikke avhengig av tilstand.

Konvensjonen i funksjonell programmering er derimot å akseptere mer kompleksitet i dataene som funksjonene skal jobbe på. Og funksjonene har mer intim kunnskap om dataene de mottar. Når man skal forstå et typisk program fra den funksjonelle paradigmen må man vite mye mer om strukturen på dataene som flyter gjennom systemet. Men å forstå hvordan funksjonene samhandler er enklere.

OOPvsFP

Ingen av paradigmene kan altså fjerne kompleksitet, men den pakkes inn og plasseres på ulike steder.

OOP ∪ FP

Jeg har tidligere fortalt om hvordan min C#-kode de siste årene har utnyttet mer og mer av de funksjonelle egenskapene til språket. Og Rich Hickey har fortalt at han i flere år kun brukte den funksjonelle paradigmen når han kodet i C# (før han lagde Clojure). Han lagde da kun statiske klasser med statiske metoder, som i prinsippet blir som funksjonelle moduler av funksjoner uten tilstand. Det går altså an å bevege seg langt inn å bevege seg inn i FP fra OOP.

Det finnes også dem som benytter en funksjonelt språk som Scheme på en sånn måte at de programmerer objektorientert. Alt man tranger for å lage objekter er jo tross alt funksjoner med frie variabler.

Men det finnes en rekke språk som er designet for å forene og utnytte begge paradigmene. Til en viss grad gjør kanskje de fleste moderne programmeringsspråk dette. Men de mest åpenbare er språk som F#, Scala og Clojure.

Disse språkene inneholder altså elementer fra begge verdener. Samtidig som de typisk gjør det enklest å løse et problem på én måte, så tillater de den andre. Disse språkene blir som en union av OOP og FP.

Personlig synes jeg dette er utfordrende. Med mange muligheter får man også muligheten til å rote det til. Jeg vingler mellom OOP og FP når jeg programmerer, og den som skal lese og forstå koden må forholde seg til et større sett med teknikker og abstraksjoner.

OOP ∩ FP

Hva om man i stedet kombinerte de to paradigmene på en sånn måte at man håndhevet det beste fra dem begge?

I går kom jeg over en artikkel som het Functional programming in object oriented languages. Her forteller Simon Harris om en reise ikke helt ulik min egen – han programmerer i utgangspunktet objektorientert, men har fått mer og mer interesse for den funksjonelle paradigmen.

Simon har derimot gjort noen litt andre observasjoner og konklusjoner enn meg. Han er glad i OO, og bruker i større grad FP til å forbedre sin objektorientering. Gjennom å være disiplinert følger han flere av "reglene" fra funksjonell programmering i sine objektorienterte design.

Han bruker altså ikke OOP der hvor det er best og FP der det er best, men smelter det sammen til en kombinert paradigme. Han opererer i skjæringspunktet mellom OOP og FP.

OOPvsFP2

Dette fikk meg til å tenke tanker jeg ikke har tenkt før. Hvordan ville et språk som kombinerte OOP og FP på denne måten se ut? Hvordan ville det vært å programmere i det?

Immutable objects

Hva om språket ikke tillot at objektenes data endret seg? For en C#-utvikler betyr det at alle felt ville vært merket med readonly. Det er dette vi ofte kaller for Value Objects. Kunne du programmert kun med slike objekter?

De som kjenner funksjonell programmering vil si "ja", dette er mulig.

I artikkelen snakker Simon mye om egenskapene til et slikt designvalg; at de f.eks. gir et tydeligere skille mellom commands og queries (CQS, et prinsipp fra objektorientert programmering). Og at slike objekter danner grunnlaget for såkalte persistente datastrukturer (som er sentrale for ytelsen i språk som Clojure).

Hva om språket også håndhevet at alle metodene på objektet måtte bruke objektets tilstand på en eller annen måte? Altså at det ikke er lov med metoder som kunne vært statiske. Vil dette kunne føre til god objektorientering?  Vil det føre til mer komplett test coverage? Vil det gi kode som er enklere å forstå og endre? Simons erfaringer kan tyde på dette.

Tankene er nokså ferske, men jeg kunne godt tenkt meg å forsøke å kode i et slikt språk. Som Simon kan jeg selvfølgelig være disiplinert og forsøke å følge disse prinsippene når jeg jobber i OO-språk. Men egentlig fikk jeg mest lyst til å lage et nytt språk.

Kanskje jeg kan gjøre en mellomting og bruke Lisp med et sett med makroer som gjør denne måten å kode på enkelt?

Konklusjon

De ulike programmeringsparadigmene handler om ulike måter å strukturere kompleksitet og gjøre den håndterbar. Paradigmene har ulike tradeoffs: OOP tilbyr en objektabstraksjon som skjuler kompleksitet, men man må forstå hva som skjer når objektene samhandler. FP tilbyr funksjoner som er enkle å forstå, men man må forstå mere komplekse datastrukturer og datatransformasjoner (eller funksjonskomposisjoner).

Språk som kombinerer OOP og FP i dag gjør det ofte (slik jeg ser det) mulig å skrive kode som kombinerer "det værste" fra begge verdener. Hva om vi hadde et språk som oppfordret til å kun kombinere "de beste" elementene? FOP – funksjonell objektprogrammering!  Det ville vært et strengere språk som gav mange føringer til hvordan man skal kode. Men jeg tenker det det hadde vært spenende å prøve.


comments powered by Disqus