Embedded type constraints and synchronizing embedded type instances


#1

Hi,

I have a “container” type with an embedded “contained” type.

Container Code Example
trait Container {
  type E
  val instanceA : E
  val instanceB : E
}

And I want to use multiple containers with the same type so I could “synchronize” (i.e. compare or use for interaction) elements between them.

CombiningConsumer Code Example
trait CombiningConsumer {
  type EBASE 
  val containerA : Container { type E = EBASE }
  val containerB : Container { type E = EBASE }
}

Next I wanted to add more containers based on some manipulation of containers inside such a CombiningConsumer that have the same contained type EBASE (so that I could use existing elements with these new containers).

Derived Container From Consumer Example
case class TransposedContainer(consumer : CombiningConsumer) extends Container {
  type E = consumer.EBASE
  override val instanceA: consumer.EBASE = consumer.containerA.instanceB
  override val instanceB: consumer.EBASE = consumer.containerA.instanceA
}

But now when I try to use TransposedContainer as an instance of a Container with a type constraint that the embedded type E = consumer.EBASE (in the context of a CombiningConsumer), it doesn’t work.

Type Mismatch for TransposedContainer as Container { type E = EBASE }
trait CombiningConsumer {
  type EBASE
  val containerA : Container { type E = EBASE }
  val containerB : Container { type E = EBASE }

  // Error - Type Mismatch:
  // found : TransposedContainer
  // required : Container { type E = CombiningConsumer.this.EBASE }
  val derivedContainerA : Container { type E = EBASE } = TransposedContainer(this)
}

Could anyone explain why this doesn’t work and how (or if) I can get around it (without brute force casting)?

Full code for reference
trait Container {
  type E
  val instanceA : E
  val instanceB : E
}

trait CombiningConsumer {
  type EBASE
  val containerA : Container { type E = EBASE }
  val containerB : Container { type E = EBASE }

  // Error - Type Mismatch:
  // found : TransposedContainer
  // required : Container { type E = CombiningConsumer.this.EBASE }
  val derivedContainerA : Container { type E = EBASE } = TransposedContainer(this)
}

case class TransposedContainer(consumer : CombiningConsumer) extends Container {
  type E = consumer.EBASE
  override val instanceA: consumer.EBASE = consumer.containerA.instanceB
  override val instanceB: consumer.EBASE = consumer.containerA.instanceA
}

thanks.


#2

I think the problem is that the compiler doesn’t track that derivedContainerA.consumer eq this. You have to help it by lifting this information into the type system. For instance like so:

trait Container {
  type E
  val instanceA : E
  val instanceB : E
}

trait CombiningConsumer { self =>
  type EBASE
  val containerA : Container { type E = EBASE }
  val containerB : Container { type E = EBASE }

  val derivedContainerA : Container { type E = EBASE } = TransposedContainer[this.type](this)
}

case class TransposedContainer[C <: CombiningConsumer](consumer : C) extends Container {
  type E = consumer.EBASE
  override val instanceA: consumer.EBASE = consumer.containerA.instanceB
  override val instanceB: consumer.EBASE = consumer.containerA.instanceA
}

#3

Wow, thanks, I would never have thought of that :slight_smile:

I will start using it.