In this parameterized Scala function, why do I need a listing?

In this parameterized function, why do I need a listing? And how can I get rid of him?

/** Filters `xs` to have only every nth element.
  */
def everyNth[A <% Iterable[B], B](xs: A, n: Int, offset: Int = 0): A =
  (xs.zipWithIndex collect { case (x, i) if (i - offset) % n == 0 => x }).asInstanceOf[A]

If I don't have a role at the end, I get this error message:

type mismatch; found : Iterable[B] required: A

This function (with cast) works for all cases in which I tried it, and I know that when entering things in the REPL, such as the following: Scala can correctly determine the type of result, if not in the context of the parameterized function:

scala> val a: Stream[Int] = (Stream.from(0).zipWithIndex collect { case (x, i) if (i + 3) % 5 == 0 => x })
a: Stream[Int] = Stream(2, ?)

scala> a take 10 force
res20: scala.collection.immutable.Stream[Int] = Stream(2, 7, 12, 17, 22, 27, 32, 37, 42, 47)

Explain, please!

+5
source share
3 answers

Following some suggestions in the comments, I looked at CanBuildFrom, and this is what I came up with:

import scala.collection.IterableLike
import scala.collection.generic.CanBuildFrom

/** Filters `xs` to have only every nth element.
  */
def everyNth[A, It <: Iterable[A]]
        (xs: It with IterableLike[A, It], n: Int, offset: Int = 0)
        (implicit bf: CanBuildFrom[It, A , It]): It = {
  val retval = bf()
  retval ++= xs.zipWithIndex collect { case (x, i) if (i - offset) % n == 0 => x }
  retval.result     
}

Yay, it works !!!

And there is NO . Thus, it even works for ranges.

, retval, "++ =", , , , - , .

, , , . I.e., A 's, (A, A)' s:

def zipWithSelf[A, It[A] <: Iterable[A]]
        (xs: It[A] with IterableLike[A, It[A]])
        (implicit bf:  CanBuildFrom[It[A], (A, A), It[(A, A)]]): It[(A, A)] = {
    val retval = bf()
    if (xs.nonEmpty) {
      retval ++= xs zip xs.tail
      retval.result
  } else retval.result
}

:

/** Calls `f(x)` for all x in `xs` and returns an Iterable containing the indexes for
  * which `f(x)` is true.
  *
  * The type of the returned Iterable will match the type of `xs`. 
  */
def findAll[A, It[A] <: Iterable[A]]
        (xs: It[A] with IterableLike[A, It[A]])
        (f: A => Boolean)
        (implicit bf:  CanBuildFrom[It[A], Int, It[Int]]): It[Int] = {
    val retval = bf()
    retval ++= xs.zipWithIndex filter { p => f(p._1) } map { _._2 }
    retval.result
}

"Like" CanBuildFrom, . , CanBuildFrom IterableLike, .

+4

, collect Iterable, , , Range:

scala> everyNth(1 to 10, 2)
java.lang.ClassCastException: scala.collection.immutable.Vector cannot be cast to scala.collection.immutable.Range$Inclusive
        at .<init>(<console>:9)
        at .<clinit>(<console>)
        at .<init>(<console>:11)
        at .<clinit>(<console>)
        at $print(<console>)
        at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
        at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
        at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
        at java.lang.reflect.Method.invoke(Method.java:616)
        at scala.tools.nsc.interpreter.IMain$ReadEvalPrint.call(IMain.scala:704)
        at scala.tools.nsc.interpreter.IMain$Request$$anonfun$14.apply(IMain.scala:920)
        at scala.tools.nsc.interpreter.Line$$anonfun$1.apply$mcV$sp(Line.scala:43)
        at scala.tools.nsc.io.package$$anon$2.run(package.scala:25)
        at java.lang.Thread.run(Thread.java:679)
+3

The problem is that by calling collecton xs, you convert it to Iterable[B]. A <% Iterable[B]means that it Acan be considered as Iterable[B], which does not necessarily mean that Iterable[B]it can also be considered as A. What actually happens here:

def everyNth[A, B](xs: A, n: Int, offset: Int = 0)(implicit view: (A => Iterable[B])): A =
  (view(xs).zipWithIndex collect {
    case (x, i) if (i + offset) % n == 0 => x
  }).asInstanceOf[A]

If I have, for example, the following:

class Foo
implicit def foo2Iterable(foo: Foo) = List(foo)

and call

everyNth(new Foo, 2)

I get

java.lang.ClassCastException: scala.collection.immutable.$colon$colon cannot be cast to Foo

You should avoid casting here. Or you add a view fromIterable[B] => A

edit: type bound does not work here.

+1
source

All Articles