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.
- 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
- 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
- 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
- https://scastie.scala-lang.org/DmytroMitin/mKmrLm71SPiOuXLBpx1ulg
- https://scastie.scala-lang.org/DmytroMitin/mKmrLm71SPiOuXLBpx1ulg/1
- https://scastie.scala-lang.org/DmytroMitin/mKmrLm71SPiOuXLBpx1ulg/2
But in 2.13 there is easier reproduction (without lower priority) scala/bug#12767
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
Intfor any typeT(higher-priority instance) orStringfor any typeT(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.https://scastie.scala-lang.org/DmytroMitin/MEyLY8TkRfaif5C778wkRw/1
https://scastie.scala-lang.org/DmytroMitin/MEyLY8TkRfaif5C778wkRw/2
Minimized code
https://scastie.scala-lang.org/DmytroMitin/MEyLY8TkRfaif5C778wkRw
Output
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
But in 2.13 there is easier reproduction (without lower priority) scala/bug#12767