summaryrefslogtreecommitdiff
path: root/src/library/scala/PartialFunction.scala
diff options
context:
space:
mode:
authorPavel Pavlov <pavel.e.pavlov@gmail.com>2012-08-21 01:40:14 +0700
committerPavel Pavlov <pavel.e.pavlov@gmail.com>2012-08-23 15:37:28 +0700
commit42c6fb8ce814ed109f6418bcc5809d23e1db1965 (patch)
tree41dae80febf880c1c85fc4c5154de1984ea12dbe /src/library/scala/PartialFunction.scala
parent1a1678dac0d4ff7d08efffbe2c80f324c173a69e (diff)
downloadscala-42c6fb8ce814ed109f6418bcc5809d23e1db1965.tar.gz
scala-42c6fb8ce814ed109f6418bcc5809d23e1db1965.tar.bz2
scala-42c6fb8ce814ed109f6418bcc5809d23e1db1965.zip
PartialFunction polishing
- ScalaDocs added - TODOs fixed - controversive method `run` deleted - not used class runtime.AbstractTotalFunction removed - small corrections & fixes - tests for `orElse` & `runWith`
Diffstat (limited to 'src/library/scala/PartialFunction.scala')
-rw-r--r--src/library/scala/PartialFunction.scala114
1 files changed, 76 insertions, 38 deletions
diff --git a/src/library/scala/PartialFunction.scala b/src/library/scala/PartialFunction.scala
index 7154b8da34..91eae2984a 100644
--- a/src/library/scala/PartialFunction.scala
+++ b/src/library/scala/PartialFunction.scala
@@ -67,7 +67,7 @@ trait PartialFunction[-A, +B] extends (A => B) { self =>
* of this partial function and `that`. The resulting partial function
* takes `x` to `this(x)` where `this` is defined, and to `that(x)` where it is not.
*/
- def orElse[A1 <: A, B1 >: B](that: PartialFunction[A1, B1]) : PartialFunction[A1, B1] =
+ def orElse[A1 <: A, B1 >: B](that: PartialFunction[A1, B1]): PartialFunction[A1, B1] =
new OrElse[A1, B1] (this, that)
//TODO: why not overload it with orElse(that: F1): F1?
@@ -78,10 +78,8 @@ trait PartialFunction[-A, +B] extends (A => B) { self =>
* @return a partial function with the same domain as this partial function, which maps
* arguments `x` to `k(this(x))`.
*/
- override def andThen[C](k: B => C) : PartialFunction[A, C] = new PartialFunction[A, C] {
- def isDefinedAt(x: A): Boolean = self isDefinedAt x
- def apply(x: A): C = k(self(x))
- }
+ override def andThen[C](k: B => C): PartialFunction[A, C] =
+ new AndThen[A, B, C] (this, k)
/** Turns this partial function into an plain function returning an `Option` result.
* @see Function.unlift
@@ -90,28 +88,54 @@ trait PartialFunction[-A, +B] extends (A => B) { self =>
*/
def lift: A => Option[B] = new Lifted(this)
- /**
- * TODO: comment
+ /** Applies this partial function to the given argument when it is contained in the function domain.
+ * Applies fallback function where this partial function is not defined.
+ *
+ * Note that expression `pf.applyOrElse(x, default)` is equivalent to
+ * {{{ if(pf isDefinedAt x) pf(x) else default(x) }}}
+ * except that `applyOrElse` method can be implemented more efficiently.
+ * For all partial function literals compiler generates `applyOrElse` implementation which
+ * avoids double evaluation of pattern matchers and guards.
+ * This makes `applyOrElse` the basis for the efficient implementation for many operations and scenarios, such as:
+ *
+ * - combining partial functions into `orElse`/`andThen` chains does not lead to
+ * excessive `apply`/`isDefinedAt` evaluation
+ * - `lift` and `unlift` do not evaluate source functions twice on each invocation
+ * - `runWith` allows efficient imperative-style combining of partial functions
+ * with conditionally applied actions
+ *
+ * For non-literal partial function classes with nontrivial `isDefinedAt` method
+ * it is recommended to override `applyOrElse` with custom implementation that avoids
+ * double `isDefinedAt` evaluation. This may result in better performance
+ * and more predictable behavior w.r.t. side effects.
+ *
+ * @param x the function argument
+ * @param default the fallback function
+ * @return the result of this function or fallback function application.
* @since 2.10
*/
def applyOrElse[A1 <: A, B1 >: B](x: A1, default: A1 => B1): B1 =
if (isDefinedAt(x)) apply(x) else default(x)
- /**
- * TODO: comment
- * @since 2.10
- */
- def run[U](x: A)(action: B => U): Boolean =
- applyOrElse(x, fallbackToken) match {
- case FallbackToken => false
- case z => action(z); true
- }
-
- /**
- * TODO: comment
+ /** Composes this partial function with an action function which
+ * gets applied to results of this partial function.
+ * The action function is invoked only for its side effects; its result is ignored.
+ *
+ * Note that expression `pf.runWith(action)(x)` is equivalent to
+ * {{{ if(pf isDefinedAt x) { action(pf(x)); true } else false }}}
+ * except that `runWith` is implemented via `applyOrElse` and thus potentially more efficient.
+ * Using `runWith` avoids double evaluation of pattern matchers and guards for partial function literals.
+ * @see `applyOrElse`.
+ *
+ * @param action the action function
+ * @return a function which maps arguments `x` to `isDefinedAt(x)`. The resulting function
+ * runs `action(this(x))` where `this` is defined.
* @since 2.10
*/
- def runWith[U](action: B => U): A => Boolean = { x => run(x)(action) }
+ def runWith[U](action: B => U): A => Boolean = { x =>
+ val z = applyOrElse(x, checkFallback[B])
+ if (!fallbackOccurred(z)) { action(z); true } else false
+ }
}
/** A few handy operations which leverage the extra bit of information
@@ -137,11 +161,10 @@ object PartialFunction {
def apply(x: A): B = f1.applyOrElse(x, f2)
- override def applyOrElse[A1 <: A, B1 >: B](x: A1, default: A1 => B1): B1 =
- f1.applyOrElse(x, fallbackToken) match {
- case FallbackToken => f2.applyOrElse(x, default)
- case z => z
- }
+ override def applyOrElse[A1 <: A, B1 >: B](x: A1, default: A1 => B1): B1 = {
+ val z = f1.applyOrElse(x, checkFallback[B])
+ if (!fallbackOccurred(z)) z else f2.applyOrElse(x, default)
+ }
override def orElse[A1 <: A, B1 >: B](that: PartialFunction[A1, B1]) =
new OrElse[A1, B1] (f1, f2 orElse that)
@@ -150,23 +173,40 @@ object PartialFunction {
new OrElse[A, C] (f1 andThen k, f2 andThen k)
}
- private[scala] lazy val FallbackToken: PartialFunction[Any, PartialFunction[Any, Nothing]] = { case _ => FallbackToken.asInstanceOf[PartialFunction[Any, Nothing]] }
- private[scala] final def fallbackToken[B] = FallbackToken.asInstanceOf[PartialFunction[Any, B]]
- //TODO: check generated code for PF literal here
+ /** Composite function produced by `PartialFunction#andThen` method
+ */
+ private final class AndThen[-A, B, +C] (pf: PartialFunction[A, B], k: B => C) extends PartialFunction[A, C] {
+ def isDefinedAt(x: A) = pf.isDefinedAt(x)
+
+ def apply(x: A): C = k(pf(x))
- private[scala] final class Lifted[-A, +B] (val pf: PartialFunction[A, B])
+ override def applyOrElse[A1 <: A, C1 >: C](x: A1, default: A1 => C1): C1 = {
+ val z = pf.applyOrElse(x, checkFallback[B])
+ if (!fallbackOccurred(z)) k(z) else default(x)
+ }
+ }
+
+ private[this] final val fallback_pf: PartialFunction[Any, Any] = { case _ => fallback_pf }
+ @inline private final def checkFallback[B] = fallback_pf.asInstanceOf[PartialFunction[Any, B]]
+ @inline private final def fallbackOccurred[B](x: B) = (fallback_pf eq x.asInstanceOf[AnyRef])
+
+ private final class Lifted[-A, +B] (val pf: PartialFunction[A, B])
extends runtime.AbstractFunction1[A, Option[B]] {
- def apply(x: A): Option[B] = pf.applyOrElse(x, fallbackToken) match {
- case FallbackToken => None
- case z => Some(z)
+ def apply(x: A): Option[B] = {
+ val z = pf.applyOrElse(x, checkFallback[B])
+ if (!fallbackOccurred(z)) Some(z) else None
}
}
private final class Unlifted[A, B] (f: A => Option[B]) extends runtime.AbstractPartialFunction[A, B] {
def isDefinedAt(x: A): Boolean = f(x).isDefined
- override def applyOrElse[A1 <: A, B1 >: B](x: A1, default: A1 => B1): B1 =
- f(x) getOrElse default(x) //TODO: check generated code and inline getOrElse if needed
+
+ override def applyOrElse[A1 <: A, B1 >: B](x: A1, default: A1 => B1): B1 = {
+ val z = f(x)
+ if (!z.isEmpty) z.get else default(x)
+ }
+
override def lift = f
}
@@ -178,7 +218,6 @@ object PartialFunction {
/** Converts ordinary function to partial one
* @since 2.10
*/
- //TODO: check generated code for PF literal here
def apply[A, B](f: A => B): PartialFunction[A, B] = { case x => f(x) }
private[this] final val constFalse: Any => Boolean = { _ => false}
@@ -189,12 +228,11 @@ object PartialFunction {
override def orElse[A1, B1](that: PartialFunction[A1, B1]) = that
override def andThen[C](k: Nothing => C) = this
override val lift = (x: Any) => None
- override def run[U](x: Any)(action: Nothing => U) = false
override def runWith[U](action: Nothing => U) = constFalse
}
- /**
- * TODO: comment
+ /** The partial function with empty domain.
+ * Any attempt to invoke empty partial function leads to throwing [[scala.MatchError]] exception.
* @since 2.10
*/
def empty[A, B] : PartialFunction[A, B] = empty_pf