Referentiel gennemsigtighed er en egenskab ved dele af computerprogrammer. En del af et program kaldes "referentielt gennemsigtig", hvis den kan erstattes med den værdi, den giver tilbage, uden at programmets adfærd ændres. En referentielt gennemsigtig funktion skal være ren — den skal altid give det samme output for det samme input, og den må ikke have nogen sideeffekter - dele af programmet, der udfører en anden handling end at give en værdi tilbage. Det modsatte af referentiel gennemsigtighed er referentiel uigennemsigtighed.

I matematik er alle funktioner referentielt gennemsigtige, fordi en matematisk funktion kun kan modtage værdier og udlevere en værdi. I programmering er dette ikke altid sandt — en funktion kan også finde ud af, hvilken dag i året det er, læse eller skrive filer eller udskrive en besked på skærmen. På grund af denne forskel bruger nogle mennesker andre navne for funktioner i programmering, f.eks. procedurer eller subrutiner, når de udfører synlige handlinger ud over at returnere en værdi.

Hvorfor det er nyttigt

Referentiel gennemsigtighed gør det muligt for programmører og compilere at tænke på kode som et omskrivningssystem — at tage et udtryk og erstatte det med et andet ekvivalent udtryk. Det åbner for både teoretiske og praktiske fordele:

  • At bevise, at et program eller en funktion er korrekt (formel verifikation) bliver enklere, fordi man kan udskifte udtryk uden at ændre adfærd.
  • Gør en algoritme mere enkel og lettere at forstå, da man kun behøver at tænke på input → output.
  • Gør det nemmere at ændre eller refaktorere kode med lav risiko for utilsigtede bivirkninger.
  • Muliggør optimeringer som hurtigere kørsel og lavere hukommelsesforbrug (se nedenfor).

Typiske optimeringer

Der er flere teknikker, som især kan udnytte referentiel gennemsigtighed:

  • Memoisering: Gemme resultatet af dyre beregninger, så gentagne kald med samme input kan genbruges.
  • Eliminering af fælles underudtryk: Identificere og genbruge beregnede resultater i stedet for at gentage beregningen.
  • Doven evaluering (lazy evaluation): Udskyde beregning, indtil resultatet virkelig er nødvendigt — kombineret med renhed kan dette spare arbejde.
  • Parallelisering: Uafhængige, rene funktioner kan køres samtidigt uden risiko for delt mutable tilstand.
  • Kompleksere compiler-optimeringer som inlining, algebraiske forenklinger og fjernelse af ubrugelig kode.

Hvad bryder referentiel gennemsigtighed?

  • I/O (f.eks. læse/skrivning til filer, netværk, skærm).
  • Mutation af delt state (ændre globale variabler eller objekters interne tilstand).
  • Tilfældighed (random tal) eller afhængighed af systemtid (clock).
  • Non-deterministiske operationer eller interaktioner med omverdenen.
  • Sideeffekter som logning, signalering eller ændringer i databaser.

Eksempler

En enkel, referentielt gennemsigtig funktion i pseudokode:

funktion kvadrer(x):     return x * x

For samme input x vil kvadrer(x) altid returnere det samme output uden at påvirke noget andet i programmet.

Et eksempel på en ikke-referentielt gennemsigtig funktion:

funktion læsBrugerId():     return læsFraDatabase("current_user_id")

Her afhænger udfaldet af ekstern tilstand (databasen), og man kan ikke frit erstatte kaldet med en enkelt værdi uden at ændre programmets adfærd.

Sprog og praksis

Nogle sprog og paradigmer fremmer referentiel gennemsigtighed (f.eks. Haskell, som har ren funktionel kerne), mens andre tillader eller tilskynder mutation og I/O i stor skala. Det betyder dog ikke, at man ikke kan skrive rene funktioner i typisk imperative sprog — mange moderne programmer bygger på at isolere og begrænse sideeffekter og ellers skrive så meget kode som muligt som rene funktioner for at få fordelene nævnt ovenfor.

Begrænsninger og overvejelser

  • Ikke al kode kan eller bør være ren; I/O og mutation er nødvendige for at interagere med brugeren og verden udenfor programmet.
  • Undtagelser og fejlbehandling kan komplicere bedømmelsen — deterministic undtagelser kan stadig betragtes som del af funktionenes adfærd, men ukontrollerede bivirkninger bryder renheden.
  • Overdreven brug af memoisering kan øge hukommelsesforbruget; valget af optimering bør være velovervejet.

Sammenfattende gør referentiel gennemsigtighed kode lettere at forstå, teste, optimere og parallelisere — men det er et designmål og en egenskab, ikke altid en praktisk mulighed for al kode. Ved at skrive så meget som muligt af programlogikken som rene funktioner får man dog ofte konkrete fordele i vedligeholdelse, ydeevne og pålidelighed.