Reputation: 3598
I have this code:
object Peek {
implicit def traversableToPeek[A](underlying: Traversable[A]) = new Peek(underlying)
}
class Peek[A](underlying: Traversable[A]) {
/**
* Java-style peek method
*/
def peek(func: A => Unit): Traversable[A] = {
underlying.foreach(func)
underlying
}
}
while lets me write things like List(1,2,3).peek(println).map(_+1).peek(println)
which prints 1,2,3 and then 2,3,4 (after import Peek._
)
However, the compile-time value of that expression is Traversable[Int]
. This means that while this code compiles:
val l = List(1,2,3)
val l2 = 4 :: l
this code does not:
val l = List(1,2,3).peek(println)
val l2 = 4 :: l
since ::
is not defined on Traversable
. The intent of peek is to allow side-effect only operations without requiring somewhat ugly constructs like l.map(e => println(e); e)
I tried adding a second generic parameter T <: Traversable
or T[A] <: Traversable[A]
but could not get it compile in a manner where the implicit conversion worked.
I feel this should be possible, but I am not familiar enough with Scala to get the syntax correct.
Upvotes: 0
Views: 557
Reputation: 992
I don't see much difference than above, here is another alternative using CanBuildFrom
import scala.collection.generic._
import scala.language.higherKinds
implicit class CollWrapper[Coll[X] <: Traversable[X], A](coll:Coll[A]) {
def peek(f:A => Unit)(implicit cbf:CanBuildFrom[Coll[A], A, Coll[A]]):Coll[A] = {
val builder = cbf(coll)
coll.foreach{elem =>
builder += elem;
f(elem)
}
coll
}
}
List(1,2,3,4).peek(println).map(x => x * 3)
println("--")
List("One", "Two", "Three").peek(println).map(x => x + "!")
println("--")
List('c', 'd', 'e').peek(println).map(x => x + 3)
println("--")
Set("Nine", "Ten", "Twelve").peek(println).map(x => x + "!")
println("--")
Map(1 -> "One", 2 -> "Two", 3 -> "Three").peek(println).map{case (k,v) => (k, v + "!")}.toMap
println("--")
Upvotes: 0
Reputation: 170713
The second generic parameter is the correct approach:
implicit class Peek[C[X] <: Traversable[X], A](underlying: C[A]) {
def peek(func: A => Unit): C[A] = {
underlying.foreach(func)
underlying
}
}
(in Scala 2.13, replace deprecated Traversable
with Iterable
).
In case you have a type extending Traversable[Something]
which isn't itself generic, the above won't work, but
implicit class Peek[C <: Traversable[A], A](underlying: C with Traversable[A]) {
def peek(func: A => Unit): C = {
underlying.foreach(func)
underlying
}
}
should.
Upvotes: 2