5

I am having trouble with syntax for partial function application. The following code works fine, and it outputs: two-three-four

import kotlin.coroutines.experimental.*

inline fun <T> Iterable<T>.forEachFrom(beg:Int, act:(T)->Unit) {
  var i=0;  if (beg>=0) for (e in this) if (i++ >= beg) act(e)
}    // sample function I am testing; please don't change this!

fun main(a:Array<String>) {
  val l = listOf("zero", "one", "two", "three", "four")
  fun test() = buildSequence { l.forEachFrom(2) { yield(it) } }.joinToString("-")
  println(test())
}

I'd like to encapsulate my test(), so it is called as: test(l.forEachFrom(2)) However, I can't seem to get the types/syntax right. How would I re-write the test() function definition to make this possible?

duplode
  • 33,731
  • 7
  • 79
  • 150
sirksel
  • 747
  • 6
  • 19
  • But your `act` returns `Unit`, how could that `yield` gather something? – madhead Nov 08 '17 at 16:32
  • 2
    The biggest problem in your case that prevents me from arriving at an easy solution is that Kotlin coroutines can do suspend calls only from higher-order functions that are inlined, so you cannot just make a partially applied function by writing a lambda that works with the last argument and pass `{ yield(it) }` there: the compiler needs the place where you pass it to be an inline function. – hotkey Nov 08 '17 at 17:37
  • @hotkey Inline could be ok for these test procedures, but I'm still not sure what that syntax would look like. I tried, but I think I'm still not getting the types right. Could you show me what the syntax would look like if everything were inlined? – sirksel Nov 09 '17 at 04:12

1 Answers1

0

It's not entirely clear what you're trying to achieve. If you give more details we can provide a better answer. That said, the following code will hopefully point you in the right direction.

import kotlin.coroutines.experimental.*

inline fun <T,S> Iterable<T>.mapFrom(beg:Int, act:(T)->S) =
    drop(beg).map {act(it)}

fun main(a:Array<String>) {
    val l = listOf("zero", "one", "two", "three", "four")
    fun <T> test(values: List<T>) = values.joinToString("-")
    println(test(l.mapFrom(2) {it}))
}

forEachFrom was not really being used as a forEach, but as a map. This was the main issue, as you were using yield to output the results instead of just returning the results. If forEachFrom was also going to be used for its side-effects, it can still be used for that purpose.

Also, there was no reason for test to take a function as a parameter instead of just accepting the output of that function as its parameter. The buildSequence call was also redundant as you immediately call joinToString which is terminal and forces eager computation of the function inside buildSequence.

Steven Waterman
  • 611
  • 4
  • 17