This repository contains two implementation of function composition in Kotlin and benchmark tests.
The implementation:
inline operator fun <P1, R1, R2> ((R1) -> R2).plus(crossinline f: (P1) -> R1): (P1) -> R2 {
return { p1: P1 -> this(f(p1)) }
}
If you need combine several operations in one sequence you can implement it as follows:
@Benchmark
@CompilerControl(CompilerControl.Mode.DONT_INLINE)
fun firstCase(): Double {
val prices = listOf(3.4, 5.6, 5.6, 3.4, 3.4, 3.4, 5.6, 5.6, 3.4, 3.4)
return prices
.map { it - it * 0.9 }
.map { it + 0.3 }
.map { it - 0.1 }
.sum()
}
To increase readability of code you can extract code from maps to separate functions:
@Benchmark
@CompilerControl(CompilerControl.Mode.DONT_INLINE)
fun secondCase(): Double {
val prices = listOf(3.4, 5.6, 5.6, 3.4, 3.4, 3.4, 5.6, 5.6, 3.4, 3.4)
return prices
.map(::discount)
.map(::tax)
.map(::aid)
.sum()
}
fun discount(price: Double) = price - price * 0.9
fun tax(price: Double) = price + 0.3
fun aid(price: Double) = price - 0.1
It still contains several invoking of map(). Now we can use the composition of functions approach:
@Benchmark
@CompilerControl(CompilerControl.Mode.DONT_INLINE)
fun thirdCase(): Double {
val prices = listOf(3.4, 5.6, 5.6, 3.4, 3.4, 3.4, 5.6, 5.6, 3.4, 3.4)
return prices
.map(::aid + ::tax + ::discount)
.sum()
}
It looks much better!
And what about performance!??
To run benchmarks:
- Clone the repository
- Build a .jar file using this command at the root of the repository:
mvn clean install
- To run the .jar with fast benchmarking:
java -jar target/benchmarks.jar -wi 0 -i 1 -f 1 -tu ns -bm avgt
To run the .jar with default benchmarking:java -jar target/benchmarks.jar
Added inline and crossinline based on this [suggestion] (#1).
Added function to combine predicates:
@Benchmark
@CompilerControl(CompilerControl.Mode.DONT_INLINE)
fun filteringSecondCase(): List<Student> {
return students
.filter(::ageMoreThan20 and ::firstNameStartsWithE and ::theLengthOfSecondNameMoreThan5)
}
inline infix fun <P> ((P) -> Boolean).and(crossinline predicate: (P) -> Boolean): (P) -> Boolean {
return { p: P -> this(p) && predicate(p) }
}