Result Type: wat is het, Result-container-type en hoe het werkt in mobiele ontwikkeling

Auteur: IT Sectr Gepubliceerd: 2026-05-26 Leestijd: 9 min

Result Type — een container-type dat het resultaat vertegenwoordigt van een bewerking die kan eindigen in succes (Success) of fout (Failure). In tegenstelling tot uitzonderingen geeft Result de fout door als een gewone waarde zonder het afwikkelen van de stack, waardoor de afhandeling van verwachte fouten veiliger en beter samenstelbaar is. Volgens Apple Swift Documentation (2026), stelt Result<Success, Failure> in Swift je in staat om ketens van bewerkingen te combineren met automatische foutafhandeling via map en flatMap, zonder de uitvoering van het programma te onderbreken.

Belangrijkste punten

  • Result Type — generieke container met twee toestanden: Success (gegevens) en Failure (fout), zonder stack-afwikkeling
  • In Swift Result<Success, Failure> — ingebouwd type met methoden map, flatMap, mapError en get
  • In Kotlin Result<T> vertegenwoordigt succes of Throwable, met functies getOrNull, getOrDefault, fold
  • In Dart standaard Result<T> beschikbaar vanaf Dart 3.0, ook Either uit de fpdart-package
  • Compositie via map en flatMap maakt het mogelijk om meerdere bewerkingen die Result retourneren te combineren zonder geneste controles

Wat is Result Type?

Result Type is een generiek type dat het resultaat inkapselt van de uitvoering van een bewerking die zowel succesvol als met een fout kan eindigen. In tegenstelling tot uitzonderingen, waar de fout de normale uitvoeringsstroom onderbreekt en het afwikkelen van de stack vereist om een catch te vinden, geeft Result de fout door als een gewone waarde — de aanroepende partij ontvangt altijd een object en beslist zelf hoe ermee om te gaan. Dit is vooral nuttig voor verwachte fouten: ongeldige invoer, bedrijfsregels, serverweigering, waar een uitzondering een te zwaar mechanisme zou zijn.

Het concept van Result komt uit functioneel programmeren, waar vergelijkbare typen Either (of Left/Right) worden genoemd. In Swift werd Result een standaardtype in Swift 5.0, in Kotlin verscheen Result<T> in de standaardbibliotheek, in Dart 3.0 — ingebouwd Result<T>. Elke implementatie biedt methoden om met de container te werken: map (transformatie van de succeswaarde), flatMap (keten van Result-functies), mapError (transformatie van de fout), fold (afhandeling van beide gevallen). Result Type — de hoeksteen van functionele foutafhandeling in mobiele ontwikkeling, een alternatief voor try-catch voor verwachte scenario's.

Het belangrijkste voordeel van Result is de samenstelbaarheid. Je kunt meerdere bewerkingen, die elk een fout kunnen retourneren, combineren in één keten zonder geneste try-catch. Als een bewerking in de keten eindigt met Failure, wordt de hele keten onderbroken en wordt Failure geretourneerd — zonder één if-voorwaarde of catch-blok. Dit maakt de code lineair en leesbaar, vooral in scenario's met meerdere opeenvolgende API-verzoeken of controles van bedrijfsregels.

Result in Swift

In Swift is Result<Success, Failure> een enum met twee gevallen: .success(Success) en .failure(Failure), waarbij Failure is beperkt door het Error-protocol. Swift Result is een volledig functioneel type met de methoden map, flatMap, mapError en get. get is een bijzondere methode: het retourneert de Success-waarde als het resultaat succesvol is en gooit een fout als het Failure is. Dit maakt het mogelijk om Result te gebruiken als een brug tussen functionele en op uitzonderingen gebaseerde stijlen: behandel via map/flatMap in een keten, en aan het einde — get via do-catch voor integratie met throws-code.

Result enum en exhaustieve switch

Swift Result is een enum met generieke parameters, waardoor de compiler exhaustieve afhandeling kan controleren via switch of do-catch. Als je een nieuw geval toevoegt aan de enum NetworkError, geeft de compiler een fout in alle switch-expressies waar dit geval niet wordt afgehandeld. Exhaustieve controle (exhaustive checking) — het belangrijkste voordeel van Result boven uitzonderingen: de compiler garandeert dat alle mogelijke fouten in de compilatiefase in aanmerking worden genomen. In tegenstelling hiermee worden uitzonderingen in Swift niet gecontroleerd door de compiler (alleen throws wordt gedeclareerd, maar niet het fouttype).

swift
enum NetworkError: Error {
    case badURL
    case requestFailed(String)
    case decodingFailed
}

