Searles
Searles

Reputation: 1827

Call collect on partial function as member of an object

I am using some functions that return Options, but I would like to replace them by PartialFunctions in order to use the collect function in Scala. In detail I am trying to call collect (and collectFirst) on a list of objects that contain a partial function in the following way:

class A(val bs : List[B]) {
    def getAll(i : Int): List[Int] = bs.map(_.foo(i))
}

class B(val y : Int) {
    val foo : PartialFunction[Int, Int] = {
        case x if x > y => y
    }
}

The above code compiles and does what I want to if the function foo is defined for all values in bs.

val a = new A(List(new B(1), new B(2), new B(3)));
println(a.getAll(5))

prints "List(1, 2, 3)", but of course I run into an error if foo is not defined for a value. Hence, I want to replace it by "collect"

def getAll(i : Int): List[Int] = bs.collect(_.foo(i))

In my understanding collect should work pretty much the same as map, yet still the above code does not compile (I get a type mismatch because foo cannot be resolved). What is the best way in this case?

Upvotes: 0

Views: 194

Answers (1)

dk14
dk14

Reputation: 22374

collect expects to receive a partial function (not a function or lambda), that will take an element of your collection as an input: PartialFunction[B, Int]. But here you want just to call your PartialFunction[Int, Int] inside another function, so you don't even passing a PartialFunction into collect (you're passing function (_.foo(i)): B => Int).

Solution without collect (but still with PartialFunction as a member of B):

def getAll(i : Int): List[Int] = bs.flatMap(_.foo.lift(i))

lift rises your function from PartialFunction[Int, Int] to Int => Option[Int] here.

If you really really want to collect:

import Function._
def getAll(i : Int): List[Int] = bs.collect(unlift(_.foo.lift(i)))

unlift reduces B => Option[Int] into PartialFunction[B, Int]

The ugliest version is collect{ case x if x.foo.isDefinedAt(i) => x.foo(i) }

Better solution is to move your partial function outside of B:

case class A(bs : List[B]) {
    def getAll(x : Int): List[Int] = bs.collect {
       case B(y) if x > y => y
    }
}

case class B(val y : Int)    

Upvotes: 1

Related Questions