0

I am designing a F-Bound data type, and have a working companion object. I would like to reference this companion object from the trait itself, but I can't get the types right.

trait A[AA <: A[AA]] {
  self =>
  val data: String
}
case class A1(data : String) extends A[A1]

trait B[BB <: B[BB, AA], AA <: A[AA]] {
  self: BB =>
  val content: AA
  def companion: BComp[BB, AA]  // What is the correct type?
  def companion2: BComp2[BB, AA] // What is the correct type?
}
trait BComp[BB[X <: BB[X, AA], Y <: AA[Y]], AA[Y <: AA[Y]]]

trait BComp2[BB[X <: AA[X]], AA[X <: AA[X]]]

case class BInst[AA <: A[AA]](content: AA) extends B[BInst[AA], AA] {
  def companion = BInst
  def companion2 = BInst2
}

object BInst extends BComp[B, A]
object BInst2 extends BComp2[BInst, A]

A working solution for either companion or companion2 would suffice, although a general hint on how to construct these type signatures would be useful.

edit

I want to use the companion object to store canBuildFrom style implicits as well as Builders, but as the content type A has an upper bound, all generating functions need to be aware of this bounding, so hence the parametrization of the companion object trait. The inspiration for this design comes from GenericCompanion.scala, of course, adding the type bounds makes everything more difficult :P

Karalga
  • 495
  • 4
  • 11
  • + for a concise and complete *non compilable* minimal working example ;) – Andrey Tyukin Apr 20 '15 at 14:45
  • Ok, sorry - it compiles if you comment out the two defs `companion` and `companion2`. – Karalga Apr 20 '15 at 16:22
  • No, I really meant what I told. There was no sarcasm. The question *is* good. But in this case, instead of *running* code, we compute a *static compile time-approximation* of the runtime behaviour by using the type system. Therefore, the code is exactly as it should be: it fails at type-checking stage, not at runtime. – Andrey Tyukin Apr 20 '15 at 16:25
  • I'm pretty sure that I understand a little bit about CBF's and Builders and Companion objects: http://stackoverflow.com/questions/23432339/understanding-generictraversabletemplate-and-other-scala-collection-internals/24420552#24420552 However, I can not map all the A's and BB's to cbf's and builders. The statement that it is inspired by GenericCompanion is too vague, imho, so I don't understand what you are trying to achieve. – Andrey Tyukin Apr 21 '15 at 12:34
  • Another comment: The convention is that identifiers like `CC` stand for some kind of collection. In particular, it's always `CC[_]`, not just `CC`. I can not see it in your code. – Andrey Tyukin Apr 21 '15 at 12:36
  • Ah, I am not being clear enough: the idea is that `val content: Seq[AA]`, and trait B will also be inheriting `with IndexedSeq[A] with IndexedSeqLike[AA, B[BB, AA]]`. I didn't want to add that complexity to the question, since it does not influence the composition of the types. – Karalga Apr 21 '15 at 14:08
  • But in your question it's `val content: AA` not `val content: Seq[AA]`. The only way to make sense of it is to assume that `A[T] <: Seq[T]`. But then the definition of `A` does not make any sense to me, because it would be something like `Seq[Seq[...]]`. – Andrey Tyukin Apr 21 '15 at 15:58
  • Another problem with your last comment: `with IndexedSeq[A]` is illegal, because `A` requires one type parameter. – Andrey Tyukin Apr 21 '15 at 16:00
  • whoops, that was meant to be `with IndexedSeq[AA]`. I don't see why you would need to assume that `A[T] <: Seq[T]` - `content` is just a seq of F-Bound types `A`. I only need to define `def apply(idx: Int): AA = content(idx)`, and `B` behaves like a list. – Karalga Apr 21 '15 at 16:38
  • You listed following requirements: `val content: Seq[AA]` (comment), `val content: AA` (code), `AA <: A[AA]` (code). In order to fulfill them simultaneously, I assume that `A[X] <: Seq[X]`. I try to take a look at the whole thing again, but I'm not too optimistic, because now I'm more confused than before... As I said, a little bit more context (e.g. more details about what BComp and BComp2 are supposed to do), and following the conventions for naming higher kinded types would probably clarify the question. – Andrey Tyukin Apr 22 '15 at 13:19
  • As general remark. As far as I understand, `AA` and `BB` in definition of `B` stand for some types of the lowest kind `*`, `B` itself is something like `(*,*)->*` and `companion: BComp` is supposed to be implemented by an object. Objects can not take type parameters at all. Therefore, `BComp` should not depend on `AA` and `BB`, but only on `A` and `B`. Example: in Scala's `List[+A]`, it's `override def companion: GenericCompanion[List] = List`, it's not `GenericCompanion[List[A]]` or `GenericCompanion[A]` or anything like that. In your code however, `AA` and `BB` appear in type of `companion`. – Andrey Tyukin Apr 22 '15 at 13:31
  • thanks for trying! I didn't list the requirement of val content: Seq[AA] - you just wanted some more knowledge of my problem. But for the sake of this question, val content: AA is all that matters. `BComp` and `BComp2` are just two alternative traits that I have come up with that I want to implement in the case classes, but need to be able to reference via the trait `B`. I am not sure what benefit renaming `B` to `CC` really has - but that can be done. – Karalga Apr 22 '15 at 13:33
  • It's just a naming convention. Amount of `BB`'s in types name indicates it's "kindedness". In your code, it currently looks like `Interger[List]` instead of `List[Int]`, which is slightly confusing. But I keep looking. – Andrey Tyukin Apr 22 '15 at 13:37
  • `BComp` does not depend on `BB` - it just depends on some generic type, that is F-Bound and has one type parameter that is also F-Bound (this could be `B` and `A`, but it does not have to be!). Any type that fulfils this typing will work. – Karalga Apr 22 '15 at 13:38

1 Answers1

0

The obstacle to any clear definition of types for companion is that BComp is parametrized by two parametrized types - while the first one is compatible with B, the second one AA[Y <: AA[Y]] is impossible to construct. So quite simply, we need to add this type to the parametrization of B:

trait B[BB <: B[BB, AA, X],
        AA[T <: AA[T]] <: A[T], 
        X <: AA[X]]  {
    self: BB =>
    val content: X
    def companion: BComp[B, AA]
}

now we have a compatible type AA for our companion object (which needs only a small expansion):

trait BComp[BHere[BB <: BHere[BB, AAA, Z],
                  AAA[Y <: AAA[Y]],
                  Z <: AAA[Z]],
            AA[Y <: AA[Y]]]

Isn't that pretty?

case class BInst[X <: A[X]](content: X) extends B[BInst[X], A, X] {
    def companion: BComp[B, A] = BInst5
  }
object BInst extends BComp[B, A]

companion2

For companion2, we just need to change the first part of the parametrization of B, so that trait B and the companion trait become:

trait B[BB[TS <: AA[TS]] <: B[BB, AA, TS],
        AA[T <: AA[T]] <: A[T], 
        X <: AA[X]] {
    self: BB[X] =>
    val content: X
    def companion2: BComp[BB, AA]
  }

trait BComp[BHere[TS <: AA[TS]],
            AA[Y <: AA[Y]]]

This is slightly more manageable. The case class and case objects are:

case class BInst[X <: A[X]](content: X) extends B[BInst, A, X] {
  def companion2: BComp[BInst, A] = BInst5
}
object BInst extends BComp[BInst, A]

If this is useful to anybody the rethink. I have changed my own design approach and I suggest you do too. Coming up with a solution to these types has now been a purely academic exercise!

Karalga
  • 495
  • 4
  • 11