简体   繁体   中英

Type Mismatch on Recursive Call of Method with Generic Traversable Parameters in Scala

I can make a (silly) drop wrapper which accepts some Traversable type and returns that same Traversable type using the following code:

import scala.collection.TraversableLike

def dropN[E, T <: Traversable[E]]
(xs: T with TraversableLike[E, T], n: Int): T =
{
    xs.drop(n)
}

dropN(0 to 3, 1) // returns: Range 1 to 3

However if I try to make a similar function with recursion:

// basically finds the tail
def dropNR[E, T <: Traversable[E]]
(xs: T with TraversableLike[E, T]): T =
{
    if (xs.size > 1) dropNR[E,T](xs.drop(1)) else xs
}

I get the following message: Type mismatch: expected T with TraversableLike[E, T], actual: T .

Meanwhile a pure pass-through recursive method has no compile errors (but would obviously recurse forever)

def recurse[E, T <: Traversable[E]]
(xs: T with TraversableLike[E, T]): T =
{
    recurse[E,T](xs)
}

Why am I getting a Type mismatch when I use drop?

Because xs.drop(1) returns a T , not the mixed in T with Traversable[E] type you mix in to the original xs . In recurse , you're returning the unmodified collection, which is why it works.

I'm not sure why you need all those extra type parameters (perhaps I'm missing something), this should do:

def dropNR[E](xs: Traversable[E]): Traversable[E] = {
  if (xs.size > 1) dropNR[E](xs.drop(1)) else xs
}

Turns out I need to use CanBuildFrom :

scala> :pa
// Entering paste mode (ctrl-D to finish)
import scala.collection.generic.CanBuildFrom

def dropR[E, D[E] <: Traversable[E]](xs: D[E])
(implicit cbf: CanBuildFrom[D[E], E, D[E]]): D[E] =
{
    if (xs.size > 1) dropR[E,D](xs.drop(1).to[D])  else xs.to[D]
}

// Exiting paste mode, now interpreting.

import scala.collection.generic.CanBuildFrom
dropR: [E, D[E] <: Traversable[E]](xs: D[E])(implicit cbf: scala.collection.generic.CanBuildFrom[D[E],E,D[E]])D[E]

scala> val l = List(1,2,3)
l: List[Int] = List(1, 2, 3)

scala> dropR(l)
res0: List[Int] = List(3)

scala> dropR(l.toSeq)
res1: scala.collection.immutable.Seq[Int] = List(3)

scala> dropR(l.toSet)
res2: scala.collection.immutable.Set[Int] = Set(3)

scala> dropR(l.toBuffer)
res3: scala.collection.mutable.Buffer[Int] = ArrayBuffer(3)

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