简体   繁体   中英

Use type parameter's type argument as field type in Scala

So following up on Recursive type definition in Scala , I now have my type for the data-bearing structure of CatenableListFromQueue :

object CatenableListFromQueue {

  sealed trait CatList[+Q, +E]

  object Empty extends CatList[Nothing, Nothing]

  case class C[Q[_], E](x: E, q: Q[CatList[Q, E]]) extends CatList[Q, E]
}

now the intended value for the parameter Q is, naturally, a queue. My Queue itself is a type class, ie it only defines the methods for some generic data-bearing structure:

trait Queue[E, Q] {

  def empty: Q

  def isEmpty: Q => Boolean

  def snoc: (Q, E) => Q

  def head: Q => E

  def tail: Q => Q
}

It seems to work just fine for me, eg

class BatchedQueue[E] extends Queue[E, (List[E], List[E])] {
  override def empty: (List[E], List[E]) = (Nil, Nil)

  override def isEmpty: ((List[E], List[E])) => Boolean = {
    case (Nil, _) => true
    case _ => false
  }

  val checkf: (List[E], List[E]) => (List[E], List[E]) = {
    case (Nil, r) => (r.reverse, Nil)
    case q => q
  }

  override def snoc: ((List[E], List[E]), E) => (List[E], List[E]) = {
    case ((f, r), x) => checkf(f, x :: r)
  }

  override def tail: ((List[E], List[E])) => (List[E], List[E]) = {
    case (Nil, _) => throw new IllegalStateException("tail called on an empty queue")
    case ((_ :: f), r) => checkf(f, r)
  }

  override def head: ((List[E], List[E])) => E = {
    case (Nil, _) => throw new IllegalStateException("head called on an empty queue")
    case ((x :: _), _) => x
  }
}

That all was going well until I needed to create a CatenableListFromQueue , which holds a data structure from the previous question with the data-bearing structure of the Queue , Queue#Q :

class CatenableListFromQueue[E, CL, Q](queue: Queue[E, Q]) extends CatenableList[E] {

  type CL = CatList[queue#Q, E]

So in my ideal world the class gets an instance of Queue , ie the implementation of the queue operations over some structure (here: Queue#Q ), and in turn creates and handles a CatList of the actual elements and the Queue#Q s containing further CatList s.

The problem is, I cannot seem to come up with a way to code that in Scala; in Haskell, it seems as trivial as:

data CatList q a = E | C a (q (CatList q a))

instance Queue q => CatenableList (CatList q) where
   -- methods

But in Scala I cannot come up with any way to encode the same as my Queue requires that data-bearing structure as a parameter, and the data-bearing naturally contains it elements which in this case contain further data-bearing structures of the same type.

Well, following Haskell's approach was the right way:

object CatenableListFromQueue {

  sealed trait CatList[+Q[_], +E]

  object Empty extends CatList[Nothing, Nothing]

  case class C[Q[_], E](x: E, q: Q[Susp[CatList[Q, E]]]) extends CatList[Q, E]
}

trait CatenableListFromQueue[E, QBS[_]] extends CatenableList[E, CatList[QBS, E]] {

  type Q = Queue[Susp[CatList[QBS, E]], QBS[Susp[CatList[QBS, E]]]]

  def q: Q

  type CL = CatList[QBS, E]

  def just(e: E): CL = C(e, q.empty)

  // etc.

So we keep the CatenableListFromQueue a trait, and use it later with a specific type for QBS :

new CatenableListFromQueue[Int, HoodMelvilleQueue.Repr] {
  val q = new HoodMelvilleQueue[Susp[CL]]
}

This way, it all compiles just fine, and seems to work well.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM