When used in lambda functions the placeholder is generally presented as just syntactic sugar that allows you to avoid specifying a parameter, e.g.:
def bar(s: String) = s
val o = Some("x")
o.foreach(s => bar(s)) // Explicit parameter.
o.foreach(bar(_)) // No need for explicit parameter.
The second foreach is just a shorter version of the first. However if I add in another level of calls then things don't work as one might first assume:
def foo(s: String) = s
o.foreach(s => foo(bar(s))) // OK
o.foreach(foo(bar(_)) // Error
What's happening here is that foo feels it's being passed a lambda function rather than a String.
So while it might seem obvious to a simple human like me that Scala should transform foo(bar(_))
to s => foo(bar(s))
what it actually does is equivalent to:
o.foreach(foo(s => bar(s)))
Fair enough - it was maybe stupid to imagine Scala would magically know, as it were, how far outward to pull the parameter (though in this case one could imagine parsing the initial expression into a tree, seeing the placeholder and then trying to reformulate its immediate parent as a lambda and, on seeing that that doesn't make sense, push the reformulation up and up the tree until it does, but I guess it might just be too hard to reason about expressions in general if one had this kind of implicit behavior).
I can avoid using an explicit parameter and reformulate the error case above like so:
o.foreach((foo _).compose(bar)(_))
This is fairly clear.
But can someone provide a concise explanation of how to think about the placeholder in these situation and the transformations Scala is performing?
While it now seems obvious to me that Scala couldn't deduce that in my error case I meant s => foo(bar(s))
it's unclear to me what rules it must be using.
Given the nice theory behind most things in Scala I hardly imagine there are just some arbitrary rules like "if the placeholder is used as an argument to a function replace this by a lambda that invokes this function on its argument."
Note: I know that in the above examples that I don't even need to use the placeholder explicitly, i.e. I can write o.foreach(bar)
or o.foreach((foo _).compose(bar))
and yes - the original title is a naive formulation of this question - as often the process of asking the question made things clearer to me.