Est-il possible d'utiliser une classe de types pour implémenter un trait?
J'ai une situation où je voudrais implémenter un trait donné ( CanBeStringdans l'exemple ci-dessous). Je voudrais avoir la possibilité d'implémenter ce trait en utilisant une classe de cas nouvellement créée ( NewImplementationdans l'exemple ci-dessous), ou de l'implémenter en ajoutant des fonctionnalités à un type préexistant (juste Intdans l'exemple ci-dessous), en utilisant un type classe. Ceci est probablement mieux illustré par ce qui suit:
package example
// typeclass
trait ConvertsToString[A] {
def asString(value: A): String
}
// the trait I would like the typeclass to implement
trait CanBeString {
def asString: String
}
// this implementation approach taken from the scala with cats book
object ConvertsToStringInstances {
implicit val intConvertsToString: ConvertsToString[Int] =
new ConvertsToString[Int] {
def asString(value: Int): String = s"${value}"
}
}
object ConvertsToStringSyntax {
implicit class ConvertsToStringOps[A](value: A) {
def asString(implicit c: ConvertsToString[A]): String = c.asString(value)
}
}
object Test {
import ConvertsToStringInstances._
import ConvertsToStringSyntax._
def testAsFunc(c: CanBeString): String = c.asString
case class NewImplementation (f: Double) extends CanBeString {
def asString = s"{f}"
}
println(testAsFunc(NewImplementation(1.002))) // this works fine!
println(testAsFunc(1)) // this sadly does not.
}
Est-ce que quelque chose comme ça est possible? Je ne découvre que récemment le sujet des classes de types, donc je suis conscient que ce que je demande ici est peut-être possible mais tout simplement imprudent - si c'est le cas, veuillez m'interrompre et me faire savoir ce que pourrait être un meilleur idiome.
Merci d'avance, et aussi après!
Réponses
Par exemple, vous pouvez avoir deux versions surchargées de testAsFunc(style POO et classe de type)
object Test {
...
def testAsFunc(c: CanBeString): String = c.asString
def testAsFunc[C: ConvertsToString](c: C): String = c.asString
println(testAsFunc(NewImplementation(1.002))) // {f}
println(testAsFunc(1)) // 1
}
Ou si vous préférez avoir le seul, testAsFuncvous pouvez ajouter des instances de la classe de type pour les sous-types du trait à implémenter
object ConvertsToStringInstances {
implicit val intConvertsToString: ConvertsToString[Int] = ...
implicit def canBeStringSubtypeConvertsToString[A <: CanBeString]: ConvertsToString[A] =
new ConvertsToString[A] {
override def asString(value: A): String = value.asString
}
}
object Test {
...
def testAsFunc[C: ConvertsToString](c: C): String = c.asString
println(testAsFunc(NewImplementation(1.002))) // {f}
println(testAsFunc(1)) // 1
}
Veuillez noter que si pour a cil y a à la fois OOP-ish c.asStringet extension-method, c.asStringseule la première est réellement appelée.