Dans un précédent article, nous avions parlé de dérivation de typeclass en Scala 2 avec Magnolia. Nous avions vu qu’il est possible de dériver des typeclass
pour des sealed trait
à l’aide de la méthode split
. En revanche, nous avons aussi observé que dans les cas de dérivation récursive, il nous fallait forcer la dérivation de notre sealed trait
.
Dans cet article, nous allons tâcher de montrer comment faire une dérivation récursive de typeclass avec magnolia en Scala 3.
Reprenons…
Imaginons que l’on ait le co-produit suivant :
A
et case class / objectsOn souhaite dériver la typeclass Show
suivante pour toute instance de A
:
On peut se demander pourquoi nous avons Show
et ShowObj
?
Ce pattern peut être utile lorsque l’on ne souhaite pas implémenter des comportements spécifiques à un type.
Par exemple, on peut imaginer que l’on ait besoin de Show
pour afficher les types primitifs sur la console (String
,Int
…) et ShowObj
pour afficher les case class
Show
et ShowObj
Dans notre exemple, on va essayer de dériver Show de manière à obtenir :
Show[String]
Show[Int]
ShowObj[CaseClassOne]
ShowObj[CaseClassTwo]
Maintenant, projetons notre exemple sur notre Trait A
On remarque que l’on va souhaiter obtenir les dérivations suivantes :
Show[String]
ShowObj[A]
→ va être obtenu par la méthode splitShowObj[B]
ShowObj[C]
On peut voir avec la figure 3 que Show[C]
va être récursive.
En effet, C
contient un A
qui peut être un C
qui contient un A
…
Nous avons vu qu’avec Magnolia en Scala 2, la dérivation récursive de typeclass semble avoir des bugs. Partons d’un exemple rapide proche de ce que nous voulons faire.
Voici l’utilisation de ce code.
Qu’en est-il en Scala 3 ?
On est toujours obligé de forcer la dérivation. La documentation de Magnolia l’explique ici : https://github.com/softwaremill/magnolia#limitations.
Un exemple
Dans cet exemple, l’idée est de reprendre le problème de la figure 2
, mais en Scala 3.
On va tâcher de dériver la typeclass Show
pour le trait suivant :
Le type Kind
représente donc soit des humains, soit des elfes, soit des êtres hybrides (avec deux noms ^^).

Reprenons le code de la figure 4 pour le porter en Scala 3 :
Comme on l’a vu dans la Figure 3 nous allons avoir besoin de Show[String]
pour afficher les String
sur la console
Désormais, nous avons un moyen d’afficher nos String
et le squelette principal de notre dérivation.
Il nous faut alors implémenter split
et join
qui vont nous permettre de dériver les typeclass pour les sealed trait et les case class :
Arrêtons-nous sur deux choses :
- Le
showObj
dejoin
:- On regarde si on souhaite dériver un
Show
ou unShowObj
de notre case class (en l’occurrence, on voudra unShow
, si on a un type primitif, ou unShowObj
, si on a une case class). - On formate correctement les valeurs des paramètres en les déréférençant, c'est-à-dire en récupérant leur valeur “réelle”.
- On regarde si on souhaite dériver un
- Le
showObj
desplit
:- On parcourt tous les sous-types d’un sealed trait (en l’occurrence, les sous-types de
Kind
) pour trouver celui que l’on cherche à dériver. - On cast notre sous-type dans son type réel (eg. j’ai un sous-type de
Kind
, si je vois que son type réel estElf
, alors je le cast enElf
) et on appelleshowObj
pour ce bon type.
- On parcourt tous les sous-types d’un sealed trait (en l’occurrence, les sous-types de
Utilisons tout cela désormais.
Ce qui donne.
Si on retourne sur la Figure 3 on voit que l’on a bien obtenu :
Show[String]
ShowObj[A]
→ obtenu par la méthode splitShowObj[B]
ShowObj[C]
Et que Show[T]
peut aussi dériver un A
(cf. HYBRID(ELF(AELFRAED),HUMAN(MAEVA))
) comme indiqué dans la Figure 2.
Voilà le code exécutable dans une worksheet, happy hacking!
Et voici l’utilisation.
Ressources
code de l’article : https://github.com/univalence/recursive-derivation-magnolia/blob/main/src/main/scala/ShowDerivationMagnolia3.scala