0

I'm working on something in work that I want to generalize over (some behavior that repeats exactly in several classes but for different concrete types [called it C, see bellow] ) with some restrictions of some interfaces(traits) in order be able to use some of the behaviors of those interfaces that are common between all the concrete types (Sound very abstract and general.. the best way to explain my problem is to look at the code).

I simplified the real code with a similar structure just to demonstrate the problem

package playground

object Conformance {
  trait S
  trait G
  trait R {
    type ST <: S
    type GT <: G

    def getS: ST
    def getG: GT
  }
  trait RT[SD <: S, GD <: G] extends R {
    override type ST = SD
    override type GT = GD
  }
  
  final case class ConcreteS() extends S

  final case class ConcreteG() extends G

  final class C1 extends RT[ConcreteS, ConcreteG] {
    override def getS: ConcreteS = ConcreteS()

    override def getG: ConcreteG = ConcreteG()
  }
  //some different impl
  final class C2 extends RT[ConcreteS, ConcreteG]{
    override def getS: ConcreteS = ConcreteS()

    override def getG: ConcreteG = ConcreteG()
  }
  //some different impl
  final class C3 extends RT[ConcreteS, ConcreteG]{
    override def getS: ConcreteS = ConcreteS()

    override def getG: ConcreteG = ConcreteG()
  }

  //generic impl over W
  trait GeneralizationOverW[C <: RT[S, G]] {
   // With `<: RT[S, G]` restriction I know C is RT with some S and G I can use S and G behaviours
  }
  
  class W1 extends GeneralizationOverW[C1]
  class W2 extends GeneralizationOverW[C2]
  class W3 extends GeneralizationOverW[C3]

}


Imagine that C1,C2,C3 have different implementations (which are code generated), and I tried to generalize over the W's using different C's with GeneralizationOverW

I tried to read about compound types in Scala spec, but could not figure out why I get the following compile error and how to fix it:

type arguments [playground.Conformance.C1] do not conform to trait GeneralizationOverW's type parameter bounds [C <: playground.Conformance.RT[playground.Conformance.S,playground.Conformance.G]] class W1 extends GeneralizationOverW[C1]

Edited: updated code after suggestion:

package playground

object Conformance {
  trait S
  trait SS extends S
  trait G
  trait R {
    type ST <: S
    type GT <: G

    def getS: ST
    def getG: GT
  }
  trait RT[SD <: S, GD <: G] extends R {
    override type ST = SD
    override type GT = GD
  }

  final case class ConcreteS1() extends SS
  final case class ConcreteS2() extends SS
  final case class ConcreteS3() extends SS

  final case class ConcreteG1() extends G
  final case class ConcreteG2() extends G
  final case class ConcreteG3() extends G

  final class C1 extends RT[ConcreteS1, ConcreteG1] {
    override def getS: ConcreteS1 = ConcreteS1()

    override def getG: ConcreteG1 = ConcreteG1()
  }
  //some different impl
  final class C2 extends RT[ConcreteS2, ConcreteG2]{
    override def getS: ConcreteS2 = ConcreteS2()

    override def getG: ConcreteG2 = ConcreteG2()
  }
  //some different impl
  final class C3 extends RT[ConcreteS3, ConcreteG3]{
    override def getS: ConcreteS3 = ConcreteS3()

    override def getG: ConcreteG3 = ConcreteG3()
  }

  //generic impl over W
  trait GeneralizationOverW[ST <: S,GT <: G , CON <: RT[ST, GT]] {
    //  With `<: RT[S, G]` restriction I know C is RT with some S and G I can use S and G behaviours
  }

  class W1 extends GeneralizationOverW[ConcreteS1,ConcreteG1,C1]
  class W2 extends GeneralizationOverW[ConcreteS2,ConcreteG2,C2]
  class W3 extends GeneralizationOverW[ConcreteS3,ConcreteG3,C3]


}

I think it works now :)

2 Answers 2

1

I am not sure this is what you are looking for, but you can try:

trait GeneralizationOverW[S1 <: S, G1 <: G, C <: RT[S1, G1]] {
  // With `<: RT[S, G]` restriction I know C is RT with some S and G I can use S and G behaviours
}

class W1 extends GeneralizationOverW[ConcreteS, ConcreteG, C1]
class W2 extends GeneralizationOverW[ConcreteS, ConcreteG, C2]
class W3 extends GeneralizationOverW[ConcreteS, ConcreteG, C3]
Sign up to request clarification or add additional context in comments.

Comments

1

Several things are happening:

trait GeneralizationOverW[C <: RT[S, G]] {

}

It expects that C passed here will extends RT[S, G] exactly, using S and G and not its subtypes because RT is invariant in both paramaters.

You can make it covariant by adding +

trait RT[+SD <: S, +GD <: G] {
  def getS: SD
  def getG: GD
}

I removed extends R because path-dependent types ST and GT defined here:

trait R {
  type ST <: S
  type GT <: G

  def getS: ST
  def getG: GT
}

are invariant and cannot be made covariant. If you give up on using R (your example weren't using path-dependent types at all) you can create a valid code:

object Conformance {
  trait S
  trait G

  trait RT[+SD <: S, +GD <: G] {
    def getS: SD
    def getG: GD
  }

  final case class ConcreteS() extends S

  final case class ConcreteG() extends G

  final class C1 extends RT[ConcreteS, ConcreteG] {
    override def getS: ConcreteS = ConcreteS()

    override def getG: ConcreteG = ConcreteG()
  }
  //some different impl
  final class C2 extends RT[ConcreteS, ConcreteG]{
    override def getS: ConcreteS = ConcreteS()

    override def getG: ConcreteG = ConcreteG()
  }
  //some different impl
  final class C3 extends RT[ConcreteS, ConcreteG]{
    override def getS: ConcreteS = ConcreteS()

    override def getG: ConcreteG = ConcreteG()
  }

  //generic impl over W
  trait GeneralizationOverW[C <: RT[S, G]] {
   // With `<: RT[S, G]` restriction I know C is RT with some S and G I can use S and G behaviours
  }

  class W1 extends GeneralizationOverW[C1]
  class W2 extends GeneralizationOverW[C2]
  class W3 extends GeneralizationOverW[C3]

}

However, if you do need these path-dependent types, you should replace hardcoded S and G with bound parameters as in @TomerShetah answer.

3 Comments

I do need those path-dependent types, not because I actually using it but because those traits already have "clients" that using it as it is (the fact that RT is a R type and etc...) - so I cannot remove or change anything except GeneralizationOverW and W1,W2,W3. tried what you suggested with making it covariance, and now I am getting that is not possible due to the path-dependent types ``` covariant type SD occurs in invariant position in type SD of type ST override type ST = SD ``` also tried to do what @TomerShetah and that does not seem to work as well.
updating the post with your suggestions and extending it a bit
never mind your explanation + what tomer suggested I think did work for me, thanks for the help guys!

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.