Skip to content

Can't find implicit delegating to lower-priority implicit via path-dependent type #17212

@DmytroMitin

Description

@DmytroMitin

Compiler version

3.3.0-RC3

Motivation

I have two type classes. They are working on type level. They accept a type and return a type. The 1st type class delegates to the 2nd (it can do some extra work but for minimality it just delegates). The 2nd type class returns Int for any type T (higher-priority instance) or String for any type T (lower-priority instance). In a more meaningful example the "return type" is calculated in some way and the higher-priority instance manages the case with additional upper bound or additional type-class constraint but for minimality it's just two similar instances, higher-priority and lower-priority. I make the "return type" of the 1st type class a type member.

  1. If I make the "return type" of the 2nd type class a type parameter then everything compiles
trait TC1[T]:
  type S
object TC1:
  type Aux[T, S0] = TC1[T] {type S = S0}
  given [T, S](using TC2[T, S]): Aux[T, S] = null

trait TC2[T, S]
trait LowPriorityTC2:
  given [T]: TC2[T, String] = null
object TC2 extends LowPriorityTC2:
  given [T]: TC2[T, Int] = null

summon[TC1.Aux[Boolean, String]] // compiles
summon[TC2[Boolean, String]]     // compiles
summon[TC1.Aux[Boolean, Int]]    // compiles
summon[TC2[Boolean, Int]]        // compiles

https://scastie.scala-lang.org/DmytroMitin/MEyLY8TkRfaif5C778wkRw/1

  1. If I make the "return type" of the 2nd type class a type member and use an extra type parameter in the instance declaration of the 1st type class then everything compiles too
trait TC1[T]:
  type S
object TC1:
  type Aux[T, S0] = TC1[T] { type S = S0}
  given [T, S](using TC2.Aux[T, S]): Aux[T, S] = null

trait TC2[T]:
  type S
trait LowPriorityTC2:
  type Aux[T, S0] = TC2[T] {type S = S0}
  given [T]: Aux[T, String] = null
object TC2 extends LowPriorityTC2:
  given [T]: Aux[T, Int] = null

summon[TC1.Aux[Boolean, String]] // compiles
summon[TC2.Aux[Boolean, String]] // compiles
summon[TC1.Aux[Boolean, Int]]    // compiles
summon[TC2.Aux[Boolean, Int]]    // compiles

https://scastie.scala-lang.org/DmytroMitin/MEyLY8TkRfaif5C778wkRw/2

  1. But if I make the "return type" of the 2nd type class a type member and do not use an extra type parameter in the instance declaration of the 1st type class, using path-dependent type instead, then the code doesn't compile (even although the code manually resolved compiles):

Minimized code

trait TC1[T]:
  type S
object TC1:
  type Aux[T, S0] = TC1[T] { type S = S0}
  given [T](using tc2: TC2[T]): Aux[T, tc2.S] = null

trait TC2[T]:
  type S
trait LowPriorityTC2:
  type Aux[T, S0] = TC2[T] {type S = S0}
  given [T]: Aux[T, String] = null
object TC2 extends LowPriorityTC2:
  given [T]: Aux[T, Int] = null
  
summon[TC1.Aux[Boolean, String]] // doesn't compile
summon[TC1.Aux[Boolean, String]](using TC1.given_Aux_T_S[Boolean](using TC2.given_Aux_T_String[Boolean])) // compiles
summon[TC2.Aux[Boolean, String]] // compiles
summon[TC1.Aux[Boolean, Int]]    // compiles
summon[TC2.Aux[Boolean, Int]]    // compiles

https://scastie.scala-lang.org/DmytroMitin/MEyLY8TkRfaif5C778wkRw

Output

No given instance of type TC1.Aux[Boolean, String] was found for parameter x of method summon in object Predef.
I found:
    TC1.given_Aux_T_S[Boolean](TC2.given_Aux_T_Int[Boolean])
But given instance given_Aux_T_S in object TC1 does not match type TC1.Aux[Boolean, String].
  summon[TC1.Aux[Boolean, String]]

Expectation

summon[TC1.Aux[Boolean, String]] in 3) should compile too.

Or is it intended difference between type parameters and type members? (Functional dependencies?)

In Scala 2.13 behavior is the same

  1. https://scastie.scala-lang.org/DmytroMitin/mKmrLm71SPiOuXLBpx1ulg
  2. https://scastie.scala-lang.org/DmytroMitin/mKmrLm71SPiOuXLBpx1ulg/1
  3. https://scastie.scala-lang.org/DmytroMitin/mKmrLm71SPiOuXLBpx1ulg/2

But in 2.13 there is easier reproduction (without lower priority) scala/bug#12767

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions