Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Scala: selecting function returning Option versus PartialFunction

I'm a relative Scala beginner and would like some advice on how to proceed on an implementation that seems like it can be done either with a function returning Option or with PartialFunction. I've read all the related posts I could find (see bottom of question), but these seem to involve the technical details of using PartialFunction or converting one to the other; I am looking for an answer of the type "if the circumstances are X,Y,Z, then use A else B, but also consider C".

My example use case is a path search between locations using a library of path finders. Say the locations are of type L, a path was of type P and the desired path search result would be an Iterable[P]. The patch search result should be assembled by asking all the path finders (in something like Google maps these might be Bicycle, Car, Walk, Subway, etc.) for their path suggestions, which may or may not be defined for a particular start/end location pair.

There seem to be two ways to go about this:

(a) define a path finder as f: (L,L) => Option[P] and then get the result via something like finders.map( _.apply(l1,l2) ).filter( _.isDefined ).map( _.get )

(b) define a path finder as f: PartialFunction[(L,L),P] and then get the result via something likefinders.filter( _.isDefined( (l1,l2) ) ).map( _.apply( (l1,l2)) )`

It seems like using a function returning Option[P] would avoid double evaluation of results, so for an expensive computation this may be preferable unless one caches the results. It also seems like using Option one can have an arbitrary input signature, whereas PartialFunction expects a single argument. But I am particularly interested in hearing from someone with practical experience about less immediate, more "bigger picture" considerations, such as the interaction with the Scala library. Would using a PartialFunction have significant benefits in making available certain methods of the collections API that might pay off in other ways? Would such code generally be more concise?

Related but different questions:

  • Inverse of PartialFunction's lift method
  • Is the PartialFunction design inefficient?
  • How to convert X => Option[R] to PartialFunction[X,R]
  • Is there a nicer way of lifting a PartialFunction in Scala?
  • costly computation occuring in both isDefined and Apply of a PartialFunction
like image 686
Gregor Scheidt Avatar asked Dec 01 '11 05:12

Gregor Scheidt


2 Answers

It feels like Option might fit your use case better.

My interpretation is that Partial Functions work well to be combined over input ranges. So if f is defined over (SanDiego,Irvine) and g is defined over (Paris,London) then you can get a function that is defined over the combined input (SanDiego,Irvine) and (Paris,London) by doing f orElse g.

But in your case it seems, things happen for a given (l1,l2) location tuple and then you do some work...

If you find yourself writing a lot of {case (L,M) => ... case (P,Q) => ...} then it may be the sign that partial functions are a better fit.

Otherwise options work well with the rest of the collections and can be used like this instead of your (a) proposal:

val processedPaths = for {
  f <- finders
  p <- f(l1, l2)
} yield process(p)

Within the for comprehension p is lifted into an Traversable, so you don't even have to call filter, isDefined or get to skip the finders without results.

like image 190
huynhjl Avatar answered Nov 08 '22 11:11

huynhjl


It's not all that well known, but since 2.8 Scala has a collect method defined on it's collections. collect is similar to filter, but takes a partial function and has the semantics you describe.

like image 31
Dave Griffith Avatar answered Nov 08 '22 12:11

Dave Griffith