Confused by the parameters of higher order functions in Kotlin - kotlin

In Kotlin, I write the following code, which calls the fold function.
fun operation(acc: Int, next: Int): Int {
return acc * next
}
val items = listOf(1, 2, 3, 4, 5)
println(items.fold(1, ::operation))
In the 5th line of the above code, the fold function uses the operation function.
This is reasonable because the fold function is declared to accept a function reference or lambda that takes exactly TWO parameters(3rd line of the following fold implementation from the Kotlin stdlib _Collections.kt)
public inline fun <T, R> Iterable<T>.fold(
initial: R,
operation: (acc: R, T) -> R
): R {
var accumulator = initial
for (element in this) accumulator = operation(accumulator, element)
return accumulator
}
What confuses me is that the fold function can also be fed with a one-parameter function Int::times like below.
val items = listOf(1, 2, 3, 4, 5)
println(items.fold(1, Int::times))
AFAIK, Int::times is declared to be a one-parameter member function as below:
/** Multiplies this value by the other value. */
public operator fun times(other: Int): Int
I don't quite understand the contradiction. Does it have anything to do with the keyword operator?

public operator fun times(other: Int): Int
(or more specifically, ::times) is actually of type Int.(Int) -> Int, which is the same as (Int, Int) -> Int. That allows for both ::times and ::operator to be used with fold.

Related

Operator overloading on += for set and get calls wrong setter

