Is *arrayOf() a statement? - operators

I was using the spread operator, when I noticed something strange:
// compiles
val list1 = listOf(1, 2, *(if(0 > 1) arrayOf(3) else arrayOf()))
// does not compile
val list2 = listOf(1, 2, if(0 > 1) *arrayOf(3) else *arrayOf())
One of the compiler errors is
Expecting an expression
So, is *arrayOf() a statement?
If yes, how can it be evaluated by listOf()?

No, it's neither a statement nor expression.
The spread operator has a very special role: it can only modify the semantics of an array-typed expression passed as vararg, giving a special kind of argument. It cannot be used independently in other expressions, that's why the code where it's used inside an if expression does not compile.

Related

Kotlin - Is it possible to check for operator precedence

Let's say I have the following class:
data class Foo(var name: String) {
operator fun plus(foo: Foo): Foo {
name += foo.name
return this
}
}
Which is then used like this:
val foo1 = Foo("1")
val foo2 = Foo("2")
val foo3 = Foo("3")
foo1+foo2+foo3
println(foo1.name) // 123
Now, what if I wanted different behavior depending on whether the operations are chained like this:
foo1+foo2+foo3
Or like this:
(foo1+foo2)+foo3
In both cases foo1's name would be 123, but let's say that in the second case I would want foo1's name to be (12)3.
Is there a way to add a condition to the plus function, which checks whether the foo that it is called on originates from within parentheses/has a higher precedence or not.
No, that is not possible, because that makes no sense tbh. The compiler will just resolve the order of operations, brackets just indicate that 1+2 should resolve first and the result should be added to 3. There is no concept of brackets anymore in that result, you just have the outcome.
What is confusing you is that you are abusing the plus function to do something people wouldn't expect. You should not use the plus function to mutate the object it is called upon, this is not expected behaviour. Users will expect the plus function to return a new object not a mutation of the left or right operand.
In your case:
operator fun plus(foo: Foo): Foo {
return Foo(name += foo.name)
}
Don't do something different lest you want other people to be really confused. Fyi plusAssign is a mutating function, but still wouldn't allow you to do what you want. To achieve that you'd probably need to write your own parser and parse the operands and operators yourself.

How to prevent implicit generalization casts when using generic types in Kotlin? [duplicate]

This question already has an answer here:
Force type parameter to be invariant at use-site when it is covariant at declaration site
(1 answer)
Closed 1 year ago.
Minimal example:
fun <T> Iterable<T>.find2(elem: T) = this.find { it == elem }
Here, T is used to both denote the type of the Iterable, as well as the type of elem. It seems fine, however, a statement like this is syntactically correct, but doesn't make semantic sense:
listOf(1, 2, 3).find2("foo")
I assume this works because T resolves to Any.
I'm aware of the solution to explicitly state the type of the function:
listOf(1, 2, 3).find2<Int>("foo")
In this case, the compiler reports an incompatibility error, and rightfully so. However, this doesn't seem to be the solution, since it requires to explicitly declare the type, and will not report an error if forgotten (which makes bugs likely).
Is there a way to "constrict" the type <T>, so that, if, for example, the receiver is of type Iterable<Int>, the parameter must also be Int? In other words, is there a way to prevent the implicit cast to Any?
In the statement
listOf(1, 2, 3).find2("foo")
There are actually two types that need to be resolved,
T1 in listOf<T1> and T2 in Iterable<T2>.find2,
and have the following constraints
T1 is the same as T2, because listOf returns List<T1>, which is Iterable<T2>, for find2
1, 2 and 3 should be assignable to T1
"foo" should be assignable to T2
Therefore, the type has to be Any.
We usually explicitly declare the type of the list since it is the source,
listOf<Int>(1, 2, 3).find2("foo")
Or separate it into two statements
val listOfInt = listOf(1, 2, 3)
listOfInt.find2("foo")

What is the difference in these way of creating a list in Kotlin ? My result1 is valid but result2 is invalid

val list1 = listOf(1,2,3,4,5,6,7,8,9)
val list2 = listOf(1..9)
/*
my result1 is valid but result2 gives error
*/
val result1= list1.filter{i -> i>2}
val result2 = list2.filter{i -> i>2}
listOf(1, 2, 3, 4, 5, 6, 7, 8, 9)
is a list of nine numbers — specifically, Ints.
However, 1..9 is a range object — specifically, an IntRange.  So:
listOf(1..9)
is a list that contains a single object.
list1.filter{ i -> i > 2 }
is valid, because filter() applies its lambda to each Int in the list — and the > operator is defined between two Ints.  However:
list2.filter{ i -> i > 2 }
is not valid, because the lambda is applied to the IntRange, and you can't compare an IntRange to an Int.
Your IDE and compiler should give you several clues about this.  First, if you hover over list1 and list2 you'll see the types that the compiler infers for them: List<Int> and List<IntRange>, respectively.
And not only does the IDE underline { i -> i > 2 }, it also shows the > in red.  Hovering over that shows the error ‘Unresolved reference. None of the following candidates is applicable because of receiver type mismatch…’  That's a bit cryptic, but you should get to know it, because it's quite common and very useful.  It's telling you that you're trying to call a function/method/operator that's not defined for the type(s) you have.  In this case, > (which is equivalent to compareTo()) is defined for two Ints, two Longs, two Strings, etc., but not for an IntRange and an Int.
So the compiler doesn't know what return type it should have, and temporarily assumes Unit — but filter() needs a lambda returning a Boolean, which is why the whole lambda is underlined with a ‘Type mismatch’ error.
As Tenfour04 says, you can convert a range into a list if you need to (using the toList() method).  But in practice, that's not needed very often because there's usually a better approach.  (A range takes much less memory than most lists, and is therefore quicker to iterate through; and it implements Iterable so you can already do most list-type things with it anyway.)

