-1.plus(2) == -3

I had an extension function for Int .
When I wrote some tests for it and found something strange.

-1.plus(2) //result: -3

I tried to use reformat code but is still -1.plus(2) not - 1.plus(2).
I must use (-1).plus(2) here.
I tried to test some infix functions, so

-1 xor 3 //result: -4
-1.xor(3) //result: -2

:joy:

1 Like

Makes sense if you see it as function calls by putting parentheses for clarity:

-(1.plus(2)) == -3

I know that, but it is inconsistent with the result of infix function and a little unnatural here.

Different operators have different priorities (like * and +), and an infix function also has a priority which is different from that of the other operators, so there is no requirement for consistency here.

-1 is a negative number. The minus sign is a part of negative numbers, is not an operator here.
So here is a constant -1 not a - symbol .

No, that’s not how the Kotlin parser works. -1 is an application of the unary minus operator to the constant 1.

I suggest to fix the it even it is a very small bug.
Thank you for your reply and happy new year: :grin:

This is not a bug, this is how the language is designed to work.

2 Likes

Yes, of course, this is one of any number of tricky details that a programmer could get wrong if she isn’t paying attention, but as @yole has already mentioned, this is the correct and intended behavior, and that behavior (the low priority of unary operators) is not something unique to Kotlin; it’s standard across programming languages.

Your particular example might make it feel like the behavior is wrong, but imagine this contrasting example:

data class 
Person( var age: BigDecimal )
{
    fun getAge(): BigDecimal = age
}

val person = Person( BigDecimal( 42 ) )

val x = -person.getAge()

Do you expect x to equal a BigDecimal instance of -42? Or do you expect a compile-time error that the class Person doesn’t have a unaryMinus operator?

To at least the benefit of consistency and simplicity-of-implementation, just like the D programming language does, Kotlin’s lexer & parser is designed to treat -1 in the same manner as it treats -person: as the unary minus operator applied to the integer literal 1, not as some unique and different symbol from 1 (just like -person isn’t some unique symbol that’s unrelated to person). And the priority of unary operators, additionally, makes it such that -person.getAge() applies the unary operator to person.getAge() rather than just to person (just as it applies the unary operator to 1.plus(2) rather than to just 1).

2 Likes

Thank you for your reply :smiley:

I’ve tried Swift, C# and Ruby in the past few days, OK, only in Ruby -1.plus(2) is 1.

So I don’t think it’s a problem for the Kotlin’s lexer & parser to treat -1 in the different manner as it treats -person. An small if will simply solve this problem.

It is designed to be so… I don’t think it’s a good idea due to the principle of least astonishment.

I still think it would be better if it is not designed like this. It would be more consistent compare to the infix function and would be more comfortable for most programmers who meet it.

IMO code like -1.plus(2) should be prohibited by the style guide and the IDE must emit a warning with a quick-fix to add the brackets.

6 Likes

It has already been designed like this, and changing the design would be a backwards incompatible change. And once again: the unary minus operator has different precedence from an infix call, and there is no more reason to complain about the inconsistency than to complain about the inconsistency of handling + and * signs in mathematical expressions.

2 Likes

Both arguments have good points.

If I were reading somebody else’s code and saw -1.plus(2) I would first ask, why didn’t they just write -1 + 2? And without a defense of the strange looking code I personally would first guess that -1 was a constant, not a unary minus operator.
In the spirit of least astonishment, I asked a couple non-kotlin developers what they guessed -1.plus(2) should be, and I get both answers -3 and 1. So I guess write your expression as -1 + 2 if you don’t want to confuse people.

Thank you for your reply.

Q: “Hey guys, guess what -1.plus(2) will be in Kotlin?”
A: “(Since you asked me deliberately, perhaps it’s not 1) Um… -3?”

It’s different when you read about it.
“Why does these tests got failed !
Failed at -2.next() shouldBe -1?!”:confused:

I wrote extension functions for Int and Float. When I wrote some tests, I found this problem.
-1.plus(2) is just a simple example to emphasize how funny this problem is.

I wrote something like

-1.atLeast(0) shouldBe 0 //equals to Math.max(num,atLeast)

or some BDD assertions:

-1.shouldSmallerThan(0)

But when take these functions in the project it will be vocabulary.atLeast(0). No confusion at all !

Yole said what is done cannot be undone, so I’m not confused about it at all now.

Finally I realize there’s no perfect languages, it’s my fault to always think that the only constant in the world is change.

I still use Kotlin everyday and discover a lot of fun about Kotlin,
Happy :kotlin:!

1 Like