6

I occasionally come across the following pattern, where I essentially have a PartialFunction[SomeType,AnotherType], and want to treat it as a Function[SomeType,Option[AnotherType], eg:

def f(s:SomeType):Option[AnotherType] = s match {
  case s1:SubType1 => Some(AnotherType(s1.whatever))
  case s2:SubType2 => Some(AnotherType(s2.whatever))
  case _ => None
}

Is there a way to write the above function in a way that avoids the default case and wrapping the result in Some where it's defined? The best I've come up with so far is this:

def f(s:SomeType):Option[AnotherType] = pf.lift(s)
def pf:PartialFunction[SomeType,AnotherType] = {
  case s1:SubType1 => AnotherType(s1.whatever)
  case s2:SubType2 => AnotherType(s2.whatever)
}

Is there a way to do it without defining an intermediate function? I've already tried various things along the lines of the following, but haven't got anything to compile yet:

def f:Function[SomeType,Option[AnotherType]] = {
  case s1:SubType1 => AnotherType(s1.whatever)
  case s2:SubType2 => AnotherType(s2.whatever)
}.lift
tshepang
  • 12,111
  • 21
  • 91
  • 136
Kristian Domagala
  • 3,686
  • 20
  • 22

2 Answers2

8

condOpt in object scala.PartialFunction. From the scaladoc:

def onlyInt(v: Any): Option[Int] = condOpt(v) { case x: Int => x }
huynhjl
  • 41,520
  • 14
  • 105
  • 158
  • But `condOpt(x)(pf)` is equivalent to `pf.lift(x)`. Am I missing something? Or is the point that you can use a function literal here without explicit type annotations? – oxbow_lakes Jan 18 '11 at 08:41
  • @oxbow_lakes, I can't speak for Kristian and I didn't think too much about the question or my answer, `condOpt` just seemed relevant to what he was asking. – huynhjl Jan 18 '11 at 15:15
  • Indeed, I do like the fact that condOpt infers the types. – Kristian Domagala Jan 20 '11 at 08:12
4

Not so much an answer, as an explanation of why huynhjl's answer is correct...

Part of your confusion is that you're trying to def a partial function. All this does is to create a method that returns a PartialFunction object, when you may as well create the thing directly:

val pf: PartialFunction[SomeType,AnotherType] = {
  case s1:SubType1 => AnotherType(s1.whatever)
  case s2:SubType2 => AnotherType(s2.whatever)
}

Though I personally prefer to use type ascription:

val pf = {
  case s1:SubType1 => AnotherType(s1.whatever)
  case s2:SubType2 => AnotherType(s2.whatever)
} : PartialFunction[SomeType,AnotherType]

Either way, you have to specify what the input type is, so you have to give the exact signature of the PartialFunction. I know it feels like it should be possible to to infer this but, alas, that is sadly not the case!

Using the ascribed version, you can then define and lift all in the same place:

val pf = ({
  case s1:SubType1 => AnotherType(s1.whatever)
  case s2:SubType2 => AnotherType(s2.whatever)
} : PartialFunction[SomeType,AnotherType]).lift

PartialFunction.condOpt is the better solution though, as it allows the inferencer to do most of this work for you, leaving much cleaner code :)

Kevin Wright
  • 49,540
  • 9
  • 105
  • 155
  • This is exactly what I do also when I need the same behaviour: it's a shame that `condOpt` is such an awful method name. I'd use that instead. Why they haven't called it `applyLifted` I do not know! – oxbow_lakes Jan 19 '11 at 08:05
  • It's not exactly the most intuitive name in the world, is it? – Kevin Wright Jan 19 '11 at 11:14
  • Thanks Kevin, I thought I tried val as well, but it must have been the wrong incantation. Your answer is certainly along the lines of the path I was going down, but I accepted huynhjl's as it was more what I was looking for (despite the function name!) – Kristian Domagala Jan 20 '11 at 08:11
  • @Kristian Rightly so, mine was just a clarification as to *why* it was the correct answer. – Kevin Wright Jan 20 '11 at 11:44