Skip to content

Commit

Permalink
Added infrastructure to enable easy enrichment of GenTraversables.
Browse files Browse the repository at this point in the history
  • Loading branch information
milessabin committed May 17, 2012
1 parent ce896d6 commit 73f7001
Show file tree
Hide file tree
Showing 5 changed files with 105 additions and 0 deletions.
9 changes: 9 additions & 0 deletions src/library/scala/collection/GenTraversableLike.scala
Expand Up @@ -411,3 +411,12 @@ trait GenTraversableLike[+A, +Repr] extends Any with GenTraversableOnce[A] with
def stringPrefix: String

}

object GenTraversableLike {
/** Manufacture a conversion from collection representation type `Repr` to
* its corresponding `GenTraversableLike` given an implicitly available
* instance of `FromRepr[Repr]`.
* @see [[scala.collection.generic.FromRepr]]
*/
implicit def fromRepr[Repr](implicit fr : FromRepr[Repr]) = fr.hasElem
}
56 changes: 56 additions & 0 deletions src/library/scala/collection/generic/FromRepr.scala
@@ -0,0 +1,56 @@
/* __ *\
** ________ ___ / / ___ Scala API **
** / __/ __// _ | / / / _ | (c) 2003-2012, LAMP/EPFL **
** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ **
** /____/\___/_/ |_/____/_/ | | **
** |/ **
\* */

package scala.collection
package generic

/** Type class witnessing that a collection representation type `Repr` has
* elements of type `A` and has a conversion to `GenTraversableLike[A, Repr]`.
*
* This type enables simple enrichment of `GenTraversable`s with extension
* methods which can make full use of the mechanics of the Scala collections
* framework in their implementation.
*
* Example usage,
* {{{
* import scala.collection.generic.{ CanBuildFrom, FromRepr, HasElem }
*
* class FilterMapImpl[A, Repr](val r : Repr)(implicit hasElem : HasElem[Repr, A]) {
* def filterMap[B, That](f : A => Option[B])
* (implicit cbf : CanBuildFrom[Repr, B, That]) : That = r.flatMap(f(_).toSeq)
* }
*
* implicit def filterMap[Repr : FromRepr](r : Repr) = new FilterMapImpl(r)
*
* val l = List(1, 2, 3, 4, 5)
* List(1, 2, 3, 4, 5) filterMap (i => if(i % 2 == 0) Some(i) else None)
* // == List(2, 4)
* }}}
*
* @author Miles Sabin
* @since 2.10
*/
trait FromRepr[Repr] {
type A
val hasElem: HasElem[Repr, A]
}

object FromRepr {
import language.higherKinds

implicit val stringFromRepr : FromRepr[String] { type A = Char } = new FromRepr[String] {
type A = Char
val hasElem = implicitly[HasElem[String, Char]]
}

implicit def genTraversableLikeFromRepr[C[_], A0]
(implicit hasElem0: HasElem[C[A0], A0]) : FromRepr[C[A0]] { type A = A0 } = new FromRepr[C[A0]] {
type A = A0
val hasElem = hasElem0
}
}
6 changes: 6 additions & 0 deletions src/library/scala/collection/generic/package.scala
Expand Up @@ -4,6 +4,12 @@ import generic.CanBuildFrom
package object generic {
type CanBuild[-Elem, +To] = CanBuildFrom[Nothing, Elem, To]

/** The type of conversions from a collection representation type
* `Repr` to its corresponding GenTraversableLike.
* @see [[scala.collection.generic.FromRepr]]
*/
type HasElem[Repr, A] = Repr => GenTraversableLike[A, Repr]

@deprecated("use ArrayTagTraversableFactory instead", "2.10.0")
type ClassManifestTraversableFactory[CC[X] <: Traversable[X] with GenericClassManifestTraversableTemplate[X, CC]] = ArrayTagTraversableFactory[CC]

Expand Down
4 changes: 4 additions & 0 deletions test/files/run/enrich-gentraversable.check
@@ -0,0 +1,4 @@
List(2, 4)
Array(2, 4)
HW
Vector(72, 108, 108, 32, 114, 108, 100)
30 changes: 30 additions & 0 deletions test/files/run/enrich-gentraversable.scala
@@ -0,0 +1,30 @@
object Test extends App {
import scala.collection.generic.{ CanBuildFrom, FromRepr, HasElem }

def typed[T](t : => T) {}

class FilterMapImpl[A, Repr](val r : Repr)(implicit hasElem : HasElem[Repr, A]) {
def filterMap[B, That](f : A => Option[B])(implicit cbf : CanBuildFrom[Repr, B, That]) : That = r.flatMap(f(_).toSeq)
}

implicit def filterMap[Repr : FromRepr](r : Repr) = new FilterMapImpl(r)

val l = List(1, 2, 3, 4, 5)
val fml = l.filterMap(i => if(i % 2 == 0) Some(i) else None)
typed[List[Int]](fml)
println(fml)

val a = Array(1, 2, 3, 4, 5)
val fma = a.filterMap(i => if(i % 2 == 0) Some(i) else None)
typed[Array[Int]](fma)
println(fma.deep)

val s = "Hello World"
val fms1 = s.filterMap(c => if(c >= 'A' && c <= 'Z') Some(c) else None)
typed[String](fms1)
println(fms1)

val fms2 = s.filterMap(c =>if(c % 2 == 0) Some(c.toInt) else None)
typed[IndexedSeq[Int]](fms2)
println(fms2)
}

0 comments on commit 73f7001

Please sign in to comment.