I have made an extension functions for BigIntegers, allowing me to add Ints to them.
operator fun BigInteger.plus(other: Int): BigInteger = this + other.toBigInteger()
// Allowing me to do
val c = myBigInt + 3
I have also made a Counter class, holding bigintegers for various keys, for easy counting. Since doing counter["1"] += myBigInt isn't allowed on standard maps (it's nullable), I have added a custom getter that returns a default value, making this possible.
class Counter<K>(val map: MutableMap<K, BigInteger>) : MutableMap<K, BigInteger> by map {
constructor() : this(mutableMapOf())
override operator fun get(key: K): BigInteger {
return map.getOrDefault(key, BigInteger.ZERO)
}
I can then use it like this
val counter = Counter<String>()
c["ones"] += 5.toBigInteger()
Problem is that I cannot use it like this:
c["ones"] += 5 // doesn't work, "Kotlin: No set method providing array access"
but this should be equivalent to this, which works, since it should use my extension operator on the bigint:
c["ones"] = c["ones"] + 5 // works
Why doesn't this work?
I've tried adding a set method for Ints, but then I see a very weird behavior. Kotlin will do the calculation correct, but then convert the BigInteger to an Int before passing it to my class! Example:
inline operator fun BigInteger.plus(other: Int): BigInteger {
val bigInteger = this + other.toBigInteger()
println("calculated bigint to $bigInteger")
return bigInteger
}
class Counter<K>(val map: MutableMap<K, BigInteger>) : MutableMap<K, BigInteger> by map {
constructor() : this(mutableMapOf())
override operator fun get(key: K): BigInteger {
return map.getOrDefault(key, BigInteger.ZERO)
}
operator fun set(key: K, value: Int) {
println("setting int $value")
map[key] = value.toBigInteger()
}
}
val c = Counter<String>()
c["1"] = "2192039569601".toBigInteger()
c["1"] += 5
println("result: ${c["1"]}")
c["1"] = "2192039569601".toBigInteger()
c["1"] = c["1"] + 5
println("result: ${c["1"]}")
Which prints
calculated bigint to 2192039569606
setting int 1606248646 <--- why does it call the int setter here?
result: 1606248646
calculated bigint to 2192039569606
result: 2192039569606
Why does Kotlin do the BigInt summation, but converts it back to an Int before sending to my setter?
Update
Since a comment suggest this is a compiler issue, any other ideas?
My ultimate goal here, was to have a counter of big integers, but to be able to easily add ints to it.
Adding this as a set function, makes it being called for both ints and bigints, so I can do the proper assignment myself. However, it will also then allow someone to add floats that will crash at runtime.
operator fun set(key: K, value: Number) {
map[key] = when (value) {
is BigInteger -> value
is Int -> value.toBigInteger()
else -> throw RuntimeException("only ints")
}
}
Any tips?
Notice that c["ones"] += 5 can be translated into calls in two ways:
c.set("ones", c.get("ones").plus(5))
c.get("ones").plusAssign(5)
The first way is what your code currently translates to, because you don't have a plusAssign operator defined. As I said in the comments, there is a bug in the compiler that prevents the operators from resolved correctly. When resolving c["ones"] += 5, It seems to be trying to find a set operator that takes an Int instead (possibly because 5 is an Int), which is unexpected. If you modify the code in the bug report a little, you can even make it throw an exception when executed!
class Foo {
operator fun get(i: Int) : A = A()
operator fun set(i: Int, a: A) {}
operator fun set(i: Int, a: Int) {}
}
class A {
operator fun plus(b: Int) = A()
}
class B
fun main(args: Array<String>) {
val foo = Foo()
foo[0] = foo[0] + 1
foo[0] += 1 // this compiles now, since there is a set(Int, Int) method
// but A can't be casted to Int, so ClassCastException!
}
It is rather coincidental (and lucky) in your case, that the compiler knows how to convert from BigInteger (or any other Number type actually) to Int, using Number#intValue. Otherwise the program would have crashed too.
A natural alternative way is to define the plusAssign operator, so that the assignment gets translated the second way. However, we can't do it on BigInteger, because plusAssign would need to mutate this, but BigInteger is immutable. This means that we need to create our own mutable wrapper. This does mean that you lose the nice immutability, but this is all I can think of.
fun main() {
val c = Counter<String>()
c.set("1", "2192039569601".toMutableBigInteger())
c.get("1").plusAssign(5)
println("result: ${c["1"]}")
}
data class MutableBigInteger(var bigInt: BigInteger) {
operator fun plusAssign(other: Int) {
bigInt += other.toBigInteger()
}
}
fun String.toMutableBigInteger() = MutableBigInteger(toBigInteger())
class Counter<K>(val map: MutableMap<K, MutableBigInteger>) : MutableMap<K, MutableBigInteger> by map{
constructor() : this(mutableMapOf())
override operator fun get(key: K): MutableBigInteger {
return map.getOrPut(key) { MutableBigInteger(BigInteger.ZERO) }
}
operator fun set(key: K, value: Int) {
println("setting int $value")
map[key] = MutableBigInteger(value.toBigInteger())
}
}
Notably, getOrDefault is changed to getOrPut - when a value is not found, we want to put the zero we return into the map, rather than just returning a zero that is not in the map. Our changes to that instance wouldn't be visible through the map otherwise.

What's the different between `plusAssign` and `add` for mutableList (or `put` for mutableMap)

In mutableList, we could add. and mutableMap we could put.
However I notice we could now also have plusAssign for them both. Checking under the hood, they are just add or put. What's the use of plusAssign?
public inline operator fun <T> MutableCollection<in T>.plusAssign(element: T) {
this.add(element)
}
public inline operator fun <K, V> MutableMap<in K, in V>.plusAssign(pair: Pair<K, V>) {
put(pair.first, pair.second)
}
plusAssign is a function for overloading += operator.
In the implementation for MutableList, this function adds an item to the list. For MutableMap puts a pair to the map.
val ml = mutableListOf(1)
ml += 2 // will be translated to `ml.plusAssign(2)`
println(ml) // [1, 2]
operator-overloading

Component destructuring with fewer than expected components

Let's say I want to do the following:
val (k, v) = pair.split("=".toRegex(), 2)
This code is fine if I always get 2 components from the split - however, if the delimiter is not present in the string, this code throws an exception, because the second element in the array isn't present.
The answer is almost certainly "no", but is there some way to coerce destructure to assign null values to missing components?
When destructuring objects, Kotlin calls componentN() for that object. For arrays, component1() is equal to get(0), component2() is equal to get(1), and so on.
So if the index is out of bounds, it'll throw ArrayIndexOutOfBoundsException, instead of returning null.
But you can make your operator function like this:
operator fun <T> Array<out T>.component1(): T? = if (size > 0) get(0) else null
operator fun <T> Array<out T>.component2(): T? = if (size > 1) get(1) else null
so if I run
val (k, v) = arrayOf(1)
println(k)
println(v)
the output will be
1
null
See:
Destructuring Declarations
You could add your own extension to List that adds the required number of null values to the end:
val (k, v) = pair.split("=".toRegex(), 2).padWithNulls(limit = 2)
Implementation can be done a couple of ways, here's just one:
private inline fun <reified E> List<E>.padWithNulls(limit: Int): List<E?> {
if (this.size >= limit) {
return this
}
val result: MutableList<E?> = this.toMutableList()
result.addAll(arrayOfNulls(limit - this.size))
return result
}
Here's a simpler one as well:
private fun <E> List<E>.padWithNulls(limit: Int): List<E?> {
val result: MutableList<E?> = this.toMutableList()
while (result.size < limit) {
result.add(null)
}
return result
}
Or wrapping this functionality even further:
val (k, v) = pair.splitAndPadWithNulls("=".toRegex(), 2)
private fun String.splitAndPadWithNulls(regex: Regex, limit: Int): List<String?> {
return this.split(regex, limit).padWithNulls(limit)
}
Its working for me
val pair="your string"
if(pair.isNotEmpty()&&pair.contains("=")) {
val (k, v) = pair.split("=".toRegex(), 2)
println(k)
println(v)
}
It doesn't cover as many cases as other answers (also might not be as obvious what's happening) but you can always force there to be at least the correct number of values to destructure (extra values will be ignored). Using your example you can just add null to increase the size of the list returned by split:
val (k, v) = "foo=bar".split("=".toRegex(), 2) + null
> k=foo, v=bar
val (k, v) = "foo".split("=".toRegex(), 2) + null
> k=foo, v=null
Playground example https://pl.kotl.in/W7gGYyAjC

