5

I'm new to scala and trying to write a function literal that check whether a given integer is odd or not. my first attempt is:

val isOdd = (x:Int) => (x & 1) == 1

it works great, and, since the parameter x only appears once within this function literal, I'm tempted to use the "_" notation to simplify it further, like this:

val isOdd = ((_:Int) & 1 ) == 1

however this time the compiler complains :

warning: comparing a fresh object using `==' will always yield false
val isOdd = ((_:Int) & 1 ) == 1

what does this warning mean? why does the compiler recognize ((_ :Int) & 1) as fresh object rather than a bitwise operation that results in a value? is there any way to write this function literal using the "_" notation?

hind_d
  • 355
  • 2
  • 6

5 Answers5

20

The problem is basically that Scala needs to tell the difference between

val isOdd = ((_:Int) & 1 ) == 1

where you want everything to the right of the equals sign to be a lambda, and

val result = collection.map( _ + 1 )

where you want only the stuff inside the parentheses to be a lambda

Scala has decided that when you use the underscore to create a lambda, that it's going to pick the innermost set of parentheses as the boundaries of that lambda. There's one exception: (_:Int) doesn't count as the innermost parentheses because its purpose is only to group they type declaration with the _ placeholder.

Hence:

val isOdd = ((_:Int) & 1 ) == 1
            ^^^^^^^^^^^^^^
            this is the lambda

val result = collection.map( _ + 1 )
                            ^^^^^^^
                            this is the lambda

val result = collection.map(( _ + 1) / 2)
                            ^^^^^^^^
                            this is the lambda
                            and the compiler can't infer the type of the _

val result = somemap.map(( _ + 1) / 2 * _)
                         ^^^^^^^^
                         this is an inner lambda with one parameter
                         and the compiler can't infer the type of the _
                         ^^^^^^^^^^^^^^^^^
                         this is an outer lambda with one parameter

This last case lets you do things like

_.map(_ + 1)

and have that get translated into

x => x.map( y=> y + 1 )
Ken Bloom
  • 57,498
  • 14
  • 111
  • 168
10

Only slightly cheating:

val isOdd = (_: Int) % 2 == 1

:-)

Martin Odersky
  • 20,470
  • 9
  • 51
  • 49
  • 2
    +1 It's not cheating. In fact, I maintain that using an `&` for this is premature optimization. The `%` form is clearer, and any compiler (or maybe the JIT) worth it's salt will see `% 2` and change it to `& 1` if it's actually faster in that operating environment. – Ken Bloom Jan 19 '11 at 01:01
  • I agree that in general we should leave this sort of optimizations to compilers; but here i actually want to understand the rationale behind this lambda thing. again thx for clarifying the parentheses rule regarding lambda :) – hind_d Jan 19 '11 at 08:02
  • @KenBloom I would maintain that using the best algorithm for a job isn't premature optimization, it's just good programming, and I get tired of people using this justification for writing slow code (generally speaking, this is a micro-example where the difference is probably negligible). That you can accomplish it more elegantly with another mechanism has everything to do with style, and nothing to do with optimization. – PlexQ Dec 18 '12 at 18:53
  • Just be aware this doesn't work for negative numbers, whereas `&` does. – Karl Bielefeldt May 14 '14 at 19:42
7

There you go:

val isOdd = ((_: Int) & 1) andThen (1 ==)
Debilski
  • 66,976
  • 12
  • 110
  • 133
2

What Scala is doing is this:

  • it sees ((_:Int) & 1 ) and creates an object of type (Int) => Int, that is, a function.
  • it then applies the comparison operator == to compare this function to the value 1

A function is not equal to the value 1. Therefore the result is false, so your code is equivalent to:

val isOdd = false

What you could do is create another anonymous function that does the == 1 part of your computation. This is ugly:

val isOdd = ((_: Int) & 1)(_: Int) == 1

This is equivalent to the more verbose (and perhaps easier to understand):

val isOdd = (x: Int) => 1 == ((_: Int) & 1)(x)
Bruno Reis
  • 37,201
  • 11
  • 119
  • 156
1

A different approach

val isOdd = (_:Int).&(1) == 1