Search code examples
scalagenericstypeclassimplicitdotty

Scala implicit def returning A with B


I've been wracking my head against this and I can't figure out if there is a way to properly do this. I feel I know what the problem is, but don't know how to solve it.

I have a method:

implicit def combineAlg[A: Alg, B: Alg]: Alg[A with B] = ...

if I call it explicitly it works fine, however it never gets implied properly.

// works
implicit val comb: Alg[A with B] = combineAlg[A, B]

// doesn't work
implicit val comb: Alg[A with B] = implicitly[Alg[A with B]]

Through my debugging with -Xlog-implicits, I believe its calling combineAlg[A with B, Nothing].

I'm looking to find a way to do something like:

implicit def combineExpAlg[AB, A >: AB, B >: AB]

or

implicit def combineExpAlg[AB, A, B](implicit ev1: AB <:< A, ev2: AB <:< B)

so that it understands that it needs to split apart the "with", but neither helps.

Not sure if there is a way to do this, really its an experiment I'm doing for "object algebras" in Scala and I'm trying to see how to remove boilerplate.

It'd be awesome if there was a solution. A dotty solution would also be acceptable, as I'm also implementing it there to see if some of the new features make it simpler.

In case more information is needed you can view the repository here What I'm trying to change is algebra.combineExpAlg. It will look like it's working because I define specific implicits in algebra.interpreters.package that specifically spell out each interpreter pair, which is what I'm trying to generalize.


Solution

  • The following code compiles:

      trait Alg[T]
    
      trait LowPriorityAlg {
        implicit def bAlg: Alg[B0] =  ???
      }
      object Alg extends LowPriorityAlg {
        implicit def aAlg: Alg[A0] =  ???
        implicit def combineAlg[AB, A: Alg, B: Alg](implicit ev1: AB <:< A, ev2: AB <:< B): Alg[AB] = ???    
      }
    
      trait A0
      trait B0
    
      val comb: Alg[A0 with B0] = Alg.combineAlg[A0 with B0, A0, B0]
    
      val comb1: Alg[A0 with B0] = implicitly[Alg[A0 with B0]]