PartialFunction
's lift
method turns a PartialFunction into a Function
returning an Option
result.
Is there an inverse operation to this, that turns a Function1[A, Option[B]]
into a PartialFunction[A, B]
?
PartialFunction
's lift
method turns a PartialFunction into a Function
returning an Option
result.
Is there an inverse operation to this, that turns a Function1[A, Option[B]]
into a PartialFunction[A, B]
?
It's hard to top all these fine answers from a range of scala luminaries, but in case you would like to know about the one in the standard library, it's in the scala.Function companion object. (In 2.9.)
/** Turns a function `A => Option[B]` into a `PartialFunction[A, B]`. Important note:
* this transformation implies the original function will be called 2 or more
* times on each logical invocation, because the only way to supply an implementation
* of isDefinedAt is to call the function and examine the return value.
*
* @param f a function T => Option[R]
* @return a partial function defined for those inputs where
* f returns Some(_) and undefined where f returns None.
* @see PartialFunction#lift
*/
def unlift[T, R](f: T => Option[R]): PartialFunction[T, R] = new PartialFunction[T, R] {
def apply(x: T): R = f(x).get
def isDefinedAt(x: T): Boolean = f(x).isDefined
override def lift: T => Option[R] = f
}
Not in the library, but it's easy to build. However, isDefinedAt will have to fully evaluate the function making it more expensive than is typical for partial functions built from pattern matching and also possibly result in unwanted side effects.
scala> def unlift[A, B](f : (A => Option[B])) = new PartialFunction[A,B] {
| def isDefinedAt(x : A) = f(x).isDefined
| def apply(x : A) = f(x).get
| }
unlift: [A,B](f: (A) => Option[B])java.lang.Object with PartialFunction[A,B]
scala> def f(x : Int) = if (x == 1) Some(1) else None
f: (x: Int)Option[Int]
scala> val g = unlift(f)
g: java.lang.Object with PartialFunction[Int,Int] = <function1>
scala> g.isDefinedAt(1)
res0: Boolean = true
scala> g.isDefinedAt(2)
res1: Boolean = false
scala> g(1)
res2: Int = 1
scala> g(2)
java.util.NoSuchElementException: None.get
at scala.None$.get(Option.scala:262)
at scala.None$.get(Option.scala:260)
at $anon$1.apply(<console>:7)
at scala.Function1$class.apply$mcII$sp(Function1.scala:39)
at $anon$1.apply$mcII$sp(<console>:5)
at .<init>(<console>:9)
at .<clinit>(<console>)
at RequestResult$.<init>(<console>:9)
at RequestResult$.<clinit>(<console>)
at RequestResult$scala_repl_result(<console>)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
at java.lang.reflect.Method.invoke(Unknown Source)
at scala.tools.nsc.Interpreter$Request$$anonfun$loadAndRun$1$$anonfun$apply$17.apply(Interpreter.scala:988)
at scala.tools....
A purist might also wrap isDefinedAt with a try/catch block to return false on exceptions.
To build on James' answer with a more complex example, I have the following code in my library of things-the-Scala-library-forgot (or didn't-trust-you-with):
class DroppedFunction[-A,+B](f: A => Option[B]) extends PartialFunction[A,B] {
private[this] var tested = false
private[this] var arg: A = _
private[this] var ans: Option[B] = None
private[this] def cache(a: A) {
if (!tested || a != arg) {
tested = true
arg = a
ans = f(a)
}
}
def isDefinedAt(a: A) = {
cache(a)
ans.isDefined
}
def apply(a: A) = {
cache(a)
ans.get
}
}
class DroppableFunction[A,B](f: A => Option[B]) {
def drop = new DroppedFunction(f)
}
implicit def function_is_droppable[A,B](f: A => Option[B]) = new DroppableFunction(f)
Most of the code is devoted to making sure the function evaluation is cached (as long as the apply comes right after the isDefinedAt). Example of use:
scala> val f = (x: Int) => if (x>=0) Some(x) else None
f: (Int) => Option[Int] = <function1>
scala> Array(-2,-1,0,1,2).collect(f.drop)
res0: Array[Int] = Array(0, 1, 2)
The caching helps speed things up and avoid double-side-effect problems (at least when isDefinedAt
is used immediately before apply
, and when the function omits side effects when it returns None
).
To build on James's answer...
It's also possible to cache the result generated in isDefinedAt
and then return this on the call to apply
, thus avoiding a double execution.
However, Scala doesn't enforce pure functions and so it's easy to find real life examples where either unlifting strategy will produce surprising and unexpected results. Because of this, it's generally ill-advised to "unlift" something into a PartialFunction
We should always use a partial function literal to build a PartialFunction
because it's too trivial to correctly implement an applyOrElse
for a PartialFunction
.
Thus, the correct unlift
should be implemented as such:
// Use AnyVal to avoid the indirect reference to f
class Extractor[A, B](val f: A => Option[B]) extends AnyVal {
def unapply(a: A) = f(a)
}
def unlift[A, B](f: A => Option[B]): PartialFunction[A, B] = {
val LocalExtractor = new Extractor(f)
// Create the PartialFunction from a partial function literal
{ case LocalExtractor(b) => b }
}