Introduktion til Elm

Funktionel programmering

1 Introduktion til Elm

I dette kapitel skal vi i gang med at lære Elm. Elm er et programmeringssprog, som gør det muligt for os at gøre vores hjemmeside dynamisk. Vi kan altså begynde at bygge interaktive knapper, loginsystemer, spil og stort set alt andet, som du ser, når du surfer rundt på nettet. Man kan bruge mange forskellige sprog til at gøre en hjemmeside dynamisk. Vi har valgt Elm, fordi det er et funktionelt programmeringssprog. Som ordet lægger op til, kan vi altså overføre meget af vores forståelse fra mængder og funktioner i matematik til begreber i Elm.

Inden vi begynder at bygge hjemmesider med Elm, skal vi se på nogle af de grundlæggende ting man kan med Elm. Og du skal selvfølgelig kode med!

For at komme i gang skal vi lige en tur forbi terminalen for at starte elm repl. Det går du sådan her:

  1. Åbn terminalen.
    • På Windows søger du efter "Kommandoprompt" i søgefeltet og trykker Enter.
    • På Mac søger du efter "Terminal" i en Spotlight-søgning og trykker på Enter.
  2. I terminalen skriver du elm repl. Du skulle gerne få en besked i terminalen, der ser nogenlunde sådan her ud:
    ---- Elm 0.19.1 ----------------------------------------------------------------
    Say :help for help and :exit to exit! More at https://elm-lang.org/0.19.1/repl
    --------------------------------------------------------------------------------
  3. Når man er færdig med at skrive i elm repl, kan man afslutte elm repl igen ved at trykke Ctrl+D eller Cmd+D. Så er man tilbage i den normale terminal igen.

1.1 Typer

En type i Elm er pendant til mængder i matematik: Man kan altså tænke på en type som en samling af objekter. Vi kalder objekterne for værdier af typen.

Ligesom med mængder i matematik, kan man selv definere sine egne typer, men der også nogle typer, der er så almindelige, at de allerede er bygget ind i Elm. Akkurart, som der allerede findes symboler for heltallene (Z) elller de reelle tal (R) i matematik. Lad os se på et par eksempler:

Læg mærke til, at vi navngiver typer med stort begyndelsesbogstav, ligesom mængder i matematik bliver angivet med store bogstaver.

Lad os prøve ovenstående af i elm repl. Som altid er det en rigtig god ide at kode med selv!

Eksempel
Prøv at skrive 3.14159 i elm repl og tryk derefter på Enter. Nu fortæller elm repl dig, hvilken type 3.14159 er:
> 3.14159
3.14159 : Float
Hvad sker der, hvis vi skriver 2 og trykker på Enter? Vi forventer, at elm repl vil fortælle os, at 2 er af typen Int. Men det sker faktisk ikke! I stedet får vi denne besked:
> 2
2 : number
Der er faktisk to ting, der er overraskende her: Det forklarer vi nedenfor.

Typer har altid stort begyndelsesbogstav, så number er ikke en type! Men hvad er det så? Formelt set er number en type class, men det er ikke relevant for os lige nu. Det der er vigtigt for os er, at Elm skriver at 2 er et number, fordi Elm ikke ved, om vi har tænkt os 2 skal bruges som et helttal eller et decimaltal. Vi bliver altså opmærksom gjort på, at 2 kan være enten af typen Int eller af typen Float, når vi bare skriver 2.

Det oplagte spørgsmål fra et matematisk synspunkt er nu: "Jamen er alle værdier af typen Int ikke også værdier af typen Float?" Med andre ord, er Int ikke bare en "deltype" af Float, ligesom Z er en delmængde af R?

Svaret er: NEJ! Her adskiller typer sig fra mængder: En værdi kan kun have én type i en given kontekst. Så tallet 2 kan enten have typen Int eller typen Float, men det kan ikke have begge typer på samme tid! Det lyder nok lidt underligt for en matematiker, men nu introducerer vi funktioner, og derefter kan vi præcist forstå, hvad der menes.

1.2 Min første funktion i Elm

Når vi skal definere en funktion i Elm foregår det på stort set samme måde, som det gør i matematik. Lad os for eksempel prøve at definere en funktion, der tager et heltal som input og giver et dobbelt så stort heltal som output. Det ville se sådan her ud i matematik:

