Skip to content

Instantly share code, notes, and snippets.

@milessabin
Created April 26, 2012 15:22
Show Gist options
  • Save milessabin/2500326 to your computer and use it in GitHub Desktop.
Save milessabin/2500326 to your computer and use it in GitHub Desktop.
Scala collection extension methods made easy (dependent types FTW!)
/*
* Copyright (c) 2012 Miles Sabin
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.chuusai.util
/**
* A possible addition to the Scala collections framework to address some of the issues
* raised in: http://yz.mit.edu/wp/true-scala-complexity/. Writers of extension methods
* like filterMap (see immediately below) don't have to be particularly concerned with
* the underlying mechanics which make this possible.
*
* Requires Scala 2.10.0 or Scala 2.9.x with -Ydependent-method-types .
*
*/
object TestEnrichTraversable extends App {
import EnrichTraversable._
import scala.collection.generic.CanBuildFrom
// External implementation of filterMap method. The line you would want to write is
// "def filterMap ..." the rest is fairly minimal boilerplate.
class FilterMapImpl[A, Repr](r : Repr)(implicit elem : Elem[A, Repr]) {
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)
// In use ...
val l = List(1, 2, 3, 4, 5)
val a = Array(1, 2, 3, 4, 5)
val s = "Hello World"
// Lists ...
val fml = l.filterMap(i => if(i % 2 == 0) Some(i) else None) // type is List[Int]
typed[List[Int]](fml)
assert(fml == List(2, 4))
// Arrays ...
val fma = a.filterMap(i => if(i % 2 == 0) Some(i) else None) // type is Array[Int]
typed[Array[Int]](fma)
assert(fma sameElements Array(2, 4))
// Strings: mapping to a String
val fms1 = s.filterMap(c => if(c >= 'A' && c <= 'Z') Some(c) else None) // type is String
typed[String](fms1)
assert(fms1 == "HW")
// Strings: mapping to a non-String
val fms2 = s.filterMap(c =>if(c % 2 == 0) Some(c.toInt) else None) // type is IndexedSeq[Int]
typed[IndexedSeq[Int]](fms2)
assert(fms2 == IndexedSeq(72, 108, 108, 32, 114, 108, 100))
}
// Reusable library infrastructure ...
object EnrichTraversable {
import scala.collection.GenTraversableLike
def typed[T](t : => T) {}
trait FromRepr[Repr] {
type A
val fromRepr : Repr => GenTraversableLike[A, Repr]
}
implicit def mkFromRepr[Repr](implicit fr : FromRepr[Repr]) = fr.fromRepr
object FromRepr {
implicit val stringFromRepr = new FromRepr[String] {
type A = Char
val fromRepr = implicitly[String => GenTraversableLike[Char, String]]
}
implicit def ccFromRepr[CC[_], A0](implicit fromRepr0 : CC[A0] => GenTraversableLike[A0, CC[A0]]) = new FromRepr[CC[A0]] {
type A = A0
val fromRepr = fromRepr0
}
}
type Elem[A, Repr] = Repr => GenTraversableLike[A, Repr]
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment