3

So, suppose, I want to provide a "catch all" fall back for a PartialFunction:

 val foo: PartialFunction[Int, String] = { case 1 => "foo" }
 val withDefault = foo orElse { _.toString }

This does not compile: missing parameter type for expanded function ((x$1) => x$1.toString). This:

  val withDefault = foo orElse { case x: Int => x.toString }

Does not compile either (same error).

This:

val withDefault = foo orElse { (x: Int) => x.toString }

fails with type mismatch; found : Int => String; required: PartialFunction[?,?]

The only way I could find to make it work is to spell out the whole thing:

val withDefault = foo orElse PartialFunction[Int, String] { _.toString }

Is there any better syntax for this? I mean, one without having to tell it that I am passing a partial function from int to string to where it expects to receive a partial function from in to string. This is not ambiguous at all, why do I have to do this?

Dima
  • 39,570
  • 6
  • 44
  • 70

2 Answers2

2

Maybe you need applyOrElse:

val withDefault = foo.applyOrElse(_: Int, (_: Int).toString)

Or maybe you would like something like this:

implicit class PartialFunToFun[A,B](val f: PartialFunction[A,B]) extends AnyVal {
  def withDefault(bar: A => B) = f.applyOrElse[A,B](_: A, bar)
}

and use it: foo.withDefault(_.toString)(1)

Also if you want to get just another PartialFunction you can use the next syntax:

val withDefault = foo.orElse[Int, String]{case x => x.toString}
  • I'll accept, because this does help. But please see this other question I just wrote, because I realized, that the question I asked here isn't exactly what I wanted to ask :) http://stackoverflow.com/questions/38359522/anonymous-partialfunction-syntax – Dima Jul 13 '16 at 18:49
0

The errors you encountered for the first two are not specific to orElse. They also occur when you attempt to define the same functions separately.

scala> { _.toString }
<console>:12: error: missing parameter type for expanded function ((x$1: <error>) => x$1.toString)
       { _.toString }


scala> { case x: Int => x.toString }
<console>:12: error: missing parameter type for expanded function
The argument types of an anonymous function must be fully known. (SLS 8.5)
Expected type was: ?
       { case x: Int => x.toString }
       ^

For the last one, you are defining a function rather than a PartialFunction, thus leading to the "type mismatch" since orElse expects a PartialFunction to be passed.

scala> { (x: Int) => x.toString }
res3: Int => String = $$Lambda$1127/2044272973@3d5790ea

The final thing I'll add is that orElse is meant as a way to union two PartialFunctions. _.toString in itself is not a PartialFunction, though you could create a PartialFunction that uses it. To me it sounds like you want to have a "default" result for all the values that foo is not defined for, so I think you actually want applyOrElse instead since that is its use case. See the API to learn more.

Scott Shipp
  • 2,241
  • 1
  • 13
  • 20
  • Of course. The thing is that when you define it separately the type of the function is indeed unknown: `{ _.toString }` is ambiguous, because `_` could really stand for anything here. On the other hand, when you do `foo orElse { _.toString } `, the only way it can be interpreted is that `_` is an `Int`, and `{ case x => x.toString }` is a partial function. – Dima Jul 13 '16 at 18:40
  • Also, see this question: http://stackoverflow.com/questions/38359522/anonymous-partialfunction-syntax where I think I express the same idea better. – Dima Jul 13 '16 at 18:48
  • It is still ambiguous because the point of ```orElse``` is to create a union of the input domains from the two PartialFunctions. How can it convert _.toString to a PartialFunction when the input domain isn't specified? That's something the compiler can't just infer. It doesn't just assume you mean a PartialFunction that applies to all Int's and I don't think that it should since that seems to lie outside the intent of orElse. The result of orElse should be another PartialFunction which still only applies to a limited input domain. – Scott Shipp Jul 13 '16 at 18:53
  • The input domain is, obviously, all of ints. Just like in `List(1,2,3).collect { case x => x.toString } `. Which of your arguments doesn't apply to this example? Because it does compile. I mean, I don't know about "intent" part, but what is _formally_ different about .`collect` expecting a partial function as an argument, and `orElse` expecting the same? – Dima Jul 13 '16 at 18:57
  • It's the result type that's different between ```collect``` and ```orElse```. My point is that the result type of ```orElse``` is still another PartialFunction. All PartialFunctions should have have a set of values they are defined upon, that's why there is an ```isDefinedAt``` method. The collect example you gave suffers from a similar issue. It should use map not collect because you are expecting _all_ values in the list to get operated on, whereas the point of ```collect``` is to use a PartialFunction to only operate on some values of the list. – Scott Shipp Jul 13 '16 at 19:31
  • Try this and see if it answers your questions. http://code.scottshipp.com/2015/12/03/how-scala-partialfunction-is-a-practical-and-elegant-solution/ – Scott Shipp Jul 13 '16 at 19:33