Providing multiple instances of same implicit specialized with different type parameters

StackOverflow https://stackoverflow.com/questions/21293849

  •  01-10-2022
  •  | 
  •  

Вопрос

Having an implicit instance implementation which perfectly fits for multiple types I need to provide a module with concrete instances for specific types. Following is something similar to my attempt to achieve this in the spirit of "Cake Pattern".

trait Show[ a ]{
  def show( a: a ): String
}
trait PrimitiveShowInstance[ a ] {
  implicit def primitiveShowInstance = new Show[ a ] {
    def show( a: a ) = a.toString
  }
}
object SomeModule
  extends PrimitiveShowInstance[ Int ]
  with PrimitiveShowInstance[ String ]
  with PrimitiveShowInstance[ Boolean ]

The above code fails to compile complaining about inheriting the same trait twice. This is obviously just an example and in real case the signature of implicit conversion is quite more involved - it includes other implicits and plenty of type parameters. So introducing an "alias" method is not an option, since due to Scala's syntactic inabilities I'll still have to copy-paste the method signature.

How to solve this issue?

Это было полезно?

Решение 2

Following is a solution which allows to write the implementation of primitiveShowInstance only once, that's why it scales perfectly for more involved cases. The idea is pretty simple: we introduce another implicit, which only serves as an evidence of support for primitiveShowInstance implementation by a certain type.

trait Show[ a ]{
  def show( a: a ): String
}
trait PrimitiveShowInstance {
  implicit def primitiveShowInstance[ a ]( implicit support: Support[ a ] ) = 
    new Show[ a ] {
      def show( a: a ) = a.toString
    }
  // Simply an evidence of support for this by type `value`.
  class Support[ value ]
}
object SomeModule extends PrimitiveShowInstance {
  implicit val intPrimitiveShowInstanceSupport = new Support[Int]
  implicit val stringPrimitiveShowInstanceSupport = new Support[String]
}

Другие советы

In this situation what you're really experiencing is an issue with type erasure. What you really want to do is the following:

 object SomeModule{
   implicit val primInt = new Show[Int}{
     def show(a: Int) = a toString ()
   }
   //and so on for each "A"
 }

Wherein you make sure that you always include include SomeModule._ so that the implicits are in scope. That allows you to forgoe the need to even have the implicit def primiteShowInstance method in your PrimitiveShowInstance trait.

One of the ways people get around exactly what you're seeing is just the rather vulgar:

 trait SomeFoo{
   self: WithIntFoo with WithDoubleFoo =>
 }

and so on. Yes, ugly. Yes, sometimes less than wonderful. However, this will get around the problem of type erause and implementing the same trait several times over.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top