Option 1
It actually does, but you'll have to use signatures like
(T1) -> (T2) -> R
Here's an example of a curried comparator which allows partial application (the typealias
added for readability, can just as well be rewritten w/o any):
typealias CurriedComparator<T> = (T) -> (T) -> Int
fun main() {
val integers = mutableListOf(42, 0, 1, 3, 2)
val naturalOrder: CurriedComparator<Int> = { left ->
{ right ->
// JVM only, essentially the same as `left - right`
Comparator.naturalOrder<Int>().compare(left, right)
}
}
integers.sortWith { left, right ->
// Note the partial application:
naturalOrder(left)(right)
}
println(integers)
}
Having said the above, the curried
operation for a two-argument function (from this answer) can be implemented like this:
val <T1, T2, R> ((T1, T2) -> R).curried: (T1) -> (T2) -> R
get() = { arg0: T1 ->
{ arg1: T2 ->
this(arg0, arg1)
}
}
Usage example:
val difference = { a: Int, b: Int -> a - b }.curried(11)(7)
println(difference) // 4
Option 2
You'll have to overload the invoke()
operator, e. g.:
val compareInts: (Int, Int) -> Int =
{ left, right ->
left - right
}
operator fun <T1, T2, R> ((T1, T2) -> R).invoke(t1: T1): (T2) -> R =
{ t2 ->
this(t1, t2)
}
// This is the "normal" invocation:
val a: Int = compareInts(41, 42)
// This is the partial application using the overloaded `invoke()` operator:
val b: Int = compareInts(41)(42)
check(a == b)
Limitations
The capability of partial application comes at a price. Once we follow the above path, we'll immediately lose access to:
- Java interoperability,
- default parameters, and, more importantly,
- recursion (can still be implemented using the Y combinator).
Speaking of the recursion w/o the Y combinator, it's still possible, but (in the case of a 2-argument function) you'll have to rewrite
val comparator: (String) -> (String) -> Int = { /*...*/ }
as
fun comparator(left: String): (String) -> Int { /*...*/ }