how to simplify scala's function literal like this?
Asked Answered
M

5

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?

Moro answered 18/1, 2011 at 14:53 Comment(1)
Just use: val odd = ! even (_:Int)Selfdetermination
M
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 )
Matador answered 18/1, 2011 at 15:14 Comment(1)
thx for clarifying the rules about the boundary of the lambda expression!Moro
L
10

Only slightly cheating:

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

:-)

Lambard answered 18/1, 2011 at 20:30 Comment(4)
+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.Matador
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 :)Moro
@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.Detribalize
Just be aware this doesn't work for negative numbers, whereas & does.Questa
R
7

There you go:

val isOdd = ((_: Int) & 1) andThen (1 ==)
Residential answered 18/1, 2011 at 16:36 Comment(0)
T
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)
Theoretical answered 18/1, 2011 at 15:2 Comment(4)
I don't recommend either of these solutions -- they're both more complicated, and they both create an extra, unnecessary lambda. Stick with val isOdd = (x:Int) => (x & 1) == 1Matador
Sure! But the OP asked specifically if there was any way he could write his "isOdd" lambda using only "_". I don't think we are discussing good style here.Theoretical
You missed val isOdd = ((_: Int) & 1) andThen (_ == 1). :-)Muniment
Yeah! Fortunately @Residential posted this one!Theoretical
R
1

A different approach

val isOdd = (_:Int).&(1) == 1
Reclinate answered 6/1, 2012 at 18:47 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.