func fetchUser(id: Int) -> Result<User, NetworkError> {
    guard let url = URL(string: "https://api.example.com/users/\(id)") else {
        return .failure(.badURL)
    }
    let result = performRequest(url: url)
    switch result {
    case let .success(data):
        if let user = try? JSONDecoder().decode(User.self, from: data) {
            return .success(user)
        }
        return .failure(.decodingFailed)
    case let .failure(error):
        return .failure(.requestFailed(error.localizedDescription))
    }
}

// Gebruik met switch
let result = fetchUser(id: 42)
switch result {
case .success(let user):
    showUser(user)
case .failure(.badURL):
    logError("Invalid URL")
case .failure(.requestFailed(let msg)):
    showAlert(msg)
case .failure(.decodingFailed):
    logError("Decoding error")
}

Result<Success, Failure> maakt het mogelijk om de fout op typeniveau te typeren: de functie fetchUser retourneert Result<User, NetworkError>, waarbij NetworkError een concrete enum is met drie gevallen. De aanroepende partij behandelt elk geval via een switch met exhaustieve dekking — de compiler controleert of alle varianten zijn afgehandeld. Exhaustieve switch — het belangrijkste voordeel van Result boven uitzonderingen: de compiler garandeert dat je niet vergeet .badURL, .requestFailed of .decodingFailed af te handelen. In het geval van uitzonderingen vereist de compiler geen afhandeling en een onopgevangen catch(.badURL) kan gemakkelijk worden gemist in een review.

Result in Kotlin

In Kotlin is Result<T> een ingebouwd type uit de standaardbibliotheek, dat succes (T) of fout (Throwable) vertegenwoordigt. In tegenstelling tot Swift staat Result in Kotlin niet toe om een specifiek fouttype aan te geven — alleen Throwable. Dit is gedaan voor eenvoud, maar vereist extra controle van het fouttype via een when-expressie. Kotlin Result ondersteunt de functies fold (afhandeling van beide gevallen), getOrNull (succes of null), getOrDefault (succes of standaardwaarde), map, recover, andThen. Een bijzonderheid van Kotlin: Result is niet bedoeld voor directe propagatie over functiegrenzen heen — het kan niet worden gebruikt als retourtype voor methoden van de Android SDK of Kotlin Coroutines API zonder extra aanpassing.

kotlin
fun parseJson(input: String): Result<JsonObject> {
    return runCatching {
        JsonParser.parseString(input).asJsonObject()
    }
}

fun validateEmail(email: String): Result<String> {
    return if (email.contains("@")) {
        Result.success(email.trim())
    } else {
        Result.failure(IllegalArgumentException("Invalid email"))
    }
}

data class SignupData(val name: String, val email: String)

fun processSignup(name: String, email: String): SignupResult {
    return validateEmail(email).fold(
        onSuccess = { validateName(name) },
        onFailure = { SignupResult.Error("Invalid email") }
    )
}

runCatching — een handige wrapper in Kotlin die elke uitzondering opvangt en Result.failure retourneert. validateEmail retourneert Result.success of Result.failure afhankelijk van de controle. fold handelt beide gevallen compact af: bij Success wordt de volgende validatiefunctie aangeroepen, bij Failure — wordt SignupResult.Error geretourneerd. Belangrijk: Kotlin Result is niet bedoeld voor opslag in data class-velden of directe overdracht over de grenzen van suspend-functies — gebruik je eigen sealed class (Success/Error/Loading) voor het weergeven van UI-toestanden in Jetpack Compose of MVVM.

Result in Dart en Flutter

Sinds Dart 3.0 is in de standaardbibliotheek een ingebouwd Result<T> verschenen — een sealed class met twee constructors: T.ok() (succes) en Error.error() (fout met Object en StackTrace). Vóór Dart 3.0 gebruikten Flutter-ontwikkelaars Either<L, R> uit de dartz-package of aangepaste sealed classes. Het ingebouwde Result in Dart is minimalistisch: het biedt niet direct map/flatMap aan — deze functies moeten worden geïmplementeerd via when of met behulp van extension methods. Voor serieuze functionele afhandeling blijft Either uit fpdart een krachtigere oplossing met ondersteuning voor map, flatMap, mapLeft, fold, andThen en bind-operatoren.

Either uit fpdart

Either<L, R> — een links-geassocieerd type uit de fpdart-package, waarbij Left — fout, Right — succes. In tegenstelling tot het ingebouwde Result<T>, typeert Either de fout op het niveau van de typeparameter (L), waardoor fouttypen in de compilatiefase kunnen worden onderscheiden. De fpdart-package biedt een volledige set functionele combinators: map (Right -> Right), mapLeft (Left -> Left), flatMap (bind — geneste Either), andThen (keten zonder transformatie), fold (uitgang uit Either), getOrElse (standaardwaarde). Voor Flutter-applicaties met een functionele benadering is Either de facto de standaard.

dart
import 'dart:convert';
import 'package:fpdart/fpdart.dart';

