2

I'm having trouble understanding underscores in function literals.

val l = List(1,2,3,4,5)
l.filter(_ > 0)

works fine

l.filter({_ > 0})

works fine

l.filter({val x=1; 1+_+3 > 0}) // ie you can have multiple statements in your function literal and use the underscore not just in the first statement.

works fine

And yet:

l.filter({val x=_; x > 0})
e>:1: error: unbound placeholder parameter
l.filter({val x=_; x > 0})

I can't assign the _ to a variable, even though the following is legal function literal:

l.filter(y => {val x=y; x > 0})

works fine.

What gives? Is my 'val x=_' getting interpreted as something else? Thanks!

Jacek Laskowski
  • 72,696
  • 27
  • 242
  • 420
Bruce
  • 2,406
  • 5
  • 29
  • 35

3 Answers3

8

Actually, you have to back up a step.

You are misunderstanding how the braces work.

scala> val is = (1 to 5).toList
is: List[Int] = List(1, 2, 3, 4, 5)

scala> is map ({ println("hi") ; 2 * _ })
hi
res2: List[Int] = List(2, 4, 6, 8, 10)

If the println were part of the function passed to map, you'd see more greetings.

scala> is map (i => { println("hi") ; 2 * i })
hi
hi
hi
hi
hi
res3: List[Int] = List(2, 4, 6, 8, 10)

Your extra braces are a block, which is some statements followed by a result expression. The result expr is the function.

Once you realize that only the result expr has an expected type that is the function expected by map, you wouldn't think to use underscore in the preceding statements, since a bare underscore needs the expected type to nail down what the underscore means.

That's the type system telling you that your underscore isn't in the right place.

Appendix: in comments you ask:

how can I use the underscore syntax to bind the parameter of a function literal to a variable

Is this a "dumb" question, pardon the expression?

The underscore is so you don't have to name the parameter, then you say you want to name it.

One use case might be: there are few incoming parameters, but I'm interested in naming only one of them.

scala> (0 /: is)(_ + _) res10: Int = 15

scala> (0 /: is) { case (acc, i) => acc + 2 * i } res11: Int = 30

This doesn't work, but one may wonder why. That is, we know what the fold expects, we want to apply something with an arg. Which arg? Whatever is left over after the partially applied partial function.

scala> (0 /: is) (({ case (_, i) => _ + 2 * i })(_))

or

scala> (0 /: is) (({ case (_, i) => val d = 2 * i; _ + 2 * d })(_))

SLS 6.23 "placeholder syntax for anonymous functions" mentions the "expr" boundary for when you must know what the underscore represents -- it's not a scope per se. If you supply type ascriptions for the underscores, it will still complain about the expected type, presumably because type inference goes left to right.

som-snytt
  • 39,429
  • 2
  • 47
  • 129
  • Thanks for your answer. You're exactly right - I was thinking the block I provided was a function literal. You've clarified it totally. I must admit that I don't follow the appendix, probably because my skills aren't up to that yet. I'll reread a few times and hopefully get it.. Thanks! – Bruce Oct 26 '13 at 08:27
  • Hmm.. Does that mean that you can never have a function literal using the underscore placeholder syntax that has more than one statement?! If I do full-form: l.foreach((x: Int) => { print("Hi"); print(x) }) then I get Hi1Hi2Hi3 but if I do the abbreviated form, my block gets taken as a block, rather than delimiting my anonymous function: l.foreach({ print("Hi"); print(_) }) gives Hi123. How then can I have more than one statement in my literal ie. get Hi1Hi2Hi3 using the abbreviated syntax? – Bruce Oct 26 '13 at 08:50
  • @Bruce On SO a couple of answers about this syntax try to give a rule of thumb; alexiv here calls it "desugars to closest scope"; paragraph 3 of the 6.23 section has the two conditions; it has to do with disallowing nested expressions. In practice, you expect to try it with an expression, and sometimes it doesn't work. One trick at least is to change to operator syntax, like (x op _) can help to unpack an expr (i.e. get rid of parens). – som-snytt Oct 26 '13 at 17:19
1

Because in this two cases underscore (_) means two different things. In case of a function it's a syntactic sugar for lambda function, your l.filter(_ > 0) later desugares into l.filter(x => x > 0). But in case of a var it has another meaning, not a lambda function, but a default value and this behavior is defined only for var's:

class Test {
  var num: Int = _
}

Here num gonna be initialized to its default value determined by its type Int. You can't do this with val cause vals are final and if in case of vars you can later assign them some different values, with vals this has no point.

Update

Consider this example:

l filter {
  val x = // compute something
  val z = _
  x == z
}

According to your idea, z should be bound to the first argument, but how scala should understand this, or you you have more code in this computation and then underscore.

Update 2

