24

As long as we have a PartialFunction[X,R] it's very easy to convert it to a function returning Option[R], e.g.

def pfToOptf[X, R](f: PartialFunction[X,R])(x: X) =
    if (f.isDefinedAt(x)) Some(f(x))
    else None

However, what if the task is opposite: suppose I have a function f getting X as an argument and returning Option[R] as a result. And I want to make a PartialFunction[X,R] out of it. What is the best way?

What I've come up with looks pretty ugly to my taste:

def optfToPf[X,R](f: X => Option[R]) : PartialFunction[X,R] = {
    object extractor {
        def unapply(x: X): Option[R] = f(x)
    }

    { case extractor(r) => r }
}

Is there some better way I missed?

giampaolo
  • 6,906
  • 5
  • 45
  • 73
Alexander Azarov
  • 12,971
  • 2
  • 50
  • 54
  • possible duplicate of [Inverse of PartialFunction's lift method](http://stackoverflow.com/questions/5902266/inverse-of-partialfunctions-lift-method) – Todd Owen Dec 28 '14 at 07:18
  • @ToddOwen I'm just curious what "before" means to you in the sentence "this question has been asked before". Please simply compare the dates of both questions. – Alexander Azarov Dec 29 '14 at 18:29
  • Close vote retracted. Sorry, it was not a matter of "before", but rather the fact that the other question seemed to have received a better answer (with 28 votes). But now I notice that Thayne gave the same answer here. – Todd Owen Dec 30 '14 at 14:32
  • For completeness, the standard way to transform a _pf_ `PartialFunction[X, R]`, into a `X => Option[R]` (i.e. `Function[X, Option[R]]`) is simply: `pf.lift` -- See: https://www.scala-lang.org/api/2.11.x/#scala.PartialFunction – juanmirocks Nov 22 '18 at 09:40

3 Answers3

35

Starting Scala 2.9, Function.unlift does precisely this:

def unlift[T, R](f: (T) => Option[R]): PartialFunction[T, R]

Turns a function T => Option[R] into a PartialFunction[T, R].

Xavier Guihot
  • 54,987
  • 21
  • 291
  • 190
Thayne
  • 6,619
  • 2
  • 42
  • 67
12

How about this:

Welcome to Scala version 2.8.0.r19650-b20091114020153 (Java HotSpot(TM) Client VM, Java 1.6.0_17).
Type in expressions to have them evaluated.
Type :help for more information.

scala> def optfToPf[X,R](f: X => Option[R]): PartialFunction[X,R] = x => f(x) match {
     |     case Some(r) => r
     | }
optfToPf: [X,R](f: (X) => Option[R])PartialFunction[X,R]

scala>
Eastsun
  • 18,526
  • 6
  • 57
  • 81
5

I suppose you could override apply and isDefinedAt by hand, but I'd do it the way you find ugly.

def optfToPf[X,R](f: X => Option[R]) = new PartialFunction[X,R] {
  def apply(x: X): R = f(x).get
  def isDefinedAt(x: X): Boolean = f(x) != None
}

Testing:

scala> val map = Map(1 -> 2)
map: scala.collection.immutable.Map[Int,Int] = Map(1 -> 2)

scala> map(1)
res0: Int = 2

scala> def mapOpt(key: Int) = map.get(key)
mapOpt: (key: Int)Option[Int]

scala> mapOpt(1)
res1: Option[Int] = Some(2)

scala> mapOpt(2)
res2: Option[Int] = None

scala> val mapPf = optfToPf(mapOpt _)
mapPf: java.lang.Object with PartialFunction[Int,Int] = <function1>

scala> mapPf.isDefinedAt(2)
res3: Boolean = false

scala> mapPf.isDefinedAt(1)
res4: Boolean = true

scala> mapPf(1)
res5: Int = 2

scala> mapPf(2)
java.util.NoSuchElementException: None.get
Daniel C. Sobral
  • 295,120
  • 86
  • 501
  • 681
  • 1
    Then again, I think I prefer overriding `apply` and `isDefinedAt` after all. – Daniel C. Sobral Dec 15 '09 at 16:53
  • Thank you! Though now I am under impression the need to write this "conversion" code signals me about some problem, so I am trying to think over more thoroughly. The least problem I see `f(x)` will be called twice (in case of `Some`) in any implementation. – Alexander Azarov Dec 15 '09 at 18:50
  • Yes, it get called twice. You could cache the result, but that would be awkward, and not really what happens in a partial function. – Daniel C. Sobral Dec 16 '09 at 11:25