Blog

Typsichere Fehlerbehandlung mit Scala 3

Anton Lijcklama à Nijeholt

Anton Lijcklama à Nijeholt

Aktualisiert Oktober 16, 2025
3 Minuten

Einführung

In einem früheren Blogbeitrag haben wir uns die typsichere Fehlerbehandlung mit Shapeless-Koprodukten angesehen und festgestellt, dass Koprodukte das Fehlen von Union-Typen in Scala 2.x ausgleichen. Ein Koprodukt kann als angesehen werden, das entweder Typen zurückgibt. In dieser Hinsicht gibt es keine Begrenzung für die Anzahl der Typen, die wir verwenden können, im Gegensatz zu z.B. einem Either[A, B].

Die Herausforderung bei Shapeless-Koprodukten besteht darin, dass sie zwar perfekt funktionieren, der Code aber nicht so sauber ist, wie er sein könnte, da es keine native Unterstützung für Union-Typen gibt. Das hat sich jedoch in Scala 3 geändert! In diesem Artikel zeige ich Ihnen, wie Sie denselben typisierten Fehlerkanal wie im vorherigen Blogbeitrag erstellen können, aber diesmal mit viel weniger Code und besserer Lesbarkeit!

Warum überhaupt ein getippter Fehlerkanal?

Ein typisierter Fehlerkanal zeigt dem Programmierer explizit an, welche Arten von Fehlern auftreten können, so dass er anhand der Signatur sofort versteht, welche Fälle er behandeln muss. Diese Handler können zu einfachen Funktionen werden, die leicht zu testen sind.

Wenn wir uns die Webentwicklung ansehen, sind mehrere Fehler vorprogrammiert:

  • Fehler bei der Eingabevalidierung; sollten an den Benutzer zurückgemeldet werden, damit dieser seine Eingabe korrigieren kann.
  • Fehler bei der Domainvalidierung: Je nach Fehler möchten Sie vielleicht nicht den Benutzer selbst benachrichtigen, sondern eine Benachrichtigung für einen Support-Mitarbeiter auslösen, der das Problem beheben soll.
  • Unerwartete Ausnahmen; sollten protokolliert werden, eine Warnung auslösen und den Benutzer darüber informieren, dass "etwas schief gelaufen ist". Sie möchten zum Beispiel nicht, dass möglicherweise sensible Daten nach außen dringen.

Code-Beispiel

Lassen Sie uns einen Blick auf den Code werfen:

import scala.io.StdIn.readLine

object DivideCommandLineApp extends App {
  case class ParseNumberError(value: String)
  case object DivideByZeroError

  def tryParse(s: String): Either[ParseNumberError, Double] =
    s.toDoubleOption.fold[Either[ParseNumberError, Double]](Left(ParseNumberError(s)))(a => Right(a))

  def tryDivide(a: Double, b: Double): Either[DivideByZeroError.type, Double] =
    if (b == 0) Left(DivideByZeroError)
    else Right(a / b)

  def tryRunApp: Either[ParseNumberError | DivideByZeroError.type, Double] = for {
    a <- tryParse(readLine)
    b <- tryParse(readLine)
    r <- tryDivide(a, b)
  } yield r

  tryRunApp.fold({
    case ParseNumberError(error) => println(s"Error: Input '$error' is not a number")
    case DivideByZeroError => println("Error: Cannot divide by zero")
  }, r => println(s"Result: $r"))
}

Indem wir den Typ Either nutzen, brauchen wir nichts Ausgefalleneres. Da bereits ein typisierter Fehlerkanal ist, können wir auf der linken Seite einen Union-Typ verwenden. Er lässt sich gut zusammensetzen, wie in der Methode tryRunApp zu sehen ist. Sie gibt entweder ein ParseNumberError oder ein DivideByZeroError zurück.

Voilà! So einfach können Sie in Scala 3 mit Hilfe von Union-Typen Fehlerkanäle einrichten.

Unterschiede

Werfen wir einen kurzen, objektiven Blick auf die Unterschiede zwischen dem Code, der Shapeless-Koprodukte verwendet, und dem unten stehenden Code:

  • Rund 70% weniger Code (81 Zeilen gegenüber 24)
  • Keine externe Bibliothek erforderlich (d.h. Shapeless)
  • Keine Notwendigkeit für Implikate (nicht, dass ich etwas gegen Implikate hätte, aber es kann neue Programmierer in Scala ziemlich verwirren)

Fazit

Die Verwendung von Vereinigungstypen in Scala 3 bietet neue Möglichkeiten für die Modellierung unserer Domänen und unsere Art der Fehlerbehandlung. In Scala 3 ist es nicht mehr nötig, Shapeless-Koprodukte für diese einfachen Union-Typ-Konstruktionen zu verwenden. Bedeutet dies, dass Shapeless obsolet geworden ist? Nein, natürlich nicht! Es gibt immer noch eine Menge Funktionen in Shapeless, die von Scala 3 nicht abgedeckt werden.

Verfasst von

Anton Lijcklama à Nijeholt

Contact

Let’s discuss how we can support your journey.