score:5
to be honest i'm not sure how that really works:
implicit def collectionextras[cc[x] <: iterable[x], a](xs: cc[a]) = new {
import collection.generic.canbuildfrom
def zipwith[b, c](ys: iterable[b])(f: (a, b) => c)
(implicit cbf:canbuildfrom[nothing, c, cc[c]]): cc[c] = {
xs.zip(ys).map(f.tupled)(collection.breakout)
}
}
scala> vector(2, 2, 2).zipwith(vector(4, 4, 4))(_ * _)
res1: scala.collection.immutable.vector[int] = vector(8, 8, 8)
i sort of monkey patched this answer from retronym until it worked!
basically, i want to use the cc[x]
type constructor to indicate that zipwith
should return the collection type of xs
but with c
as the type parameter (cc[c]
). and i want to use breakout
to get the right result type. i sort of hoped that there was a canbuildfrom
implicit in scope but then got this error message:
required: scala.collection.generic.canbuildfrom[iterable[(a, b)],c,cc[c]]
the trick was then to use nothing
instead of iterable[(a, b)]
. i guess that implicit is defined somewhere...
also, i like to think of your zipwith
as zip
and then map
, so i changed the implementation. here is with your implementation:
implicit def collectionextras[cc[x] <: iterable[x], a](xs: cc[a]) = new {
import collection.generic.canbuildfrom
def zipwith[b, c](ys: iterable[b])(f: (a, b) => c)
(implicit cbf:canbuildfrom[nothing, c, cc[c]]) : cc[c] = {
val builder = cbf()
val (i, j) = (xs.iterator, ys.iterator)
while(i.hasnext && j.hasnext) {
builder += f(i.next, j.next)
}
builder.result
}
}
note this article provides some background on the type constructor pattern.
score:8
the problem above is that your implicit conversion collectionextras
causes the obtained object to lose type information. in particular, in the solution above, the concrete collection type is lost because you're passing it an object of type iterable[a]
- from this point on, the compiler no longer knows the real type of xs
. although the builder factory canbuildfrom
programatically ensures that the dynamic type of the collection is correct (you really get a vector
), statically, the compiler knows only that zipwith
returns something that is an iterable
.
to solve this problem, instead of having the implicit conversion take an iterable[a]
, let it take an iterablelike[a, repr]
. why?
iterable[a]
is usually declared as something like:
iterable[a] extends iterablelike[a, iterable[a]]
the difference with iterable
is that this iterablelike[a, repr]
keeps the concrete collection type as repr
. most concrete collections, in addition to mixing in iterable[a]
, also mix in the trait iterablelike[a, repr]
, replacing the repr
with their concrete type, like below:
vector[a] extends iterable[a] with iterablelike[a, vector[a]]
they can do this because type parameter repr
is declared as covariant.
long story short, using iterablelike
causes you implicit conversion to keep the concrete collection type information (that is repr
) around and use it when you define zipwith
- note that the builder factory canbuildfrom
will now contain repr
instead of iterable[a]
for the first type parameter, causing the appropriate implicit object to be resolved:
import collection._
import collection.generic._
implicit def collectionextras[a, repr](xs: iterablelike[a, repr]) = new {
def zipwith[b, c, that](ys: iterable[b])(f: (a, b) => c)(implicit cbf: canbuildfrom[repr, c, that]) = {
val builder = cbf(xs.repr)
val (i, j) = (xs.iterator, ys.iterator)
while(i.hasnext && j.hasnext) {
builder += f(i.next, j.next)
}
builder.result
}
}
reading your question formulation more carefully ("how to write a zipwith method that returns the same type of collection as those passed to it?"), it seems to me that you want to have the same type of collection as those passed to zipwith
, not to the implicit conversion, that is the same type asys
.
same reasons as before, see solution below:
import collection._
import collection.generic._
implicit def collectionextras[a](xs: iterable[a]) = new {
def zipwith[b, c, that, repr](ys: iterablelike[b, repr])(f: (a, b) => c)(implicit cbf: canbuildfrom[repr, c, that]) = {
val builder = cbf(ys.repr)
val (i, j) = (xs.iterator, ys.iterator)
while(i.hasnext && j.hasnext) {
builder += f(i.next, j.next)
}
builder.result
}
}
with results:
scala> immutable.vector(2, 2, 2).zipwith(mutable.arraybuffer(4, 4, 4))(_ * _)
res1: scala.collection.mutable.arraybuffer[int] = arraybuffer(8, 8, 8)
score:9
you got close enough. just a minor change in two lines:
implicit def collectionextras[a, cc[a] <: iterablelike[a, cc[a]]](xs: cc[a]) = new {
def zipwith[b, c, that](ys: iterable[b])(f: (a, b) => c)(implicit cbf: canbuildfrom[cc[a], c, that]) = {
val builder = cbf(xs.repr)
val (i, j) = (xs.iterator, ys.iterator)
while(i.hasnext && j.hasnext) {
builder += f(i.next, j.next)
}
builder.result
}
}
first, you need to get the collection type being passed, so i added cc[a]
as a type parameter. also, that collection needs to be able to "reproduce" itself -- that is guaranteed by the second type parameter of iterablelike
-- so cc[a] <: iterablelike[a, cc[a]]
. note that this second parameter of iterablelike
is repr
, precisely the type of xs.repr
.
naturally, canbuildfrom
needs to receive cc[a]
instead of iterable[a]
. and that's all there is to it.
and the result:
scala> vector(2, 2, 2).zipwith(vector(4, 4, 4))(_ * _)
res0: scala.collection.immutable.vector[int] = vector(8, 8, 8)
Source: stackoverflow.com
Related Query
- How to write a zipWith method that returns the same type of collection as those passed to it?
- Scala: how to write method that returns object typed to implementation type of receiver
- How to write a function that returns something of the type `=> Int`?
- How can I check that method parameters have the same type as the methods this?
- how can I write a function in scala called Order that takes one arguement: a list of Ints. And returns the same List of Ints from least to greatest
- Can Scala method that returns a trait returns method with the same type as trait?
- scala: how to create a generic type which is subtype of all the number classes in scala so that it can include compare method
- How can I provide a compile-time guarantee that my method will return the same object it gets in Scala?
- How to resolve ambiguous implicit conversion method that takes same input type in Scala?
- How to make compiler check that 2 method arguments have the same type?
- How can I ensure that the dynamic type of my custom Scala collection is preserved during a map()?
- How can a method accept a (T <: Seq)[A] and return T[B] -- the same type of sequence with a different type parameter?
- Scala: How do I write the type of the function that sortBy takes?
- Is there any method that does the same thing as map() but generates a different type of container?
- Method returns NodeBuffer instead of Elem and that violates the type checking rule
- How do you declare a function in Scala that returns itself (or another function with the same signature)?
- How can I get an empty collection of the same type as a given instance?
- How define Structural Type that the method return this
- How do I create a function that takes any Seq[T,Int] and returns the original type of the Seq.
- How to return the original collection type from generic method
- How to make it so that dependent types in two different traits are recognized as the same type
- How do I write this method in Scala, or what is the best way to deal with early returns
- How to pass co-variant varargs through apply() to a function that takes co-variant varargs of the same type
- How to write a toCSV method for scala case class that would generate the csv string for the class?
- How can I make a scala method parameter type that is a collection of multiple types that can be converted to a given type?
- How does the Scala program compile, given that bindAndHandle method takes the first parameter of type Flow not Route?
- Scala: Typed method that returns object of the provided Type
- how to write a method correctly that returns a given array
- How are Scala collections able to return the correct collection type from a map operation?
- How to define a method in Scala that returns a Java object?
More Query from same tag
- Find out the type arguments for a type constructor knowing the type arguments of the type constructor it extends
- How to get inserted data after INSERT …. IF NOT EXISTS
- How to use play's form validation to check if the password matches
- Strange Scala 'Type mismatch' error for tuples
- How to fetch the value and type of each column of each row in a dataframe?
- How to get Histogram of all columns in a large CSV / RDD[Array[double]] using Apache Spark Scala?
- Spark task optimisation
- understanding constructors in Scala
- Akka; Passing around too many parameters with EventSourcedBehavior
- Spark Scala FoldLeft resulting in StackOverflow when run in the cluster
- Which of the many Spark/Scala kernels for Jupyter/IPython to choose?
- How to fix this compilation error with a Poly function
- Spark: Can I compress bytes before saving it to sequence files?
- Spark: How to create streaming Dataset with RowEncoder?
- Adding more pages and .scala classes in lift framework
- How programmatically limit download speed on server with Scala and Play
- Fetch all values irrespective of keys from a column of JSON type in a Spark dataframe using Spark with scala
- Manage multiple embedded javafx JFXPanel into swing tabbedpane
- Inconsistent count after window lead function, and filter
- How to pass implicit reference from constructor
- Retain materialization type when combining Sources
- How do I set Jackson parser features when using json4s?
- Difference between 1 :: 2 :: Nil vs. (1 :: (2 :: Nil))?
- Why type inference works when applying currying in Scala?
- findOneById is not a member of com.mongodb.casbah.MongoCollection
- SBT: sbt.InvalidComponent: Could not find required component: 'compiler-interface-src'
- Need help with Continuations-Error "found cps expression in non-cps position"
- bigquery Repeated record added outside of an array
- Spark-Scala-Intellij java.lang.IllegalStateException: After installing macOS Big Sur Update
- Finding objects using scala's runtime reflection