Is there any replicate function in Kotlin?

replicate(n:Int,x:T):List<T> is a list of length n with x the value of every element.
I wrote a mutable version replicate as below:
fun <T> mutableReplicate(n:Int, x:T) : MutableList<T>{
val xs = mutableListOf<T>()
for (i in 1..n){
xs.add(x)
}
return xs
}
Is there any bulid-in immutable replicate function in Kotlin?
How to write ourselves an immutable replicate function in Kotlin?
You can use List instantiation functions. They accept a function from the Index to the desired element, but you can also use them to create a List of constant values.
fun <T> replicate(n:Int,x:T):List<T> {
return List(n) { x }
}
If you need a read-only list, you can implement replicate the following way:
fun <T> replicate(n: Int, x: T): List<T> = object : AbstractList<T>() {
override val size: Int = n
override fun get(index: Int): T =
if (index in 0..lastIndex) x else throw IndexOutOfBoundsException("$n")
}
It has an advantage that it requires a constant amount of memory no matter how large n is.

Kotlin - Override/Implement array-like accessor function

Is it possible to override or implement the [] accessors in Kotlin (using operator overloading or similar)?
val testObject = MyCustumObject()
println(testObject["hi"]) // i.e. implement this accessor.
In Python this is possible by implementing __getitem__ and __setitem__.
In Kotlin, it is get and set operator functions that you need to implement:
class C {
operator fun get(s: String, x: Int) = s + x
operator fun set(x: Int, y: Int, value: String) {
println("Putting $value at [$x, $y]")
}
}
And the usage:
val c = C()
val a = c["123", 4] // "1234"
c[1, 2] = "abc" // Putting abc at [1, 2]
You can define get and set with arbitrary number of parameters for indices (at least one, of course); in addition, set has the expression which is assigned at the use site passed as its last argument:
a[i_1, ..., i_n] is translated to a.get(i_1, ..., i_n)
a[i_1, ..., i_n] = b is translated to a.set(i_1, ..., i_n, b)
get and set can have different overloads as well, for example:
class MyOrderedMap<K, V> {
// ...
operator fun get(index: Int): Pair<K, V> = ... // i-th added mapping
operator fun get(key: K): V = ... // value by key
}
Note: this example introduces undesirable ambiguity for MyOrderedMap<Int, SomeType> since both get functions will match calls like m[1].
As stated in the documentation the a[i] is translated to a.get(i). In example:
class MyObject {
operator fun get(ix:Int):String{
return "hello $ix"
}
}
Let's you write:
val a = MyObject()
println(a[123]) //-> "hello 123"
Similarly a[i] = b is translated to a method call a.set(i, b).
You have to override get().
https://kotlinlang.org/docs/reference/operator-overloading.html
a[i] translates to a.get(i)