4

I'm posting this question out of curiosity to see if someone knows how pattern matching works in the following case. Let's say I have a function value defined as follows:

val f = (s: String) => s.toInt

It's type is of course String => Int. Now I want to create a new function based on pattern matching the output of whatever is passed to this function. I can define that as follows:

val f2 = f(_: String) match {
  case i: Int => i + 2
}

Now my new function is also from String => Int, but it adds 2 during the process. It can be called as follows:

scala> f2("3")
res0: Int = 5

If I do the same without doing partial application then I get a match based on the function itself:

val f3 = f match {
  case x => "matched: " + x
}

Now the value f3 is assigned "matched <function1>" because it called match with 'f' as the value.

So my question is this, how does Scala tell these two apart? They are both function values and both are of type String => Int. In fact, if I assign the partially applied function value to a temporary variable tmp before running match, then it behaves the same as f3:

val tmp = f(_: String)
val f4 = tmp match {
  case x => "matched: " + x
}

Now f4 is assigned "matched <function1>" instead of being a function String => Int.

I can see the value in wanting to do either, I'm just curious how it is done. Is this just some magic Scala added that somehow it figures out you are partially applying a function in the context of a match so it will generate something different...

1 Answer 1

9

That's just how underscores work.

f(_: String) match {
  case i: Int => i + 2
}

is a shorthand for

(x: String) => (f(x) match {
  case i: Int => i + 2
})

(parentheses added to make things more clear) but your other examples are equivalent to

(x: String => f(x)) match {
  case y => "matched: " + y
}
Sign up to request clarification or add additional context in comments.

3 Comments

Not sure I'm convinced. In the second example, the function value f is just a value so there is no need to do (x: String => f(x)) match {..}. In fact, it has no value x to feed in from anywhere before the match is done. The case is just matching on a value with type (String => Int) so f match {...} suffices. I buy the first example. That means val tmp = (x: String) => f(x) which is then passed to match.
@Mike There is no need to eta-expand f, but f is functionally equivalent to its eta-expansion (x: String => f(x)). He said they are equivalent, not that this is what the compiler actually translates them into.
Ok, i now agree (x: String => f(x)) is equivalent to f, they are both String => Int. Which also means I can replace f with 'f __' to trigger manual eta-expansion and it should still work as in the f3 case (tried it in the REPL and it is in fact the case). So I guess the moral of the story is that 'f _' and f( _ : String) are not equivalent even though for general function application they behave the same.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.