Brugerdefinerede typer og pattern matching
Brugerdefinerede typer
Når vi begynder at definere mere avancerede funktioner, får vi også brug for mere avancerede typer. I den forbindelse kan vi også selv lave vores egne typer. Vi vil se på et eksempel som er inspireret af spillet SET. Et SET-kort har en ud af tre mulige farver. Så vi kunne lave en Color type, der så sådan her ud:
Elm
type Color
= Green
| Purple
| Red
Ovenstående skal læses sådan her: Color er en ny type, som har varianterne Green, Purple og Red. Det er meget sammenligneligt med Bool-typen, som har de to varianter True og False. Når vi blot skriver Green, så kan vi se at Green har typen Color. Bemærk at navne på typer og varianter altid starter med et stort bogstav.
Vi kan bruge ovenstående type og dens varianter ligesom alle andre typer. Lad os se et eksempel. Forestil jer at nogle elever på TalentCamp har spillet et spil SET. Der er blevet taget en masse SET: Der er taget 7 SET som var helt røde, 3 SET som var helt grønne, 6 SET som var helt lilla, 9 SET som havde et kort af hver farve, og så lå der 6 kort tilbage på bordet til sidst. Lad os bygge en record, hvor vi kan specificere en farve af typen Color og et antal af typen Int (Elm siger sikkert at antal har typen number, men det er okay). På den måde får vi en record, som indeholder et felt color, hvor vi f.eks. angiver, at vi bekymrer os om røde SET, og et felt setsTaken som angiver hvor mange SET der er taget af den givne farve.
Elm
> redSets = { color = Red, setsTaken = 7 }
{ color = Red, setsTaken = 7 }
: { color : Color, setsTaken : number }
Bemærk at når vi skriver color med småt, så er det altså bare et navn på et felt i en record. Color med stort er derimod en type. Når vi skriver recorden ind og trykker Enter, så kan vi også se hvilken type redSets har: Det er en record med et felt color af typen Color og et felt setsTaken af typen number.
Et SET-kort har fire egenskaber: Farve, antal, form og fyld. Inden for hver egenskab er der tre muligheder, f.eks. de tre farver grøn, lilla og rød.
- I kodefeltet ovenfor har vi lavet typen
Color, som har de tre mulige farver som varianter:Green,PurpleogRed. Lav for hver af de andre egenskaber en brugerdefineret type:- Lav typen
Shape, der har varianterneDiamond,SquiggleogOval. - Lav typen
Shading, der har varianterneSolid,StripedogOpen. - Lav typen
Number, der har varianterneOne,TwoogThree.
- Lav typen
- (Svær) Kan du lave en alias type
Card, der repræsenterer et SET-kort?
En af de seje ting ved brugerdefinerede typer er at varianter kan bære på ekstra information. Lad os forestille os at vi er i gang med at bygge en hjemmeside, hvor brugere kan registrere sig. Når man registrerer sig på hjemmesiden, giver man sit navn. Der er også brugere på hjemmesiden som ikke har registreret sig, så vi kender ikke deres navn.
Elm
> type User
| = Registered String
| | Anonymous
|
Ovenstående type User har to varianter: Registered angiver brugere som har registreret sig, og derfor tilføjer vi String efter varianten for at angive at brugere af varianten Registered bærer rundt på noget ekstra information, nemlig en String. Den anden variant er Anonymous, som ikke bærer rundt på noget. Sidst i det her kapitel skal vi se hvordan det ser ud, når vi skal bruge det i praksis.
Pattern matching
Lad os vende tilbage til eksemplet med typen Color, der har varianterne Green, Purple og Red. Lad os sige at vi godt kunne tænke os en funktion, som tog et objekt af typen Color som input og returnerede en String, hvoraf det fremgår hvilken farve der er tale om.
Elm
> checkColor : Color -> String
| checkColor color =
| case color of
| Green ->
| "The color is green!"
| Purple ->
| "The color is purple!"
| Red ->
| "The color is red!"
|
> checkColor Green
"The color is green!"
Ovenfor har vi defineret en funktion, som tager et objekt ind af typen Color. Der er altså kun tre muligheder for hvilket input checkColor kan få: Det skal være Green, Purple eller Red. Når vi skriver case ... of, så angiver vi, at Elm skal give forskellige outputs alt efter hvad inputtet er. Så når vi skriver case color of, så skal vi fortælle Elm hvad outputtet skal være for alle tre varianter af Color.
Msg, som er en forkortelse af message. Lad os bygge et simplet eksempel på hvordan Msg kunne se ud.
- Lav en brugerdefineret type
Msg, som har varianterneGoodogBad. - Definer en funktion
checkMessagesom tager enMsgsom input og giver enStringsom output.checkMessageskal bruge pattern matching: Den skal outputte"This is a good message!", hvis inputtet erGood-varianten og den skal outputte"This message is bad :(", hvis inputtet erBad-varianten.
Til sidst vender vi tilbage til vores type User fra før, hvor varianten Registered bærer rundt på en String. Lad os se hvordan det så ser ud, når vi bruger pattern matching:
Elm
> messageUser : User -> String
| messageUser user =
| case user of
| Registered tekst ->
| "This user registered with the name" ++ tekst
| Anonymous ->
| "This user is not registered."
|
messageUser (Registered "Peter Hansen")
Så når varianten Registered bærer rundt på en String, så skal vi angive en variabel tekst i Registered-casen.