There is a grate option in scala repl: scala -Xprint:type. If you turn it on and print your code in (l.filter({val x=1; 1+_+3 > 0})), this what you'll see:

private[this] val res1: List[Int] = l.filter({
  val x: Int = 1;
  ((x$1: Int) => 1.+(x$1).+(3).>(0))
});

1+_+3 > 0 desugares into a function: ((x$1: Int) => 1.+(x$1).+(3).>(0)), what filter actually expects from you, a function from Int to Boolean. The following also works:

l.filter({val x=1; val f = 1+(_: Int)+3 > 0; f})

cause f here is a partially applied function from Int to Boolean, but underscore isn't assigned to the first argument, it's desugares to the closes scope:

private[this] val res3: List[Int] = l.filter({
  val x: Int = 1;
  val f: Int => Boolean = ((x$1: Int) => 1.+((x$1: Int)).+(3).>(0));
  f
});
4lex1v
  • 21,367
  • 6
  • 52
  • 86
  • @om-nom-nom thanks, i see. `_` isn't defined for vars. That was an explanation when you can use such syntax – 4lex1v Oct 25 '13 at 13:38
  • Ah thanks for your answer. So is there a way I can assign the _ and use it in a function literal? (of course I could just go the long hand syntax, but I'm trying to understand scala) – Bruce Oct 25 '13 at 13:42
  • @Bruce i don't see the point in this, what's wrong with `_ > 0` for example? You can try case syntax: `l.filter { case y => > 0 }` but still – 4lex1v Oct 25 '13 at 13:45
  • 1
    It's just that I understand things better when I am clear about what they mean. Programming in scala says that "you can use underscores as placeholders for one or more parameters, so long as each parameter appears only one time within the function literal" That immediately makes me wonder "What if you had a more complicated literal where you wanted to use that parameter more than once? Can you assign it to another variable then use that other variable?" – Bruce Oct 25 '13 at 13:51
  • Your answer clarified why my approach didn't work - thank you. I'm still wondering though if you can use the _ shorthand and assign the parameter to a variable somehow - just out of interest, not because it's necessarily going to be useful.. – Bruce Oct 25 '13 at 13:53
  • @Bruce i see your point. I've added a little example, not that's not possible, because in your case it's simple to see and bind `_` to the argument, but how would you differentiate it in a harder example? – 4lex1v Oct 25 '13 at 13:59
  • Thanks for your update. Why should scala not understand that it replaces the first underscore it finds even if it's not the first statement? It works fine with l.filter({val x=1; 1+_+3 > 0}) – Bruce Oct 25 '13 at 14:13
  • @Bruce added one more example, no you see the difference? – 4lex1v Oct 25 '13 at 14:23
  • Thanks for your answer - scala -Xprint:type is a very interesting option. However, my original question still stands: how can I use the underscore syntax to bind the parameter of a function literal to a variable? Ie how can I do val x=_ (if this didn't mean something else)? – Bruce Oct 25 '13 at 14:47
1

The underscore syntax is mainly user for the following replacement:

coll.filter(x => { x % 2 == 0});
coll.filter(_ % 2 == 0);

This can only replace a single parameter. This is the placeholder syntax. Simple syntactic sugar for a lambda.

In the breaking case you are attempting null initialization/defaulting.

For primitive types with init conventions:

var x: Int = _; // x will be 0

The general case:

var y: List[String] = _; // y is null
var z: Any = _; // z = null;

To get pedantic, it works because null is a ref to the only instance of scala.Null, a sub-type of any type, which will always satisfy the type bound because of covariance. Look HERE.

A very common usage scenario, in ScalaTest:

class myTest extends FeatureTest with GivenWhenThen with BeforeAndAfter {
    var x: OAuthToken = _;
   before {
      x = someFunctionThatReturnsAToken;
   }
}

You can also see why you shouldn't use it with val, since the whole point is to update the value after initialization.

The compiler won't even let you, failing with: error: unbound placeholder parameter. This is your exact case, the compiler thinks you are defaulting, a behaviour undefined for vals.

Various constraints, such as timing or scope make this useful. This is different from lazy, where you predefine the expression that will be evaluated when needed.

For more usages of _ in Scala, look HERE.

Community
  • 1
  • 1
flavian
  • 28,161
  • 11
  • 65
  • 105
  • Thanks, Flavian. Actually I think you may be thinking me to understand more than I do. I was just trying to get the function literal parameter _ to be assigned to a variable within the function literal. (I didn't know about the var x=_ syntax at all and wasn't trying to attempt something like that with val x=_. I was just trying to get x to have the same value as the function literal parameter) – Bruce Oct 25 '13 at 13:56
  • Thanks - yes I say that works in my initial question. I'm asking - how can I bind the parameter _ to a variable in a function literal? Thanks. – Bruce Oct 25 '13 at 14:48