25

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]?

kassens
  • 4,405
  • 1
  • 26
  • 26
  • You mean something that returns a `PartialFunction[A,B]`, don't you? – Rex Kerr May 05 '11 at 18:31
  • possible duplicate of [How to convert X => Option\[R\] to PartialFunction\[X,R\]](http://stackoverflow.com/questions/1908295/how-to-convert-x-optionr-to-partialfunctionx-r) – Suma Apr 24 '15 at 12:46
  • `PartialFunction`'s `lift` is exactly what I was looking for. Thanks! :) – Zoltán Sep 30 '15 at 10:19

6 Answers6

40

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
}
psp
  • 12,138
  • 1
  • 41
  • 51
  • You didn't implement `getOrElse`. See [scala API documentation](http://www.scala-lang.org/api/current/scala/PartialFunction.html#applyOrElse[A1<:A,B1>:B](A1,(A1)⇒B1):B1) – Yang Bo Jul 09 '15 at 15:22
6

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.

James Iry
  • 19,367
  • 3
  • 64
  • 56
6

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).

Rex Kerr
  • 166,841
  • 26
  • 322
  • 407
  • Whew, this looks scary. It's guaranteed not be thread-safe. It may have its uses, but... take care. – jcsahnwaldt Reinstate Monica May 08 '12 at 10:36
  • @JonaChristopherSahnwaldt - Indeed. Very much not thread safe, as is most everything that uses caching. – Rex Kerr May 08 '12 at 14:05
  • I like this answer. I see two potential solutions to thread safety, please review: One is to wrap `tested`, `arg` and `ans` in a tuple or case class, then `DroppedFunction` will always have an internally consistent state. The other is avoid the problem altogether and ensure that in any multithreaded context the `DroppedFunction` is freshly created local to the scope. – samthebest May 22 '15 at 10:44
  • @samthebest - Using a case class provides consistency, but may not prevent multiple applications of the same function. Wrapping `isDefinedAt` and `apply` in `synchronized` blocks is slightly better, but it won't prevent the user from calling `isDefinedAt` in one thread, then in another on a different thread, then using `apply` on the first and getting the result from the second.. Better to just not use this in a multithreaded context since you can't ensure that people will use `applyOrElse` (which could be safe). – Rex Kerr May 22 '15 at 12:09
2

You can use Function.unlift, See docs.

Shani Elharrar
  • 667
  • 4
  • 5
1

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

Kevin Wright
  • 49,540
  • 9
  • 105
  • 155
0

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 }
}
Yang Bo
  • 3,586
  • 3
  • 22
  • 35
  • Interesting. Does this avoid the double execution seen in the current (29-2.11) library implementation? If it does, why the library implementation did not implement it this way? – Suma May 06 '16 at 06:51