Kotlin "let{}" Doesn't Provide Smart Cast

Just learned Kotlin Nullable type and let{} function which replaces the if (xx != null) {} operation.
But one thing I am confused is that, we all know and I Think the Complier Should Know that when we use let{}, the variable/object who is calling this function is possiblly null, however the complier still requires me to add the safe call operator "?" after the variable name instead of providing Smart Cast like it does in if (xx != null) {}. Why?
My piece of code:
fun main() {
var number1: Int? = null
//val number2 = number1.let { it + 1 } ?: 10 //doesn't work, not quite "smart"
val number2 = number1?.let { it + 1 } ?: 10 //works, must have "?"
println(number1)
println(number2)
}
You've already got answers in the comments, but just to explain the ? thing...
Kotlin lets you make null-safe calls on nullable variables and properties, by adding ? before the call. You can chain this too, by doing
nullableObject?.someProperty?.someFunction()
which evaluates nullableObject, and if it's non-null it evaluates the next bit, otherwise the whole expression evaluates to null. If any part of the chain evaluates as null, the whole expression returns null.
So it has this short-circuiting effect, and you can use the elvis "if null" operator to create a default value if you can't evaluate the whole chain to a non-null result:
nullableObject?.nullableProperty?.someFunction() ?: defaultAction()
and once you introduce the null check in the chain, you have to add it for every call after that - it's basically propagating either the result of the previous bit, or the null it resolved to, so there's a null check at each step
The let block is just a scope function - you use it on a value, so you can run some code either using that value as a parameter or a receiver (a variable or this basically). It also has the side effect of creating a new temporary local variable holding that value, so if the original is a var it doesn't matter if that value changes, because your let code isn't referring to that variable anymore.
So it's useful for doing null checks one time, without worrying the underlying value could become null while you're doing stuff with it:
nullableVar?.let { it.definitelyIsNotNull() }
and the compiler will recognise that and smart cast it to a non-null type. An if (nullableVar != null) check can't guarantee that nullableVar won't be null by the time the next line is executed.

Kotlin: What do the unary plus/minus operators do on numbers?

I've noticed in Kotlin that there are already defined unaryPlus and unaryMinus operators on all of the number types.
What's the purpose of these operators? Are they in some way connected to the prefix forms of inc and dec?
Others have defined the basic meaning of unaryMinus and unaryPlus, and in reality on numeric types they may not actually even be called as functions. For example, coding +x or x.unaryPlus() generates the same bytecode (where x is type Int):
ILOAD 1
ISTORE 2
And the code -x or x.unaryMinus() generates the identical bytecode:
ILOAD 1
INEG
ISTORE 2
But there is more going on that this...
So why does the compiler even generate anything for +x? Some people will say that +x and x.unaryPlus() doesn't do anything, and that -x and x.unaryMinus() only reverses the sign. That isn't correct. In Java it is more complicated because it can involve widening and unboxing, see Unary Numeric Promotion which explains the full consequences of these operators. This has consequences for boxed values and types smaller than Int. For value of type Short and Byte these operators will return a new unboxed value widened of type Int. And since both operators have this more hidden functionality then both must generate bytecode even if you don't think +x does anything. By the way, this is similar to what C language does and it is called Usual Arithmetic Conversions.
Therefore this code is invalid:
val x: Short = 1
val y1: Short = +x // incompatible types
val y2: Short = x.unaryPlus() // incompatible types
val z1: Short = -x // incompatible types
val z2: Short = x.unaryMinus() // incompatible types
In these numeric cases on the base numeric types they are just compiler magic to allow for the idea of these operators to be equated to operator functions that you might want to overload in other classes.
For other uses such as Operator Overloading...
But they are there for more than just mathematical use and can be used on any class as an operator. Kotlin exposes operators as functions so that you can apply operator overloading on a specific set of operators which include unaryMinus and unaryPlus.
I could use these to define operators for my own or existing classes. For example I have a Set<Things> where Things is an enum class along with an unaryMinus() operator to negate the contents of the finite set of options:
enum class Things {
ONE, TWO, THREE, FOUR, FIVE
}
operator fun Set<Things>.unaryMinus() = Things.values().toSet().minus(this)
And then I can negate my enum set whenever I want:
val current = setOf(Things.THREE, Things.FIVE)
println(-current) // [ONE, TWO, FOUR]
println(-(-current)) // [THREE, FIVE]
Notice that I had to declare my extension function with the modifier operator or this will not work. The compiler will remind you if you forget this when you try to use the operator:
Error:(y, x) Kotlin: 'operator' modifier is required on 'unaryMinus' in 'com.my.favorite.package.SomeClass'
These operators are the signs of the integers. Here are some examples:
+5 calls 5.unaryPlus() and returns 5.
-5 calls 5.unaryMinus() and returns -5.
-(-5) calls 5.unaryMinus().unaryMinus() and returns 5.
The purpose of those operators is to be able to write:
val a = System.nanoTime()
val b = -a // a.unaryMinus()
val c = +b // b.unaryPlus()
They are not directly related to ++/inc and --/dec operators however they can be used in conjunction.
Notice that the following expressions are different:
--a // a = a.dec()
-(-a) // a.unaryMinus().unaryMinus()
fun main(){
var a = 34
var b = 56
println("Orignal value:"+ a)
println("Orignal value:"+ b
//The value will not change using .unaryPlus() will generate bytecode
println("After unary plus:" + a.unaryPlus())
//The value will invert the sign using .unaryMinus() will generate bytecode
println("After unary minus:" + b.unaryMinus())
}
Solution:
Orignal value:34
Orignal value:56
After unary plus:35
After unary minus:-55