Elke programmeertaal heeft zijn eigen constructies om met fouten om te gaan. In Java zijn dat er twee: excepties en return values. Deze constructies worden vaak door elkaar gebruikt waardoor er snel verwarring kan ontstaan. Zo kwam ik afgelopen weekend tijdens het opruimen van de kranten en tijdschriften het Java magazine van de NLJUG tegen. Altijd leuk om even door te bladeren. In het eerste artikel kwam ik het volgende stuk Java code tegen:

public boolean save(final User user) {
    entitymanager.persist(user);
    return user.getId() != null;
}

Deze functie persisteert een Java object -user- in de database door gebruik te maken van de methode persist op de entitymanager. De API van de EntityManager van Java.

Wat mij opvalt is dat de persist functie van de EntityManager geen waarde retourneert. Wat de meeste programmeurs verwachten is dat de persist functie op regel 2 aangeeft of het opslaan gelukt is door gebruik te maken van een boolean return type. Een heleboel database drivers doen dat doet op die manier. Dan krijg je het volgende:

public boolean save(final User user) {
    return entitymanager.persist(user);
}

Omdat de EntityManager deze logica niet bevat heeft de auteur van het artikel deze logica zelf geïmplementeerd.

Maar is dat verstandig?

Door te checken of het id van het user object een verwijzing naar NULL bevat kan blijkbaar afgeleid worden of het user object correct is opgeslagen. In de persist functie van de EntityManager wordt kennelijk het user object zo aangepast dat het id gezet is.

Het stuk code dat de save functie aanroept moet nu de situatie afhandelen dat het opslaan niet lukt, en daar op een goede manier mee omgaan:

public void registerUser(User user) {
     boolean result = save(user);
     if(!result) {
         // handel fout situatie af
     }
} 

Nu is het probleem dat de EntityManager van Java EE gebruik maakt van excepties om fout situaties af te handelen. Namelijk een EntityAlreadyExists, IllegalArgument en een TransactionRequired exceptie. Het stuk software dat de EntityManager gebruikt mag er dus vanuit gaan dat het opslaan gelukt is tenzij er een exceptie optreedt. Het stuk code uit het Java magazine zet je dus op het verkeerde spoor en is niet goed geprogrammeerd. De save functie zal altijd true retourneren of een exceptie gooien.

Uit bovenstaand voorbeeld blijkt dat de Java programmeer taal meerdere manieren bevat om met foutsituaties om te gaan en dat hiermee snel fouten worden gemaakt. Zelfs door ervaren programmeurs. Hoe gaat dit in andere talen?

Scala

De programmeertaal Scala kent vergelijkbare exception afhandeling als Java maar biedt daarnaast twee extra concepten aan. In de eerste plaats het Option data type. Dit data type kun je gebruiken om aan te geven dat een functie niet altijd een waarde retourneert. Aanroepende code moet beide situaties, een return value of geen return value expliciet afhandelen. In de tweede plaats is er het Either data type waarmee je aan kunt geven dat het return type verschillend kan zijn. Bijvoorbeeld een String voor een foutbericht en een Double voor de uitkomst van een berekening. Door middel van Option en Either kun je veel fout situaties afhandelen. De noodzaak om excepties te gebruiken verdwijnt op deze manier en aanroepende code weet veel beter welke situaties het moet afhandelen.

Go

De go programmeer taal (golang) bevat een vergelijkbaar principe. Go bevat namelijk een Error data type en helemaal geen excepties. NB: Een functie in Go kan meerdere return values hebben. Zie onderstaand voorbeeld:

func Open(name string) (file *File, err error)

De functie Open retourneert een file en een error object. Door te checken of het error object naar NULL verwijst kun je controleren of er een error opgetreden is.

f, err := os.Open("filename.ext") 
if err != nil {     
  log.Fatal(err) 
} 
// do something with the open *File f

Voor meer informatie zie de blog op GoLang website.

Het is goed om te zien dat zowel Scala als Golang andere mechanismen aanbieden om met fout situaties om te gaan. Go heeft een heel helder uitgangspunt, het gebruik van excepties leidt tot onnodig ingewikkelde programmeerconstructies (try-catch-finally) en onvoorziene situaties zoals een reeds geopende file moeten afgehandeld worden en zijn vaak niet exceptioneel. Scala biedt meerdere manieren aan en het is aan de programmeur om een geschikt mechanisme te kiezen.