Named parameters vs _, dot notation vs. infix operation, curly and parentheses when using higher order functions in Scala

I'm having a harder time figuring out when I may or may not omit parentheses and / or periods, and how this interacts with _.

The specific case I had with this was

val x: X = ???
val xss: List[List[X]] = ???
xss map x :: _ //this doesn't compile
xss map _.::(x) //this is the same as the above (and thus doesn't compile)

      

the above two seem to be identical xss.map(_).::(x)

xss map (x :: _) //this works as expected
xss map {x :: _} //this does the same thing as the above

      

Meanwhile, the following also fails:

xss.map xs => x :: xs //';' expected but '=>' found.
xss.map x :: _ //missing arguments for method map in class List; follow this method with `_' if you want to treat it as a partially applied function
//so when I try following the method with _, I get my favourite:
xss.map _ x :: _ //Cannot construct a collection of type That with elements of type B based on a collection of type List[List[Main.X]]
//as opposed to
xss map _ x :: _ //missing parameter type for expanded function ((x$1) => xss.map(x$1).x(($colon$colon: (() => <empty>))))

      

Right now, I often play around "toggling characters until they compile", which I think is a sub-optimal programming strategy. How does it all work?

+3


source to share


1 answer


First, we need to distinguish between xss.map(f)

and xss map f

. According to Scala Documentation, any method that takes one parameter can be used as an infix operator.

In fact, method c is one of these methods. Ignoring the full signature and the fact that it inherited from , the signature looks like this: map

List

TraversableLike

final def map[B](f: (A) ⇒ B): List[B]

      

Therefore it requires one parameter f

, which is a function with a type A => B

. So if you have a function value defined as

val mySize = (xs:List[Int]) => xs.size

      

you can choose between

xss.map(mySize)

      

or

xss map mySize

      

This is a matter of preference, but according to the Scala Style Guide , the latter is preferred for this case, unless it is part of a complex expression where it is better to stick to dot notation.



Note that if you choose to use dot notation, you always need to qualify the function app with parentheses! This is why none of the following compilers work successfully.

xss.map xs => x :: xs // Won't compile
xss.map x :: _ // Won't compile
xss.map _ x :: _ // Won't compile

      

But most of the time, instead of passing the value to a function, you need to pass a literal to a function (aka anonymous function ). In this case, again, if you are using dot notation, you need something like xss.map(_.size)

. But if you are using infix notation it will be a matter of priority.

for example

xss map x :: _ // Won't compile!

      

does not work due to operator priority. Therefore, you need to use parentheses to eliminate the situation for the compiler on xss map (x :: _)

.

Using curly braces instead of braces has a very clear and simple rule of thumb. Again, any function that takes only one parameter can be applied with curly braces instead of braces, for both infix and dot notation. Thus, the following statements will be compiled.

xss.map{x :: _}
xss map {x :: _}

      

To avoid confusion, you can start with dot notation and explicit parameter types. Later, after compiling and possibly writing some unit tests for your code, you can start refactoring your code by removing unnecessary types, using infix notation, and using curly braces instead of brackets where it makes sense.

For this you can refer to the Scala Style Guide and Martin Oderski speaks in Scala Days 2013 , which deals with the Scala coding style. Also, you can always ask the IDE for help to make the code refactoring more concise.

+2


source







All Articles