# 🇫🇷 Type Result en Kotlin

# Origines de cet article

Il y a quelques années, je me suis retrouvé à faire un onboarding pour un nouveau job à San Fransisco. Mon côté 🐻 et mon niveau en 🇬🇧 de l'époque ont fait que j'ai passé pas mal de temps à l'hôtel quand je ne partais pas pour de longues marches. J'en ai profité pour une n-ième tentative dans ma compréhension de la programmation fonctionnelle. Et c'est à ce moment que j'ai découvert une nouvelle méthode de gestion des erreurs. Et ce fut l'occasion de remettre le nez dans Scala (avec plus de succès).

Mes types préférés furent Try, Success, Failure. Une instance de Try sera soit un Success (contenant une valeur), soit un Failure (contenant un message d'erreur).

Du coup, mon nouveau principe de gestion des erreurs devint le suivant: une fonction retourne un Try et à chaque appel de ma fonction je suis "obligé" de tester si le retour est un Success ou un Failure si je veux avoir la valeur de mon retour(mon resultat, donc la valeur "contenue" par mon Try).

Voici un exemple tiré de FUNCTIONAL ERROR HANDLING IN SCALA (opens new window):

import scala.util.{Try,Success,Failure}

def toInt(s: String): Try[Int] = Try {
    Integer.parseInt(s.trim)
}

toInt(x) match {
    case Success(i) => println(i)
    case Failure(s) => println(s"Failed. Reason: $s")
}

Je trouve que c'est plutôt expressif et à l'usage cela apporte une meilleure lisibilité de mon code.

# Scala c'est top, mais Kotlin est apparu ... Et aussi Arrow

Je ne me souviens pas de à quel moment exact j'ai entendu parler de Kotlin, mais c'était en 2012 Faites vos classes en Kotlin (opens new window). Ma 1ère conclusion fut "ce nouveau langage me plaît beaucoup, une touche de Java, une touche de Javascript, une touche de C#". 🖐️ J'aurais du ajouter "une touche de Scala", mais mon "voyage fonctionnel" n'a réellement commencé qu'en Avril 2016 (à San Fransisco 😉).

En Décembre 2018, j'écrivais 🇬🇧 Some reasons why I build my server stack with Vert-x, Kotlin, and Arrow.kt (opens new window). J'avais découvert lors d'une conférence Arrow.kt (opens new window)

Et ce qui m'a le plus plus c'était que Arrow fournissait un Try 🎉:

fun giveMeSomething(): Try<Any> {
  return Try {
    when((0..2).shuffled().first()) {
      0 -> throw Exception("🤭 Oups! I did it again")
      1 -> 42
      else -> 666
    }
  }
}

router.get("/oups") { context ->
  giveMeSomething().let {
    when(it) {
      is Failure -> context.json(json { obj("message" to it.exception.message) })
      is Success -> context.json(json { obj("number" to it.value) })
    }
  }
}

J'étais heureux 😆

# Mais ... 😢

Mais un jour, tristesse, en bootstrapant un nouveau projet, je découvre en parcourant la documentation, que Try est deprecated: https://arrow-kt.io/docs/0.10/apidocs/arrow-core-data/arrow.core/-try/index.html#try (opens new window). 😢😠😭

# Mais ... 🎉

Mais en cherchant un peu je découvre qu'il existe un type Result en Kotlin 😃. Sauvé! Alors pas tout à fait, car on ne peut pas utiliser Return comme un type de retour 🙀. Don't panic, il est possible d'activer un flag pour pouvoir l'utiliser pleinement (gardez à l'esprit que le "fonctionnement" de Result dans le futur peut être amené à changer).

# Si vous utilisez Gradle

Activez le flag (1️⃣) de cette manière dans build.gradle.kts:

tasks.withType<KotlinCompile>() {
  kotlinOptions.jvmTarget = "11"
  kotlinOptions.freeCompilerArgs = listOf("-Xallow-result-return-type") // 1️⃣ le flag c'est ici
}

# Si vous utilisez Maven

Activez le flag (1️⃣) de cette manière dans pom.xml:

<plugins>
  <plugin>
    <groupId>org.jetbrains.kotlin</groupId>
    <artifactId>kotlin-maven-plugin</artifactId>
    <version>${kotlin.version}</version>
    <configuration>
      <jvmTarget>11</jvmTarget>
      <args>
        <!-- 1️⃣ le flag c'est ici -->
        <arg>-Xallow-result-return-type</arg>
      </args>
    </configuration>

# Et maintenant 🍾

Maintenant vous pouvez écrire ceci:

fun divide(a: Int, b: Int): Result<Int> {
  return try {
    Result.success(a / b)
  } catch (exception: Exception) {
    Result.failure(exception)
  }
}

Et l'utiliser comme cela:

divide(42, 0).let {
  when {
    it.isFailure -> {
      println("😡 ${it.exceptionOrNull()?.message}")
    }
    it.isSuccess -> {
      println("🙂 result=${it.getOrDefault(0)}")
    }
  }
}

ou aussi:

divide(42, 0)
  .onFailure { failure -> println("😡 ${failure.message}") }
  .onSuccess { result -> println("🙂 result=${result}") }

J'espère réellement que cela deviendra un standard en Kotlin car c'est réellement pratique et agréable à utiliser. Bien sûr il est possible d'écrire sa propre implémentation de Result. Mais je suis persuadé que c'est beaucoup mieux si c'est le langage qui supporte cette fonctionnalité plutôt qu'un framework.

Bon week end 😘

Last Articles