class UserService {
    Either<AppError, User> fetchUser(String id) {
        try {
            final response = await http.get(
                Uri.parse('https://api.example.com/users/$id')
            );
            if (response.statusCode == 200) {
                final user = User.fromJson(
                    json.decode(response.body)
                );
                return Either.of(user);
            }
            return Either.left(
                AppError.serverError(response.statusCode)
            );
        } on SocketException catch (e) {
            return Either.left(AppError.networkError(e.message));
        }
    }
}

// Gebruik met fold
final result = await service.fetchUser('42');
result.fold(
    (left)  => showError(left.message),
    (right) => showUser(right),
);

Either<L, R> uit fpdart — links-geassocieerd type: Left — fout, Right — succes. fetchUser retourneert Either<AppError, User>, waarbij AppError een sealed class is met specifieke fouttypen (serverError, networkError). fold handelt beide gevallen af: de eerste callback voor Left (fout), de tweede voor Right (succes). In Dart 3.0 ondersteunt het ingebouwde Result ook fold, maar biedt het geen map/flatMap. Voor ketens biedt Either uit fpdart map, flatMap (bind), mapLeft, andThen — een volledige set functionele combinators voor foutcompositie.

Compositie van Result via map en flatMap

Het belangrijkste voordeel van Result boven uitzonderingen is compositie. Als je meerdere bewerkingen hebt, die elk een fout kunnen retourneren, kun je ze combineren in een keten via map en flatMap zonder een enkele geneste if of try-catch. map transformeert de succeswaarde: Result.success(x) -> Result.success(f(x)). flatMap (ook wel bind of andThen genoemd) — voor gevallen waarin de transformatie zelf Result retourneert: Result.success(x) -> f(x) -> Result<Y>. Als bij een stap Failure optreedt, worden volgende bewerkingen niet uitgevoerd — de keten wordt onderbroken.

kotlin
data class UserRequest(val userId: String, val token: String)

sealed class AuthError {
    data object InvalidToken : AuthError()
    data class UserNotFound(val id: String) : AuthError()
}

typealias Outcome<T> = Either<AuthError, T>

fun validateToken(token: String): Outcome<String> =
    if (token.isNotBlank()) Either.right(token)
    else Either.left(AuthError.InvalidToken)

fun fetchProfile(userId: String): Outcome<Profile> =
    if (userId == "42") Either.right(Profile("Alice"))
    else Either.left(AuthError.UserNotFound(userId))

// Compositie via flatMap (andThen in fpdart)
val result = validateToken("abc123")
    .flatMap { fetchProfile("42") }
    .map { it.name }
    .getOrElse { "Guest" }

println(result) // "Alice"

Keten: validateToken -> fetchProfile -> map name -> getOrElse „Guest“. Als validateToken Left (InvalidToken) retourneert, wordt de keten onderbroken en wordt „Guest“ geretourneerd. Als fetchProfile Left (UserNotFound) retourneert — ook „Guest“. Als beide bewerkingen succesvol zijn — de profielnaam. flatMap maakt het mogelijk om Either-functies, die elk kunnen falen, te combineren in één lineaire keten. In de traditionele uitzonderingsstijl zou dezelfde code twee geneste try-catch of null-controles vereisen. getOrElse aan het einde — het uitgangspunt van de compositie, dat een standaardwaarde biedt voor het Failure-geval.

Result vs Uitzonderingen: wanneer wat te kiezen

Result en uitzonderingen — zijn geen elkaar uitsluitende benaderingen. Elk heeft zijn toepassingsgebied en in een goed ontworpen mobiele applicatie worden beide gebruikt. De keuze hangt af van of de fout verwacht (expected) of onverwacht (unexpected) is. Result — voor verwachte fouten die deel uitmaken van de bedrijfslogica: ongeldige e-mail, onvoldoende saldo op de rekening, overschrijding van de aanvraaglimiet. Uitzonderingen — voor onverwachte systeemfouten: netwerkverlies, OutOfMemoryError, NullPointerException (die er niet zouden moeten zijn, maar ze gebeuren).

CriteriumResult TypeUitzonderingen (Exception/Error)
FouttypeVerwacht (bedrijfslogica)Onverwacht (systeem)
PrestatieLage kosten (zonder stack-afwikkeling)Hoge kosten (stack unwinding, StackTrace vastleggen)
CompositieVia map/flatMap — lineaire ketensGeneste try-catch — moeilijk te lezen
CompilerExhaustieve controle (switch/when)Alleen checked exceptions in Java
UitvoeringsstroomWordt niet onderbroken — fout als waardeWordt onderbroken tot de dichtstbijzijnde catch
TestenMakkelijk: resultaat controleren, assert isSuccess/isErrorVereist assertThrows en mock-objecten
Wanneer gebruikenBedrijfsvalidatie, aanvraagketens, formulierenNetwerkverlies, I/O-fouten, systeemcrashes

