https://kotlinlang.org logo
Title
d

dnowak

12/22/2021, 5:04 PM
Hi, I have a proposal for the partial application of leading arguments. The generated code forms a lovely Christmas Tree 🙂. https://github.com/arrow-kt/arrow/pull/2607
r

raulraja

12/22/2021, 9:57 PM
Hi @dnowak, Adding that many variants may be very expensive in terms of binary sizes and given the usage of partial application and currying in kotlin is almost non existent except for academic exercises we should probably look even at some other solution. I looked a bit into this issue and I came up with a small prototype that can be extended to fn22 or whatever appropriate that allows selective partial application of the arguments by turning the problem around https://gist.github.com/raulraja/5bca656eb545f936b99033516ad88a5d
all functions are FunctionN or Partial and when they have less arguments than the maximun arity the they are filled with
Unit
.
We can then selectively apply any argument at any position after capturing a function into its partial representation as shown in the gist
public fun divide(num: Int, den: Int): Int =
  num / den

public data class User(val id: Int, val name: String)

public fun createUser(name: String, id: Int): User =
  User(id, name)

public fun main() {
  val halfOf = Partial(::divide).second(2)
  val ten: Int = halfOf(20)

  val createUserJaneWithId = Partial(::createUser).first("Jane")
  val jane = createUserJaneWithId(ten)
  println(jane) //User(id=10, name=Jane)
}
We can apply any position of the function that we want partially applied:
Partial(::divide).second(2) //applies the second arg
val createUserJaneWithId = Partial(::createUser).first("Jane") // applies the first arg
Since
Partial
is a
fun interface
it is also a function that can be invoked
if you wanted to apply more than one argument at a time it could be adding an extra method like:
public operator fun invoke(a: A, b: B): Partial<Unit, Unit, C, D, Z> =
    Partial { _, _, c, d -> invoke(a, b, c, d) }
IMO Partial function application is still an inefficient technique in the JVM as we pay the penalty of allocations for wrapping functions but if its used as DI technique just on initialisation it should be fine
I think we should potentially also explore an untyped impl that casts the function and passes the args typed into an array so there is a single runtime for partial application instead of an exponential number of methods. it’s the best we can do until kotlin gets tuples and potentially tuple to fn args automatic conversions 🤞
d

dnowak

12/23/2021, 10:38 AM
Hi @raulraja, I just wanted to “optimize” the dependency injection phase. It would be a bit of runtime optimisation as
curried
or chain of
partially1
creates a bunch of functions.
Truly saying I have not done any measurements to find out what is the “penalty”.
I can live with what Arrow currently provides. This code is only used for binding dependencies and nobody complained on the performance 🙂. I just wanted to have a bit nicer code.
r

raulraja

12/23/2021, 10:55 AM
makes sense, I was hoping we can explore the problem further to find a solution without N functions, not so concerned about performance, but what we have now is also far from ideal in terms of ergonomics and also performance.
d

dnowak

12/23/2021, 2:01 PM
PR closed.
r

raulraja

12/23/2021, 2:07 PM
Thanks @dnowak, something I was planning down the road that could solve this is match types from scala3
if we had something similar as a plugin it’s possible to write n arity functions over tuples that are arg converted
it’s complex though, but something we may look into in 2022 if there is enough interest
d

dnowak

12/23/2021, 2:11 PM
Would be nice to have something like that. But as I said the only use-cases I have that require partial application of multiple parameters is application assembly and binding dependencies in tests.
👍 1