Er kan altijd wel iets fout gaan wanneer bepaalde logica uitgevoerd wordt. Van het parsen van incorrecte data tot het aanspreken van een webresource die niet online is. Haskell bevat een aantal features voor het afhandelen van fouten.
Haskell is een luie taal. Dingen worden pas uitgevoerd wanneer ze nodig zijn. Dit voorkomt dat lange functies bijvoorbeeld heel veel berekenen terwijl er maar een klein stukje van het resultaat nodig is. Een voorbeeld hiervan is n..n. Dit is een functie voor het genereren van arrays. Zo genereert [1..100] een array van de cijfers 1 tot en met 100. Wat interessant is, is dat deze functie gebruikt kan worden zonder eindwaarde. Wanneer we [1..] schrijven, wordt er net zo lang een lijst gegenereerd, tot er een stack overflow exception optreedt. Wanneer deze functie echter in combinatie met bijvoorbeeld de head functie gebruikt wordt, zien we de kracht van laziness. Als voorbeeld hebben we een functie die het tweede element uit een lijst teruggeeft.
getSecond :: [a] -> a
getSecond items =
head (tail items)

Het resultaat van getSecond wordt bijna meteen teruggegeven. Er is geen merkbaar verschil tussen een lijst van 3 elementen, 100 elementen of oneindig elementen. Haskell voert de functie namelijk enkel in zoverre uit, dat het genoeg is om de getSecond uit te voeren. Ook bij een oneindige lijst, worden enkel de eerste 2 elementen gegenereerd.
In het vorige kopje zagen we een functie voor het ophalen van het tweede element uit een lijst. Wanneer er echter bijvoorbeeld een lege lijst meegegeven wordt, krijgen we een foutmelding:

Het tweede element uit een lege lijst bestaat niet. Er zijn twee manieren om dit op te lossen. Het gooien van een exception of het teruggeven van een Maybe. Maybe is een type die aangemaatk kan worden met de value constructors Just a en Nothing. Zoals de namen al doen vermoeden wordt Just a gebruikt voor wanneer er een waarde teruggegeven kan worden en Nothing voor als dit niet het geval is. In het geval van de getSecond kunnen we dus een Nothing teruggeven wanneer de input leeg is of 1 waarde bevat en een Just a teruggeven wanneer de input 2 of meer elementen bevat:
getSecond :: [a] -> Maybe a
getSecond [] = Nothing
getSecond [_] = Nothing
getSecond (_:rest) = Just (head rest)

Neem de volgende functie in gedachten: We willen alle waardes in een lijst met 1 verhogen. Hiervoor gebruiken we de functie map. map heeft 2 argumenten. Een functie (a -> b) en een lijst. De functie wordt uitgevoerd op ieder element in de lijst. Deze functie zal in ons geval voor ieder element de waarde van het element + 1 teruggeven.
upByOne :: Num a => [a] -> [a]
upByOne items =
map (\item -> item + 1) items
Haskell kent de functionaliteit ‘partial application’. Dit betekent dat deelfuncties teruggeveven kunnen worden. We hebben gezien dat operators ook functies zijn. Zo kan 1 + 1 worden geschreven als (+) 1 1. De (+) functie kunnen we deels invullen door te schrijven:
addOne :: Num a => a -> a
addOne =
(+) 1
In de lambda methode van upByOne kunnen we hetzelfde doen. Deze kunnen we schrijven als:
upByOne :: Num a => [a] -> [a]
upByOne items =
map ((+) 1) items
Hetzelfde kunnen we toepassen op de map functie. We hoeven niet expliciet aan te geven dat items de tweede parameter van map is:
upByOne :: Num a => [a] -> [a]
upByOne =
map ((+) 1)
Wederom werkt dit met [1..].

Nu we een upByOne functie hebben, kunnen we Maybe gebruiken om ervoor te zorgen dat foutieve waardes juist afgehandeld worden. In ons voorbeeld is een foutieve waarde een lege lijst. Wanneer er een lege lijst meegegeven wordt, willen we een Nothing teruggeven.
upByOne :: Num a => [a] -> Maybe [a]
upByOne [] = Nothing
upByOne items =
Just (map ((+) 1) items)

Om te testen hoe slim Haskell met dit soort functies omgaat, kunnen we proberen om wederom een oneindige lijst mee te geven. Om vervolgens het eerste elmeent uit het Just resultaat te halen, definieren we een kleine utility functie. Vervolgens gebruiken we deze om de upByOne aan te roepen:

Ondanks dat het resultaat doorgegeven wordt aan een andere functie, is Haskell slim genoeg om te zien dat enkel het resultaat van het eerste item uit de lijst nodig is.
Een type dat op Maybe lijkt is het type Either. Net zoals Maybe heeft dit type 2 value constructors. Echter kunnen beide value constructors een waarde hebben. Deze value constructors heten Left a en Right a.
Either kan goed gebruikt worden in situaties waarbij het gewenst is om bijvoorbeeld een message mee te geven, wanneer het resultaat leeg is. Misschien wel een error message bij het parsen van invalide JSON. Een andere use case is het ophalen van remote data, waarbij er een ‘success’ en een ‘fout’ resultaat is.
Tijdens het maken van deze blog ben ik meerdere exceptions tegengekomen. Ik lees in het boek dat exceptions gebruik maken van monad’s. Ik heb hier nog niet mee gewerkt en dit zal ik ook niet meer gaan doen tijdens het maken van deze blog. Om deze reden licht ik hier niet meer over toe.