Matematik
f : ℤ ⟶ ℤ f(x) = x + x

Lad os, for en god ordens skyld, forklare ovenstående:

  1. Først angives funktionens navn, "f".
  2. Dernæst specificeres domæne og kodomæne, eller inputmængde og outputmængde om man vil.
  3. Til sidst angives funktionens forskrift: f(x) = x + x.

Vi kan lave denne funktion i elm repl således:

Elm
f : Int -> Int f x = x + x

Det ligner jo den matematiske definition rigtig meget, men lad os igen løbe det hele igennem:

  1. Først angives funktionens navn, "f".
  2. Dernæst specificeres input typen og output typen.
  3. Til sidst angives funktionens forskrift: f x = x + x.

Læg mærke til, at vi undelader de to parenteser omkring x. Det gør vi altid i Elm!

Øvelse

Implementer funktionen ovenfor i elm repl. Det gør du ved at skrive:

Elm
f : Int -> Int f x = x + x
Prøv derefter at kalde funktionen med værdien 2. Det gør du ved at skrive:
f 2
og trykke på Enter. Sker der det, du forventede?

Prøv nu kalde funktionen med værdien 2.0, det gør du ved at skrive:

f 2.0
Der går noget galt. Kan du forklare, hvad der går galt? (Hint: læs det elm repl skriver.)

I øvenstående eksempel går det godt, når vi skriver f 2, fordi vi har fortalt Elm, at f skal have et input af typen Int. Elm ved derfor at 2 i denne kontekst må have typen Int og ikke typen Float.

Derimod går det galt, når vi skriver f 2.0, fordi Elm forventer at f skal have en input værdi af typen Int, men i stedet giver vi den 2.0, som har typen Float. Det vil Elm ikke være med til, og så får vi en fejl! Lige nu virker det måske dumt og ufleksibelt, men når koden bliver lang og kompleks er det en stor fordel, fordi det hjælper os med at holde overblik over hvad, der kommer ind og ud af de forskellige funktioner.

1.3 God stil i Elm

Når vi først kommer igang, kommer vi til at skrive rigtig mange funktioner, og det kan blive svært at kende forskel på dem. Derfor navngiver vi ikke funktioner i Elm med f, g og h som i matematik. I stedet giver vi dem navne, som forklarer, hvad de skal bruges til.

Funktionen vi ovenfor kaldte f, burde vi i stedet definere således:

Elm
doubleInteger : Int -> Int doubleInteger x = x + x

Vi har altså navngivet funktionen doubleInteger i stedet for f, så det er lettere at huske hvad funktionen gør. Læg desuden mærke til, at vi navngiver funktionen med lille begyndelsesbogstav. Det gør vi altid i Elm:

Læg også mærke til at vi har skiftet linje efter = og indrykket næste linje med Tab. Det gør vi, fordi vores funktionsforskrifter kan blive meget lange, og så er det lettere at læse, hvis de er indrykket på de efterfølgende linjer.

Øvelse
Definer en funktion squareInteger, som tager en værdi af typen Int og returnerer en værdi af typen Int.
squareInteger : Int -> Int
Funktionen skal multiplicere inputtet med sig selv, så vi for eksempel får følgende output:
Elm
> squareInteger 7 49 : Int

1.4 Typen String

Vi er bestemt ikke begrænset til at arbejde med tal i Elm. Vi kan blandt andet også arbejde med tekst. Tekst har typen String, og vi angiver, at en værdi har typen String ved at bruge anførselstegn.

Elm
> "Hello World" "Hello World" : String

elm repl fortæller os, at "Hello World" er af typen String. Som du måske allerede har gættet, så kan enhver sekvens af karakterer være en String.

Vi kan arbejde med og udføre operationer på værdier af typen String, ligesom vi kan med værdier af typerne Int og Float. For eksempel kan vi sammensætte tekststrenge:

Elm
> "Hej " ++ "Peter" "Hej Peter" : String
Øvelse
Definer en funktion
hejMedDig : String -> String
som tager et navn som input og giver følgende output:
Elm
> hejMedDig "Peter" "Hej med dig, Peter" : String