How do I apply the pimp-my-library pattern to Scala collections?

The key to understanding this problem is to realize that there are two different ways to build and work with collections in the collections library. One is the public collections interface with all its nice methods. The other, which is used extensively in creating the collections library, but which are almost never used outside of it, is the builders.

The key to understanding this problem is to realize that there are two different ways to build and work with collections in the collections library. One is the public collections interface with all its nice methods. The other, which is used extensively in creating the collections library, but which are almost never used outside of it, is the builders.

Our problem in pimping is exactly the same one that the collections library itself faces when trying to return collections of the same type. That is, we want to build collections, but when working generically, we don't have a way to refer to "the same type that the collection already is". So we need builders.

Now the question is: where do we get our builders from? The obvious place is from the collection itself. This doesn't work.

We already decided, in moving to a generic collection, that we were going to forget the type of the collection. So even though the collection could return a builder that would generate more collections of the type we want, it wouldn't know what the type was. Instead, we get our builders from CanBuildFrom implicits that are floating around.

These exist specifically for the purpose of matching input and output types and giving you an appropriately typed builder. So, we have two conceptual leaps to make: We aren't using standard collections operations, we're using builders. We get these builders from implicit CanBuildFroms, not from our collection directly.

Let's look at an example. Class GroupingCollectionA, CA Boolean)( implicit cbfcc: CanBuildFromCA,CA,CCA, cbfc: CanBuildFromCA,A,CA ): CCA = { val it = ca.iterator val cca = cbfcc() if (!it. HasNext) cca.result else { val as = cbfc() var olda = it.

Next as += olda while (it. HasNext) { val a = it. Next if (p(olda,a)) as += a else { cca += as.

Result; as. Clear; as += a } olda = a } cca += as. Result } cca.result } } implicit def iterable_has_groupingA, CA List(1,2,2,2,3,4,4,4,5,5,1,1,1,2).

GroupedWhile(_ == _) res0: ListListInt = List(List(1), List(2, 2, 2), List(3), List(4, 4, 4), List(5, 5), List(1, 1, 1), List(2)) scala> Vector(1,2,3,4,1,2,3,1,2,1). GroupedWhile(_ IterableA, cbf: CanBuildFromC,C,DC, cbfi: CanBuildFromC,A,C ) { def groupedWhile(p: (A,A) => Boolean): DC = { val it = c2i(ca). Iterator val cca = cbf() if (!it.

HasNext) cca.result else { val as = cbfi() var olda = it. Next as += olda while (it. HasNext) { val a = it.

Next if (p(olda,a)) as += a else { cca += as. Result; as. Clear; as += a } olda = a } cca += as.

Result } cca.result } } Here we've added an implicit that gives us an IterableA from C--for most collections this will just be the identity (e.g. ListA already is an IterableA), but for arrays it will be a real implicit conversion. And, consequently, we've dropped the requirement that CA IterableA, cbf: CanBuildFromCA,CA,CCA, cbfi: CanBuildFromCA,A,CA ) = { new GroupingCollectionA,CA,C(ca)(c2i, cbf, cbfi) } where now we plug in CA for C and CCA for DC. Note that we do need the explicit generic types on the call to new GroupingCollection so it can keep straight which types correspond to what.

Thanks to the implicit c2i: CA => IterableA, this automatically handles arrays. But wait, what if we want to use strings? Now we're in trouble, because you can't have a "string of strings".

This is where the extra abstraction helps: we can call D something that's suitable to hold strings. Let's pick Vector, and do the following: val vector_string_builder = ( new CanBuildFromString, String, VectorString { def apply() = Vector. NewBuilderString def apply(from: String) = this.apply() } ) implicit def strings_have_grouping(s: String)( implicit c2i: String => IterableChar, cbfi: CanBuildFromString,Char,String ) = { new GroupingCollectionChar,String,Vector(s)( c2i, vector_string_builder, cbfi ) } We need a new CanBuildFrom to handle the building of a vector of strings (but this is really easy, since we just need to call Vector.

NewBuilderString), and then we need to fill in all the types so that the GroupingCollection is typed sensibly. Note that we already have floating around a String,Char,String CanBuildFrom, so strings can be made from collections of chars. Let's try it out: scala> List(true,false,true,true,true).

GroupedWhile(_ == _) res1: ListListBoolean = List(List(true), List(false), List(true, true, true)) scala> Array(1,2,5,3,5,6,7,4,1). GroupedWhile(_ "". GroupedWhile(_.

IsLetter == _. IsLetter) res3: VectorString = Vector( , there,! ).

– Peter Schmitz Mar 25 '11 at 8:10 @Peter - Quite possibly! I tend to write explicit implicit conversions rather than relying upon – Rex Kerr Mar 25 '11 at 14:33 Based on @Peters comment I tried to add another implicit conversion for arrays, but I failed. I didn't really understand where to add the view bounds.

@Rex, can you please edit your answer and show how to get the code to work with arrays? – Antoras Apr 12 '11 at 17:39.

I cant really gove you an answer,but what I can give you is a way to a solution, that is you have to find the anglde that you relate to or peaks your interest. A good paper is one that people get drawn into because it reaches them ln some way.As for me WW11 to me, I think of the holocaust and the effect it had on the survivors, their families and those who stood by and did nothing until it was too late.

Related Questions