This proposal has been implemented in Scala 2.13.0 and Scala 3.0.0.
By: Yang Bo
History
Date | Version |
---|---|
Aug 20th 2018 | Initial Draft |
Motivation
There are three types in Scala to represent a function that accept some of the parameters:
- optional functions:
A => Option[B]
- extracter objects:
{ def unapply(a: A): Option[B] }
and{ def unapplySeq(a: A): Option[Seq[B]] }
- partial fucntions:
PartialFunction[A, B]
Optional functions and partial functions can be converted to each other via PartialFunction.lift
and Function.unlift
. However, there is no simple approach to convert a partial function to an extractor object. As a result, partial functions are not composable. You cannot create a partial function then use it as a pattern in another partial function.
This proposal makes PartialFunction
be an extractor, and provides an unlift
method to convert optional functions to PartialFunction
s.
Motivating Examples
// Define a PartialFunction
val pf: PartialFunction[Int, String] = {
case 1 => "matched by a PartialFunction"
}
// Define an optional function
val of: Int => Option[String] = { i =>
if (i == 2) {
Some("matched by an optional function")
} else {
None
}
}
util.Random.nextInt(4) match {
case pf(m) => // A PartialFunction itself is a pattern
println(m)
case of.unlift(m) => // Convert an optional function to a pattern
println(m)
case _ =>
println("Not matched")
}
In addition, elementWise
can be used to create an object with a unapplySeq
method, which extracts each element of a sequence data.
val firstChar: String => Option[Char] = _.headOption
Seq("foo", "bar", "baz") match {
case firstChar.unlift.elementWise(c0, c1, c2) =>
println(s"$c0, $c1, $c2") // Output: f, b, b
}
Cheat sheet
This proposal allows converting among optional Functions, PartialFunctions and extractor objects as shown in the following table.
How to convert ... | to a partial function | to an optional function | to an extractor |
---|---|---|---|
from a partial function |
partialFunction
|
partialFunction.lift
|
partialFunction
|
from an optional function |
optionalFunction.unlift or Function.unlift(optionalFunction)
|
optionalFunction
|
optionalFunction.unlift
|
from an extractor |
{ case extractor(x) => x }
|
extractor.unapply _
|
extractor
|
Note that optionalFunction.unlift
is preferred to Function.unlift(optionalFunction)
when creating extractors, because only nullary methods are allowed in case
expressions.
Implementation
The idea was originally implemented in a library: Extractor.scala, which has been used in Binding.scala and sbt-api-mappings.
The new implementation aims to become part of core library. The pull request can be found at #7111.