Functional Syntax?

Welcome to Scala 2.13.5 (OpenJDK 64-Bit Server VM, Java 11.0.11).
Type in expressions for evaluation. Or try :help.

scala> val x = 2
val x: Int = 2

scala> -x.abs
val res0: Int = -2

scala> - 2.abs
val res1: Int = 2

Are people cool with this?


What happens with -(2).abs? It’s nice for me if that returns -2, since spaces between - and the number are sometimes useful

Not cool at all. The behavior is the same in Scala 3.


Because of little edge cases like this in which I am unwilling to hold in my head/memory across many code-bases, my default is to add (unnecessary but) clarifying parenthesis everywhere.

And without deep researching it, I suspect that it isn’t possible to eliminate every single edge case due to the language feature interactions at this compressed of a point in the code. IOW, there is a tension between the reading simplification versus the explicit meaning expressibility.


The -x.abs syntax is actually not functional but object-oriented. Functional syntax would be instead:

import Math.abs

, and the ambiguity vanishes.

1 Like

Then why not abs(neg(x)), since unary_- is a method on x?

I’m pretty sure this post means functional as in the usage sense, not as in the FP sense


Referential transparency.

But then, would -someVariable.abs be abs(neg(someVariable)) or neg(abs(someVariable)) if someVariable is not a literal but another kind of expression? I much prefer the latter. If was foo(neg(x)), then ! should also be foo(not(x)). That’d be very confusing.


We could be like Enso and use spaces for precedence…

Why? It makes more sense to me, unary operators should have the highest precedence. You could argue that abs(-x) makes no sense, but that’s not up to the compiler to know

In my opinion behavior should be consistent and -2.abs should be treated in the same ways as any other unary operation, e.g. ~2.abs == ~ 2.abs == val x = 2; ~x.abs == ~(x.abs) have the same result


Because . has had higher precedence than unary operators for a while now, and changing it would likely break existing code, and also confuse people for whom the new syntax doesn’t make sense.


Agreed. Until it was mentioned, I would never have suspected that -2.abs was 2.

So (-2) should not parse as a single number literal but as 2.unary_- ?

This is probably main issue, -2 is native literal in many languages, so parser has special case for that and it cannot take into account context whether after literal there is a function or not

1 Like

I agree it’s confusing. It demonstrates a particular trickiness at the interface between parser and lexer. Parser would like to consume a token stream. “-2” must be two separate tokens “-” and “2” since otherwise x-2 would not parse correctly. But then, theoretically a space between - and 2 should not matter since the token stream is not whitespace sensitive. So by the rules of the syntax,


is -(2.abs), which is not what’s expected intuitively. The way out of this is messy. We need to special treat -2 as a literal if a literal is expected in the parser and there’s no space between the sign and the first digit. That’s what’s implemented in Fix negative literals by odersky · Pull Request #12459 · lampepfl/dotty · GitHub,


Actually, -(2.abs) is what I’d expect. Currently, it behaves as (-2).abs.


One would expect -2 to be a single literal, not 2.unary_-

But then, this leaves the problems that { val x = 2; -x.abs } and -2.abs are not the same value.
The initial question was whether people are comfortable with that. Maybe some are, and some are not. Without trying, I would have guessed -2.abs to be -2, but maybe I’m in the minority.


-<number> must be a literal since otherwise the minimal integer would not parse.

scala> 2147483648                                                                                                        
1 |2147483648
  |number too large

scala> -2147483648
val res1: Int = -2147483648