Til hovedinnhold
Kaffe på et bord forran en laptop

Funksjonell programmering i Enonic XP #1 - Intro

Publisert: 13. desember 2019 av Tom Arild Jakobsen

I programmeringsverdenen har det vært en trend mot funksjonell programmering (FP) de siste årene. Vi skal nå se hvordan man kan bruke FP-konsepter til å skrive mer robust serversidekode i Enonic XP.

Artikkelnummer Andre artikler i denne serien
1 Funksjonell programmering i Enonic XP #1 - Intro
2 Funksjonell programmering i Enonic XP #2 - TypeScript
3 Funksjonell programmering i Enonic XP #3 - Option
4 Funksjonell programmering i Enonic XP #4 - Either
5 Funksjonell programmering i Enonic XP #5 - IO
6 Funksjonell programmering i Enonic XP #6 - Eksempler

Hva er funksjonell programmering?

En definisjon av funksjonell programmering kan være:

Funksjonell programmering handler om komposisjon av rene funksjoner

En ren funksjon, er en funksjon som ikke har sideeffekter. En sideeffekt er når noe utenfor funksjonen blir påvirket av at funksjonen blir kalt.

Dette kan være:

  • Operasjoner mot databasen
  • Logging
  • Kaste en exception

Når koden vår ikke har sideeffekter, kan man med trygghet vite at endringer ikke skjer andre steder enn akkurat i den funksjonen man kaller. Det er også tryggere å refaktorere koden, da dette gir referensiell transparens.

Referensiell transparens vil si at man kan bytte ut en funksjon med innholdet i funksjonen.

Hvorfor bruke FP?

Fordeler 👍

Lettere testing Når en funksjon kun tar input som parametere og alltid genererer samme output, og ikke har noen sideeffekter, er den trivell å teste
Færre bugs Veldig mange feilkilder forsvinner når bruker FP til å løse problemene. Problemene er ofte lettere å resonnere over når man kun tenker input og output
Mer modularitet Programmer bygt opp av små, men allsidige funksjoner er veldig modulære
Bedre feilhåndtering Når man jobber på FP-måten blir man tvunget til å håndtere "exceptions". Og det er en bra ting!

Ulemper 👎

Nytt paradigme Funksjonell programmering er et nytt paradigme for de som kommer fra OOP. Man må lære seg en ny måte å gjøre ting på, og man må lære andre "patterns" enn de man er vant til. Kategoriteori kan være ganske omfattende å lære seg, om man vil ta det helt ut.
Av og til mer omstendig Noen ganger blir enkle kodeoppgaver mindre "rett frem" (andre ganger motsatt).

Man trenger ikke å gå over til 100% FP med en gang heller. Dra inn konsepter og "best practices" fra FP i den imperative koden din, og få noen av fordelene.

Og etterhvert som du blir bedre, kan du utvide verktøykassen din med flere konsepter og teknikker.

Bruk Typescript

Vi anbefaler på det sterkeste å bruke TypeScript på serveren (og gjerne i klienten) når du jobber med Enonic XP. Typesjekking av en kompilator vil fange opp mange feil, uten at du trenger å deploye koden først.

I koden i denne artikkelen bruker vi også fp-ts pakken for å gi oss typeklasser for TypeScript.

Hvis du baserer et nytt prosjekt på Enonic Webpack Starter er TypeScript allerede støttet.

Fra sideeffekter til effekter

Vi har snakket om at rene funksjoner er bra, og da må vi unngå sideeffekter. Men for at et program skal kunne gjøre noe nyttig, er vi jo avhengige av å kunne utføre sideeffekter.

Vi må kanskje hente data fra en database, skrive til loggen eller kaste en exception.

Måten vi håndterer sideeffekter på, er å gjøre dem om til effekter som kan returneres og utføres. Noen eksempler på effekter er:

Option<A> Kan eller kan ikke produsere verdien A
Either<A,B> Kan enten produsere verdi A eller B
IO<A> Produserer en verdi A, eller feiler, eller terminerer aldri

Hvilke sideeffekter møter vi?

I eksempelkoden under bruker vi en funksjon som har tre sideeffekter:

  1. contentLib.get() kan kaste en Exception
  2. contentLib.get() kan returnere null hvis key ikke fins i databasen
  3. contentLib.get() berører databasen

import {Request, Response} from "enonic-types/controller";
import {ContentLibrary} from "enonic-types/content";

const contentLib: ContentLibrary = __non_webpack_require__("/lib/xp/content");

function get(req: Request): Response {
  const content = contentLib.get({ key: req.params.key! });

  return {
    status: 200,
    body: content?.data
  }
}

Vi kan bruke de tre effektene nevnt over for å håndtere sideeffektene.

  1. Vi bruker Either<Exception, Content> for å fange en Exception, og enten returnere den eller resultatet av typen Content.
  2. Vi kan bruke Option<Content> for å representere en verdi som fins, eller ikke – i stedet for null.
  3. Vi kan bruke IO<Content> for å representere en kjørbar operasjon, som vil returnere Content, men det tillater oss å splitte koden mellom en del med rene funksjoner, og en del av koden med sideeffekter.

Veien videre

I de neste kapitlene i denne serien skal vi se på hvordan vi bruker disse effektene i praksis i Enonic XP-koden vår.

Men først skal vi se nærmere på bruk av TypeScript i kapittel 2.