A partial function of type PartialFunction[A, B]
is a unary function where the domain does not necessarily include all values of type A
. The function isDefinedAt allows to test dynamically if a value is in the domain of the function.
Even if isDefinedAt
returns true for an a: A
, calling apply(a)
may still throw an exception, so the following code is legal:
val f: PartialFunction[Int, Any] = { case x => x / 0 } // ArithmeticException: / by zero
It is the responsibility of the caller to call isDefinedAt
before calling apply
, because if isDefinedAt
is false, it is not guaranteed apply
will throw an exception to indicate an error condition. If an exception is not thrown, evaluation may result in an arbitrary value.
The usual way to respect this contract is to call applyOrElse, which is expected to be more efficient than calling both isDefinedAt
and apply
.
Note that isDefinedAt
may itself throw an exception while evaluating pattern guards or other parts of the PartialFunction
. The same caveat holds for applyOrElse
.
val sample = 1 to 10
def isEven(n: Int) = n % 2 == 0
val eveningNews: PartialFunction[Int, String] = {
case x if isEven(x) => s"$x is even"
}
// The method "collect" is described as "filter + map"
// because it uses a PartialFunction to select elements
// to which the function is applied.
val evenNumbers = sample.collect(eveningNews)
// It's more usual to write the PartialFunction as a block of case clauses
// called an "anonymous pattern-matching function". Since the collect method
// expects a PartialFunction, one is synthesized from the case clauses.
def evenly = sample.collect { case x if isEven(x) => s"$x is even" }
// A method that takes a Function will get one, using the same syntax.
// Note that all cases are supplied since Function has no `isDefinedAt`.
def evened = sample.map { case odd if !isEven(odd) => odd + 1 case even => even }
The main distinction between PartialFunction
and scala.Function1 is that the client of a PartialFunction
can perform an alternative computation with input that is reported to be outside the domain of the function.
For example:
val oddlyEnough: PartialFunction[Int, String] = {
case x if !isEven(x) => s"$x is odd"
}
// The method orElse allows chaining another PartialFunction
// to handle input outside the declared domain.
val numbers = sample.map(eveningNews.orElse(oddlyEnough))
// The same computation but with a function literal that calls applyOrElse
// with oddlyEnough as fallback, which it can do because a PartialFunction is a Function.
val numbers = sample.map(n => eveningNews.applyOrElse(n, oddlyEnough))
As a convenience, function literals can also be adapted into partial functions when needed. If the body of the function is a match expression, then the cases are used to synthesize the PartialFunction as already shown.
// The partial function isDefinedAt inputs resulting in the Success case.
val inputs = List("1", "two", "3").collect(x => Try(x.toInt) match { case Success(i) => i })
Attributes
- Note
-
Optional Functions, PartialFunctions and extractor objects can be converted to each other as shown in the following table.
How to convert ...
to a PartialFunction
to an optional Function
to an extractor
from a PartialFunction
from optional Function
from an extractor
{ case extractor(x) => x }
extractor.unapply(_)
- Companion
- object
- Source
- PartialFunction.scala
- Graph
-
- Supertypes
- Known subtypes
-
class IntMap[T]class LongMap[T]class LongMap[V]class SystemPropertiestrait Seq[A]trait Seq[A]class AbstractSeq[A]class ArraySeq[A]class ofBooleanclass ofByteclass ofCharclass ofDoubleclass ofFloatclass ofIntclass ofLongclass ofRef[T]class ofShortclass ofUnitclass LazyList[A]class List[A]class ::[A]object Nilclass NumericRange[T]class Exclusive[T]class Inclusive[T]class Queue[A]class Rangeclass Exclusiveclass Inclusiveclass Stream[A]class Cons[A]object Emptyclass Vector[A]class WrappedStringtrait IndexedSeq[A]trait LinearSeq[A]trait Seq[A]class AbstractSeq[A]class AbstractBuffer[A]class ArrayBuffer[A]class ArrayDeque[A]class Queue[A]class Stack[A]class ListBuffer[A]class UnrolledBuffer[T]class ArraySeq[T]class ofBooleanclass ofByteclass ofCharclass ofDoubleclass ofFloatclass ofIntclass ofLongclass ofRef[T]class ofShortclass ofUnitclass StringBuildertrait Buffer[A]trait IndexedBuffer[A]trait IndexedSeq[T]class AnyAccumulator[A]class DoubleAccumulatorclass IntAccumulatorclass LongAccumulatorclass AbstractSeq[A]trait IndexedSeq[A]trait LinearSeq[A]
- Self type
-