5

Is there any quick way to use as a concrete function (of type, say, (A) => B) as a PartialFunction[A, B]? The most concise syntax I know of is:

(a: A) => a match { case obj => func(obj) }

Is there an implicit conversion anywhere, something like:

implicit def funcAsPartial[A, B](func: A => B) = new PartialFunction[A, B] {

  def isDefinedAt(a: A) = true
  def apply(a: A) = func(a)

}

I guess I just wrote what I was looking for, but does this already exist in the Scala libraries?

Kenneth Allen
  • 347
  • 3
  • 7

2 Answers2

5

Doing this with an implicit conversion is dangerous, for the same reason that (A) => B should not inherit from PartialFunction[A, B]. That is, the contract of PartialFunction guarantees that you can safely* call apply wherever isDefinedAt returns true. Function1's contract provides no such guarantee.

Your implicit conversion will result in a PartialFunction that violates its contract if you apply it to a function that is not defined everywhere. Instead, use a pimp to make the conversion explicit:

implicit def funcAsPartial[A, B](f: A => B) = new {
   /** only use if `f` is defined everywhere */
   def asPartial(): PartialFunction[A, B] = {
      case a => f(a)
   }

   def asPartial(isDefinedAt: A => Boolean): PartialFunction[A, B] = {
      case a if isDefinedAt(a) => f(a)
   }
}

// now you can write
val f = (i: Int) => i * i

val p = f.asPartial // defined on all integers
val p2 = f.asPartial(_ > 0) // defined only on positive integers

* As discussed in the comments, it may not be entirely clear what "safety" means here. The way I think about it is that a PartialFunction explicitly declares its domain in the following precise sense: if isDefinedAt returns true for a value x, then apply(x) can be evaluated in a way that is consistent with the intent of the function's author. That does not imply that apply(x) will not throw an exception, but merely that the exception was part of the design of the function (and should be documented).

Aaron Novstrup
  • 20,967
  • 7
  • 70
  • 108
  • Thank you; I had the misconception that Function1 implied being defined over the entire domain. – Kenneth Allen Jul 29 '11 at 18:26
  • @AaronNovstrup: your explanation of PartialFunction's contract is the only one which makes sense, but is not reflected by ScalaDocs (at least until 2.9.1). `PartialFunction`'s ScalaDocs claim that: "A partial function of type `PartialFunction[A, B]` is a unary function where the domain does not necessarily include all values of type `A`." Moreover, they never claim that it is safe (in what sense) to call f wherever defined, and that's rather easy to violate, as done by the `PartialFunction` literal `{ case 0 => 1 / 0 }`. Where did you get that info? Does a bug report need to be filed? – Blaisorblade Jan 12 '12 at 15:46
  • @Blaisorblade I believe that I read this explanation on the mailing list when I was learning Scala (it's been a while), and I wasn't looking at any documentation when I wrote this answer. And, yes, it is easy and even somewhat common to violate this contract (e.g., to wrap/rethrow an exception in a catch block). The real point is that PartialFunctions define their domain while ordinary Functions do not (with some fuzziness about what that actually means). – Aaron Novstrup Jan 12 '12 at 16:53
  • @Blaisorblade The documentation should probably be revised along the lines of the changes I just added to my answer. – Aaron Novstrup Jan 12 '12 at 17:17
  • While searching among existing bug reports, I found this explanation from Odersky - which matches what you read: http://article.gmane.org/gmane.comp.lang.scala.internals/2057. I created a bugreport for docs as discussed, linking back here and citing your name and suggestions: https://issues.scala-lang.org/browse/SI-5370 – Blaisorblade Jan 13 '12 at 11:24
0

No, I tried to find one a few months ago and ended up writing my own that's essentially the same as yours.

Alex Cruise
  • 7,939
  • 1
  • 27
  • 40
  • It seems to me that `(A) => B` should inherit from `PartialFunction[A, B]`, not the other way around. – Kenneth Allen Jul 26 '11 at 17:40
  • 1
    I would agree with that, on the ground that a (total) Function is a Partial function which happens to be defined everywhere (isDefinedAt(x) = true). Martin Odersky however says a Function is not guaranteed to be a total Function, it is just that its domain is undocumented. So a PartialFunction is a Function which documents its domain. – Didier Dupont Jul 26 '11 at 20:14