5 ms·
It doesn't. It's invalid code in most (all?) cases. scala> val f: Int => Int = _ - 1 f: Int => Int = <function1> scala> val ary = Array(1,2,3) ary: Ar
by ssmoot 12y ago
It doesn't. It's invalid code in most (all?) cases.
scala> val f: Int => Int = _ - 1
f: Int => Int = <function1>
scala> val ary = Array(1,2,3)
ary: Array[Int] = Array(1, 2, 3)
scala> ary.map(f(_ * 2))
<console>:10: error: missing parameter type for expanded function ((x$1) => x$1.$times(2))
ary.map(f(_ * 2))
^
You can't think of "_" like a placeholder. It's not. It's to lift an argument of a function.
So simplify it: map in this context wants a Function[Int, Int] right? So f(_ * 2) must return a Function[Int,Int]. But it doesn't probably. _ is lifting some argument out of whatever f is. If you assume _ is an Int, does f take a Function[Int,Int]? No. It takes an Int. So there's no way to parse this that makes sense. It's not just "I have a stack of vars, pull one off the stack and bind it every time I write an underscore, reading left to right". That would be some AST generative grammar hack. That's not what the underscore is. It's simpler and more consistent than that.
What you're probably looking for instead is Function Composition. So something like:
scala> ary.map(f compose(_ * 2))
res10: Array[Int] = Array(1, 3, 5)
So why does that work? Because we were able to compose f() into a larger function that satisfies the signature of the argument map[T](Int => T) requires. A lot like a Stream conceptually.
(Is it correct to say in this context f() is a Monad? I'm not sure, I need to sit down and grok the category stuff sometime...)
Or you can write it the long way (calling f inside a new function). But instead of defining "steps" you'd be creating a new imperative function and driving the stack.
- breuleux 12y agoThat's interesting, but you did not answer my question. I wanted to know how Scala determines where to insert =>. In my example, Scala interprets "ary.map(f(_ * 2))" as "ary.map(f(x => x * 2))" (both of these expressions yield the same error). If it interpreted it the other way, then the expression would work (using your definition of f). Perhaps a better example to use would have been: Array((_:Int) * 2) ==> (x:Int) => Array(x * 2) ? (correctly typed) ==> Array((x:Int) => x * 2) ? (also correctly typed) As it turns out, Scala goes for the latter. After playing with the feature a bit I'd say the rules are engineered to fit particular use cases, but they can be a bit finicky if you venture outside of them. For instance, "1 + _ * 2" will work, but "(1 + _) * 2" won't. "f(_)(x)" does not mean the same thing as "(f(_))(x)". I mean, I wouldn't say the feature is bad, but it definitely has a DWIM vibe to it that would make me classify it as a hack.
- ssmoot 12y ago> In my example, Scala interprets "ary.map(f(_ * 2))" as "ary.map(f(x => x * 2))" I think this is where I'm not communicating what I'm trying to say very well. What I'm trying to say is that that statement is false. The underscore isn't a placeholder saying "inject a Function here". You probably didn't mean that exactly of course, but it's a programming language; it pays to be a bit pedantic I think. It's easier to understand if you work backwards maybe. Is this valid? (_:Int) * 2 Of course. It's a Function[Int,Int]. The important part is the asterisk method that's being lifted to a Function. So now you take that perfectly valid piece of code lifting a method to a function and you pass it to Array.apply: Array.apply((_:Int) * 2) Why should your code start behaving differently? That wouldn't be consistent at all. You had a Function[Int,Int] before, but now that you've passed that function value to Array.apply instead of getting an Array[Function[Int,Int]] as you would in every single other case, you get a Function[Int,Array[Int]]? What sense does that make? That seems like straight up voodoo. There's some syntax supporting this feature. But not so much as you think. It's just the "lift". Type-inferencing lets you get away with what looks like a little more sometimes. I dunno. Maybe that's helpful. Maybe not.
- breuleux 12y ago> The underscore isn't a placeholder saying "inject a Function here". It is. Look at the error message: error: missing parameter type for expanded function ((x$1) => x$1.$times(2)) It did inject a function, it's right there, in plain text. Then it did type inference. I mean, what else is "lifting" the asterisk to a Function supposed to mean, if not injecting a function around a placeholder? And what about "1 + _ * 2"? What is it lifting? The asterisk? No. It is lifting more than that. > You had a Function[Int,Int] before, but now that you've passed that function value to Array.apply instead of getting an Array[Function[Int,Int]] as you would in every single other case, you get a Function[Int,Array[Int]]? That's besides the point. The question is, when the parser sees "_", how much of the context does it grab along with it? In other words, I know it's lifting stuff, what I want to know is how much it lifts. Here's another example: f(_, 2)(3) ==> x => (f(x, 2)(3)) ? ==> (x => f(x, 2))(3) ? Scala does the former. That's a legitimate choice given common use cases, but the latter is simpler, preserves the invariant that "a(b) <=> (a)(b)" and has use cases as well, e.g. to make an expression like "f(super_long_expression, 2)" more readable. > But not so much as you think. It's just the "lift". Type-inferencing lets you get away with what looks like a little more sometimes. I still don't see what type inference has to do with this. The error message makes it clear that the lift is done before type inference kicks in. In a dynamic language, you would stop at the lift, but it would otherwise work just the same.