I'd like to write a generic Kotlin function that works with arbitrary types, as long as they support some basic numeric operations, such as comparison and addition. Something like this (note: this code doesn't compile):
fun <T : ???> twiceTheLarger(a: T, b: T) = if (a > b) a + a else b + b;
In C++, this kind of code works. The following C++ twiceTheLarger function takes anything that supports the + and > operators, be it a primitive numeric type or a custom class:
#include <iostream>
template <typename T> T twiceTheLarger(T a, T b) {
return a > b ? a + a : b + b;
}
int main() {
std::cout << twiceTheLarger(1, 2) << std::endl; // int values
std::cout << twiceTheLarger(42.0, 1.0) << std::endl; // double values
}
How can I get a similar result in Kotlin? The best way I could come up so far is to explicitly pass an object with the required functions, like this:
interface NumericOperators<T> {
fun plus(a: T, b: T): T
fun greaterThan(a: T, b: T): Boolean
}
object IntNumericOperators : NumericOperators<Int> {
override fun plus(a: Int, b: Int) = a + b
override fun greaterThan(a: Int, b: Int) = a > b
}
object DoubleNumericOperators : NumericOperators<Double> {
override fun plus(a: Double, b: Double) = a + b
override fun greaterThan(a: Double, b: Double) = a > b
}
fun <T> twiceTheLarger(a: T, b: T, operators: NumericOperators<T>) =
if (operators.greaterThan(a, b)) operators.plus(a, a) else operators.plus(b, b)
fun main() {
println(twiceTheLarger(1, 2, IntNumericOperators)) // int values
println(twiceTheLarger(42.0, 1.0, DoubleNumericOperators)) // double values
}
Is there a better way?
Edit:
I realize that I could create overloads for each primitive numeric type. The thing is that I'm writing a library function that needs to work with arbitrary number-like types, even types my library doesn't know about. So type-specific overloads are not an option.
You will need to create functions for each primitive type. You can't use <T : Number>, despite all numbers in Kotlin inherit this. Number superclass is used only for the castings.
You'll need to create functions or extension functions:
fun Int.twiceTheLarger(a: Int, b: Int) = if (a > b) a + a else b + b;
fun Double.twiceTheLarger(a: Double, b: Double) = if (a > b) a + a else b + b;
It would be great if you can utilize Comparable<T> in another function for the comparison. Types of T need to overload operator fun plus(other: T) as well.
interface Addable<T>: Comparable<T>{
operator fun <T> Addable<T>.plus(a: T)
}
fun <T : Addable<T>> T.twiceTheLarger(a: T, b: T) {
return if (a > b) a.plus(a) else b + b
}
Related
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.
I have the following simple class hierarchy.
abstract class B {
abstract val o : Int
}
class D1 (m: Int, n: Int) : B() {
override val o = m + n
}
class D2 (m: Int, n: Int) : B() {
override val o = m * n
}
I need a "factory function" f that gives me instances of D1 or D2 by calling it as f<D1>() or f<D2>() with hard coded parameters, say 3 and 4. The following doesn't work but illustrates what I need:
// won't compile; just demonstrates what I need
fun < T : B > f () : T {
return T(3, 4) // i. e. return T.constructor(m, n)
}
How to best accomplish this? Any DRY way is fine as long I don't have to repeat 3, 4 all over my code when I instantiate D1 or D2
The only way to do it is via reflection and reified type parameter:
inline fun <reified T : B> f(m: Int = 3, n: Int = 4): T {
val constructor = T::class.constructors.first {
it.parameters.size == 2 &&
it.parameters.all { param -> param.type == Int::class }
}
return constructor.call(m, n)
}
Here's an alternate way without reflection, but you have to manually type out a line for each class you want to handle.
inline fun <reified T : B> f(): T{
val m = 3
val n = 4
return when (T::class) {
D1::class -> D1(m, n)
D2::class -> D2(m, n)
else -> error("Unsupported type ${T::class}")
} as T
}
I am learning kotlin, and want to know how to add functions to a const,
here the JS example...
function suma (a, b){
return a + b
}
console.log("sua suma::", suma(2,3))
function multiplica (a, b){
return a * b
}
console.log("sua multiplik::", multiplica(2,3))
const operations = {
suma,
multiplica
}
console.log ("sum of first class::", operations.suma(2,3))
console.log ("mult of first class::", operations.multiplica(2,3))
so how do I achieve the same on Kotlin?
Here what I have tried:
fun suma(a: Int, b: Int): Int{
return a + b
}
fun multiplica (a: Int, b: Int): Int{
return a * b
}
const operations = {
suma(),
multiplica()
}
fun main() {
println("sua suma:: ${suma(2, 3)}")
println("sua multiplica:: ${multiplica(2, 3)}")
println("sua multiplica:: ${operations.multiplica(2,3)}")
}
It can be done using object keyword, like following:
fun suma(a: Int, b: Int): Int {
return a + b
}
fun multiplica(a: Int, b: Int): Int {
return a * b
}
fun main() {
val operations = object {
val _suma = ::suma
val _multiplica = ::multiplica
}
println("sua suma:: ${operations._suma(2, 3)}")
println("sua multiplica:: ${operations._multiplica(2, 3)}")
}
The only disadvantage is that you have to choose a name for operations.XXXXX that is different from the name of referenced function: note var _suma vs fun suma. Otherwise the compiler will consider it recursive problem
"Only Primitives and String are allowed" for const
The operator you're looking for is :: (Double colon)
An enum example of this is
enum class Operations(function: KFunction<Int>) {
Suma(function = ::suma),
Multiplica(function = ::multiplica)
}
The main issues here are that Kotlin is a statically typed language, and doesn't have the same idea of Objects that can contain arbitrary attributes, like Javascript does.
I'm kinda new to Kotlin, so there may be a better way to do this, but what I'd do is define a Map to do the same sort of thing:
fun suma(a: Int, b: Int): Int{
return a + b
}
fun multiplica (a: Int, b: Int): Int{
return a * b
}
val operations: Map<String, (a: Int, b:Int) -> Int> = hashMapOf(
"suma" to ::suma,
"multiplica" to ::multiplica)
fun main() {
println("sua suma:: " + operations.get("suma")?.invoke(2, 3))
println("sua multiplica:: " + operations.get("multiplica")?.invoke(2, 3))
}
Result:
sua suma:: 5
sua multiplica:: 6
Note that operations is an immutable Map ('const' kinda) in that its contents can't be changed once it's defined.
The access and having to use invoke seems kinda messy. This seems to be due to the fact that a Map can only contain nullable types. That's the main reason I think there's probably a better way to do this.
I am playing with the idea of using Kotlin's inline classes to express type-safe operations with units of measurement. For example, let's define units for distance, time and speed:
inline class Meters(val v: Float) {
operator fun plus(other: Meters) = Meters(v + other.v)
operator fun times(amount: Float) = Meters(v * amount)
operator fun compareTo(other: Meters) = v.compareTo(other.v)
operator fun div(other: Meters): Float = v / other.v
fun calcSpeed(time: Seconds) = MetersPerSecond(v * time.v)
// operator fun times(time: Seconds) = MetersPerSecond(v / time.v) // clash (for now?)
}
inline class Seconds(val v: Float) {
operator fun plus(other: Seconds) = Seconds(v + other.v)
operator fun times(amount: Float) = Seconds(v * amount)
operator fun compareTo(other: Seconds) = v.compareTo(other.v)
operator fun div(other: Seconds): Float = v / other.v
fun calcSpeed(distance: Meters) = MetersPerSecond(distance.v / v)
}
inline class MetersPerSecond(val v: Float) {
operator fun plus(other: MetersPerSecond) = MetersPerSecond(v + other.v)
operator fun times(amount: Float) = MetersPerSecond(v * amount)
operator fun compareTo(other: MetersPerSecond) = v.compareTo(other.v)
fun calcDistance(time: Seconds) = Meters(v * time.v)
fun calcTime(distance: Meters) = Seconds(distance.v / v)
}
The idea here is to define methods for operations that either:
Leave the unit unchanged (eg: sum, multiplication for a pure
value)
Result in a pure value (eg: division by value of the same
unit)
Result in a value of another defined unit (eg: time * distance = speed)
So that one can write expressions like:
val distance = Meters(1f)
val time = Seconds(1f)
val speed: MetersPerSecond = (distance * 0.5f).calcSpeed(time)
Looking at the operators in the example which fall into cases 1 and 2, I can clearly see a pattern, and I am wondering whether there is a way to define those methods once in a more generic fashion for all types that need this "unit-of-measurement" behavior.
I thought about having a common interface and defining operators as extension functions with generics:
interface UnitOfMeasurement { val v: Float }
operator fun <T: UnitOfMeasurement> T.plus(other: T) = T(v + other.v)
But of course this won't work because I can't instantiate a generic like that. Is there any way to achieve this?
You could make use of a factory function of measurement units, that is reified on type T. Then you use it in your operators:
interface UnitOfMeasurement { val v: Float }
inline class Meter(override val v: Float) : UnitOfMeasurement
inline class Second(override val v: Float) : UnitOfMeasurement
inline fun <reified T : UnitOfMeasurement> create(v: Float): T {
return when (T::class) {
Meter::class -> Meter(v) as T
Second::class -> Second(v) as T
else -> throw IllegalArgumentException("don't know how to create ${T::class}")
}
}
inline operator fun <reified T : UnitOfMeasurement> T.plus(other: T) = create<T>(v + other.v)
fun main() {
val a = Meter(10f)
val b = Meter(5f)
println(a + b)
val c = Second(60f)
val d = Second(30f)
println(c + d)
// println(a + c) // throws IllegalArgumentException
}
Can be tried here
fun <T> doSum(a: T, b: T) : T {
val result : Number = when {
a is Int && b is Int -> a + b
a is Long && b is Long -> a + b
a is Float && b is Float -> a + b
a is Double && b is Double -> a + b
else -> throw IllegalArgumentException()
#Suppress("UNCHECKED_CAST")
return result as T
}
fun <T: Number> doOperation(a: T, b: T, operationToPerform: (T, T) -> T ) {
println(operationToPerform(a, b))
}
I have the method doOperations that takes a generics function as a parameter that I intend to run on the other 2 parameters passed.
However, invoking the same in main as :
fun main(args: Array<String>) {
doOperation (2, 3, doSum)
}
is returning errors like:
Error:(15, 24) Kotlin: Function invocation 'doSum(...)' expected
Error:(15, 24) Kotlin: No value passed for parameter 'a'
Error:(15, 24) Kotlin: No value passed for parameter 'b'
Any suggestions on the way to call doOperation with doSum()?
(& changing < T > doSum to < T: Number > doSum throws up one more error:
Error:(15, 24) Kotlin: Type parameter bound for T in fun doSum(a: T, b: T): T
is not satisfied: inferred type (Int, Int) -> Int is not a subtype of Number)
For anyone that comes in here at a later date, the fix here was to send in the doSum(...) method like this:
doOperation (2, 3, ::doSum)
Adding the :: before doSum(...) allows you to reference a top-level, local, or member function.
More info: https://kotlinlang.org/docs/reference/lambdas.html#function-types