Praktische regel: als de fout deel uitmaakt van de normale werkstroom van de applicatie (gebruiker heeft een ongeldige e-mail ingevoerd, onvoldoende toegangsrechten) — gebruik Result. Als de fout een uitzonderlijke situatie is (server reageert niet, geheugen op) — gebruik uitzonderingen. In mobiele ontwikkeling is Result op de grenzen van lagen (UseCase -> ViewModel) en uitzonderingen binnen lagen (API -> Repository) — een veelvoorkomend patroon dat de voordelen van beide benaderingen combineert.

Migratiestrategie van uitzonderingen naar Result

Migratie van bestaande op uitzonderingen gebaseerde code naar Result moet geleidelijk zijn. Begin met de grenzen van lagen: wikkel aanroepen van throws-functies in Result { try ... } (Swift) of runCatching { ... } (Kotlin). Vervang vervolgens het retourtype van Repository- en UseCase-methoden door Result/Either, terwijl je de interne implementatie op uitzonderingen houdt. In de laatste fase migreer je ViewModel: in plaats van UiState met uitzonderingen, gebruik je sealed class UiState<T> (Loading, Success, Error), waarbij Error de domeinfout opslaat, niet Throwable. Geleidelijke migratie maakt het mogelijk om elke laag afzonderlijk te testen zonder globale refactoring.

Veelgestelde vragen

Waarin verschilt Result Type van Optional/Option?

Optional (T?) vertegenwoordigt de aanwezigheid of afwezigheid van een waarde — nil betekent “geen gegevens”, maar zegt niet waarom. Result (Success/Failure) bevat niet alleen het succes, maar ook de oorzaak van de fout met een concreet type. Gebruik Optional wanneer afwezigheid van waarde normaal is (bijv. een optioneel profielveld), Result — wanneer je informatie over de fout nodig hebt.

Hoe zet je een throws-functie om naar Result?

In Swift gebruik je Result { try throwingFunc() } — de constructor van Result accepteert een throws-closure. In Kotlin — runCatching { throwingFunc() }, die Result<T> retourneert. In Dart — Result<T>.tryCatch(() => throwingFunc()). Dit maakt het mogelijk om op uitzonderingen gebaseerde code eenvoudig te integreren in Result-ketens.

Hoe behandel je een fout in Result zonder verlies van informatie?

Gebruik mapError (Swift) of mapLeft (Either in Dart/Kotlin) voor transformatie van het fouttype zonder de succeswaarde te wijzigen. Als je beide gevallen moet afhandelen en een enkele waarde moet retourneren — gebruik fold. Voor logging zonder de keten te onderbreken, gebruik onFailure (Kotlin) of een prefix inspection point.

Kan Result in Kotlin met coroutines worden gebruikt?

Ja, maar voorzichtig. Kotlin Result<T> wordt niet aanbevolen als direct retourtype van suspend-functies vanwege kenmerken van de K2-compiler en reflectie. Gebruik je eigen sealed class NetworkResult<T> (Success, Error, Loading) voor het weergeven van toestanden in coroutines. Voor verwachte fouten in bedrijfslogica is Either uit Arrow een krachtiger alternatief.

Wat is fold in de context van Result?

fold — een methode die twee callbacks accepteert: onSuccess (voor het succesgeval) en onFailure (voor het foutgeval), en retourneert een enkele waarde van elk type. Dit is het equivalent van een switch-expressie, maar in de vorm van een hogere-ordefunctie. fold — het belangrijkste uitgangspunt uit Result-ketens, waar je Success/Failure omzet naar UiState, een string voor de gebruiker of een andere Result.

Samenvatting

  • Result Type — generieke container (Success/Failure) voor veilige afhandeling van verwachte fouten zonder stack-afwikkeling
  • In Swift Result<Success, Failure> met Failure: Error zorgt voor exhaustieve controle via switch met type-veilige fouten
  • In Kotlin Result<T> wikkelt succes of Throwable in, runCatching — handige constructor uit throws-code
  • In Dart ingebouwd Result<T> (Dart 3.0) en Either<L, R> uit fpdart voor geavanceerde compositie met map/flatMap
  • Compositie via map (transformatie van succes) en flatMap (keten van Result-functies) vervangt geneste try-catch
  • Result vs uitzonderingen: Result voor verwachte bedrijfsfouten, uitzonderingen voor onverwachte systeemstoringen
  • Gebruik Result op de grenzen van lagen voor expliciete en testbare foutafhandeling zonder onderbreking van de uitvoeringsstroom

We ontwikkelen een mobiele applicatie turnkey

IT Sectr creëert sinds 2017 iOS- en Android-applicaties voor startups en bedrijven. We adviseren u en stellen de beste oplossing voor.

Bespreek het project

Lees ook