Reputation: 1769
I want to implement a cumulative sum method for List
, such a function should accept List<Int>
, List<Float>
, etc.
I could go as far as to say that it should accept List<anything that implements add>
But I see no way of specifying this in the official documentation.
I have tried using the type Number
but it apparently does not work.
How should I go about making a generic extension function that accepts any type implementing a particular method like add
?
Upvotes: 4
Views: 1146
Reputation: 6589
In Kotlin, you have these extension functions in the stdlib:
fun Iterable<Byte>.sum(): Int { /* compiled code */ }
fun Iterable<Double>.sum(): Double { /* compiled code */ }
fun Iterable<Float>.sum(): Float { /* compiled code */ }
fun Iterable<Int>.sum(): Int { /* compiled code */ }
fun Iterable<Long>.sum(): Long { /* compiled code */ }
fun Iterable<Short>.sum(): Int { /* compiled code */ }
inline fun <T> Iterable<T>.sumBy(selector: (T) -> Int): Int { /* compiled code */ }
inline fun <T> Iterable<T>.sumByDouble(selector: (T) -> Double): Double { /* compiled code */ }
And from that you can see there isn't a way to write a function for "List
of types that have plus
method", since Kotlin is not duck typed.
Also, you mentioned List<anything that implements add>
, which is unclear (or, clear but incorrect), because in Kotlin, all number types have plus
instead of add
. From that you can know, different classes have their own definitions of the "add" operation, and such operation have different names under different circumstances.
I recommend you to use the function called reduce
, or reduceRight
, or fold
, or foldRight
, which allows you to customize your "add" operation by passing an argument.
Like, the implementation of sum
for List<Int>
is basically:
fun List<Int>.sum() = fold(0, Int::plus)
And the like.
Upvotes: 2
Reputation: 170899
There is a solution for this in other languages (Haskell and Scala are best-known ones), which is likely to be added in Kotlin eventually: type classes. See https://github.com/Kotlin/KEEP/pull/87 for the (non-final) proposal to add them to Kotlin.
Until they are added, you can do something similar manually:
interface Adder<T> {
fun add(x: T, y: T): T
}
object IntAdder : Adder<Int> {
fun add(x: Int, y: Int): Int = x + y
}
// similar for other types
// definition of cumulativeSum
fun <T> cumulativeSum(list: List<T>, adder: Adder<T>): List<T> = ...
// call
cumulativeSum(listOf(1,2,3), IntAdder)
The part type classes solve is that you won't need to pass the adder
parameter manually, instead the compiler will determine it based on T
.
Upvotes: 1
Reputation: 7031
Numbers only have the following methods:
public abstract fun toDouble(): Double
public abstract fun toFloat(): Float
public abstract fun toLong(): Long
public abstract fun toInt(): Int
public abstract fun toChar(): Char
public abstract fun toShort(): Short
public abstract fun toByte(): Byte
There is no add, so you can't add them together
typealias Adder<T> = (T)->T
fun <T: Number> T.toAdder(): Adder<T> {
return when(this) {
is Long -> {{it -> (this as Long + it as Long) as T}}
is Int -> {{it -> (this as Int + it as Int) as T}}
is Double -> {{it -> (this as Double + it as Double) as T}}
else -> throw AssertionError()
}
}
fun <T: Number> List<T>.mySum(zero: T): T {
return map { it.toAdder() }.fold(zero) { acc, func -> func(acc) }
}
fun main(args: Array<String>) {
val total = listOf(1,2,4).mySum(0)
}
This works, but it uses a lot of casting, and should be avoided
Upvotes: 4