Created
April 26, 2012 15:22
-
-
Save milessabin/2500326 to your computer and use it in GitHub Desktop.
Scala collection extension methods made easy (dependent types FTW!)
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
* 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