https://kotlinlang.org logo
#announcements
Title
# announcements
n

nfrankel

07/09/2019, 8:20 PM
hi dears kotliners my issue of the day: how can i have a higher-order function that takes a vararg? something like:
Copy code
fun foo( (vararg args: Any?) -> Unit) = ...
s

Shawn

07/09/2019, 8:26 PM
vararg
isn’t (yet?) supported on lambda parameters, you’ll have to use an anonymous
fun
as for mandating that as a parameter type… I think
Array<Any?>
might technically be what it is under the hood but I doubt the compiler considers the signatures compatible
n

Nicholas Bilyk

07/09/2019, 8:28 PM
Shawn is correct, it would be KFunction1<Array<out Any?>, Unit> under the hood.
Copy code
fun vArgFunc(vararg t: String) {
	println("All ${t.joinToString()}")
}

fun t() {
	val z: KFunction1<Array<out String>, Unit> = ::vArgFunc
	z(arrayOf("one", "two", "three"))
}
t

tseisel

07/09/2019, 8:30 PM
As a workaround, you can use the following signature :
Copy code
fun foo(f: (Array<out Any?>) -> Unit)
, which is equivalent, and then simulate the vararg parameters with
arrayOf(...)
.
n

nfrankel

07/09/2019, 8:37 PM
thanks guys for your super quick answer the array trick doesn’t in my case as i want to genericize the following code:
Copy code
var sort2 = fun(frequencies: Map<String, Int>) = frequencies
    .map { it.key to it.value }
    .sortedByDescending { it.second }

fun profile(f: (Map<String, Int>) -> List<Pair<String, Int>>): (Map<String, Int>) -> List<Pair<String, Int>> {
    val profilewrapper = fun(arg: Map<String, Int>): List<Pair<String, Int>> {
        val start = System.currentTimeMillis()
        val result = f.invoke(arg)
        val elapsed = start - System.currentTimeMillis()
        print("${f.toString()} took $elapsed secs")
        return result
    }
    return profilewrapper
}

fun main() {
    sort2 = profile(sort2)
}
so that
profile()
accepts a function that takes a
vararg
and return
Any
n

Nicholas Bilyk

07/09/2019, 8:47 PM
ah I see. You'll need profile1, profile2, profile3 etc for the number of arguments you'll want to parameterize
Copy code
fun <P1, R> profile1(f: (P1)->R)): (P1) -> R { ... }
fun <P1, P2, R> profile2(f: (P1, P2)->R)): (P1, P2) -> R { ... }
n

nfrankel

07/09/2019, 8:51 PM
i thought about that @Nicholas Bilyk but i’d be happy to have the vararg stuff if i can though from your answer, it seems it’s not possible
n

Nicholas Bilyk

07/09/2019, 8:52 PM
nope. vararg is just sugar around accepting an array parameter. You're thinking of something like ecmascript's arguments array, which Kotlin doesn't support
n

nfrankel

07/09/2019, 8:52 PM
many thanks for your help!
n

Nicholas Bilyk

07/09/2019, 8:52 PM
np 🙂
what if you changed it instead of profile(p1, p2) to profile { sort2(frequencies) }
Copy code
var sort2 = fun(frequencies: Map<String, Int>) = frequencies
    .map { it.key to it.value }
    .sortedByDescending { it.second }

inline fun <R> profile(inner: () -> R): R {
    
        val start = System.currentTimeMillis()
        val result = inner()
		val elapsed = start  -System.currentTimeMillis()
       
        print("took $elapsed millisecs")
        return result
  
}

fun main(args: Array<String>) {
    val sorted = profile { sort2(mapOf()) }
}
n

nfrankel

07/09/2019, 9:16 PM
🤔 it’s not a real project so my constraint is to be able to replace the
sort2
variable with one wrapped by
profile()
otherwise, yes, good point
2 Views