How to combine variable argument definitions and function with receiver in Kotlin - kotlin

In Kotlin I am able to define a function that accepts a variable number of arguments (see: testVariableArguments below) and I can define a function with a specified receiver (see: testFunctionWithReceiver below). I am wondering if there is a way to combine both of these concepts?
fun main(args: Array<String>) {
testVariableArguments { a: Int -> println(a) }
testVariableArguments { a: Int, b: Int -> println("$a, $b") }
testVariableArguments { a: Int, b: Int, c: Int -> println("$a, $b, $c") }
testVariableArguments { a: Int, b: Int, c: Int, d: Int -> println("$a, $b, $c, $d") }
testFunctionWithReceiver {
doSomething()
doAnotherThing()
}
}
fun <R, T : Function<R>> testVariableArguments(function: T) {
val method = function::class
.java
.declaredMethods
// May need to do something else here to get the
// correct method in case the return type is
// expected to be Object, but for my case it
// would never be Object
.first { m -> m.name == "invoke" && m.returnType != Object::class.java }
val args = method
.parameterTypes
// Mapping to Int here for demonstration, in real
// situations would use the parameter types to
// create the correct value
.withIndex()
.map { i -> i.index }
// Not really needed, but would be if I were
// using multiple types and not just Int
.map { i -> i as Any }
.toTypedArray()
method.invoke(function, *args)
}
fun <R> testFunctionWithReceiver(function: MyInterface.() -> R) {
val myObject = object : MyInterface {
override fun doSomething() {
println("doing something")
}
override fun doAnotherThing() {
println("doing another thing")
}
}
function(myObject)
}
interface MyInterface {
fun doSomething()
fun doAnotherThing()
}
EDIT:
I have found a way to combine these two features, but it is a bit messy at the call site, so if there is a better approach I would be interested.
What I did was add the following operator function to MyInterface
operator fun <R, T : Function<R>> T.unaryPlus() {
testVariableArgumentDefinition(this)
}
Then when I call testFunctionWithReceiver I do the following:
testFunctionWithReceiver {
+{ a: Int, b: Int ->
println("$a, $b")
doSomething()
doAnotherThing()
}
}

You can add parameter requirements to the closure called with receiver by adding:
MyInterface.(P) -> R
Replacing your creation of test parameters with a fixed one, this is what it might look like:
fun <R, P> testVariableArgumentsWithReceiver(param: P, function: MyInterface.(P) -> R) {
function.invoke(myObject, param)
}
fun main(args: Array<String>) {
testVariableArgumentsWithReceiver(17) { a: Int ->
println("$a")
doSomething()
doAnotherThing()
}
}
Of course, you're not as flexible here as you need to pass a single value of type P (which could be an array). You can extend it to MyInterface.(P,Q) but not arbitrary signatures per se.
What you truly want is a signature like this:
fun <R, T: Function<R>> someName(function: MyInterface.T)
or
fun <R, T: FunctionWithReceiver<MyInterface, R>> someName(function: T)
To my knowledge, neither can currently be expressed:
Only function-type literals seem to be allowed as part of extension types; MyInterface.T is not valid code.
There doesn't seem to be a first-class type for functions with receiver; we can't declare FunctionWithReceiver.
This may be worthwhile to bring up on discuss.kotlinlang.org

Related

How to a class with generics with an inline refied function

If I have a class like the following:
class SimpleClass {
var test: Int = 1
}
I can do something like this:
inline fun <reified T : SimpleClass> myFunction1(): T {
//Do something with T
}
But if I have a class with generics:
class ComplexClass<T> {
var test: T? = null
}
How can I do the same?
The following doesn't work:
inline fun <reified T : ComplexClass<C>> myFunction2(): T {
//Do something with T
//Do something with C
}
How can I do it?
Here is a more complex example, if needed.
Let's take a look at your example
class Mediator {
//It does't work! My question is about this function.
inline fun <reified T : Container<C>> mediate(string: String): T {
if (C::class == Child1::class)
//Do something
if (C::class == Child2::class)
//Do something else
}
}
class UseExample : Mediator() {
fun example1(): Container<Child1> {
return mediate("test1") // your mediate function does not take
// any parameters to determine the generic type
}
fun example2(): Container<Child2> {
return mediate("test2")
}
}
To perform something with the type C which is used to create Container<C> and perform something with the result type which you represent as T : Container<C> you only need to know the C. Since reified can only be used to keep the type if it is known during the compile time at the call site, rewrite your function signature like this.
inline fun <reified C> mediate(string: String): Container<C> {
// now you know the C and you know the T which is Container<C>
if (C::class == Child1::class) ...
// Since T depends on C, you can only check the C type to perform logic
}
Use it like following
fun example1(): Container<Child1> {
return mediate<Child1>("test") // Note that know your C
// type is known at the function call therefore compiler
// can substitute it with exact type
}
Here is my minimal example from Kotlin Playground
class Container<T>(t: T) {
val smth : T = t
}
class Mediator {
inline fun <reified C> mediate(string: String): Container<C> {
if (C::class == Int::class) {
println("Int")
return Container<Int>(1) as Container<C>
}
throw IllegalStateException("Yopta")
}
}
fun main() {
val m = Mediator()
m.mediate<Int>("ABC") // Output is "Int"
}

Kotlin functions inside a const

I am learning kotlin, and want to know how to add functions to a const,
here the JS example...
function suma (a, b){
return a + b
}
console.log("sua suma::", suma(2,3))
function multiplica (a, b){
return a * b
}
console.log("sua multiplik::", multiplica(2,3))
const operations = {
suma,
multiplica
}
console.log ("sum of first class::", operations.suma(2,3))
console.log ("mult of first class::", operations.multiplica(2,3))
so how do I achieve the same on Kotlin?
Here what I have tried:
fun suma(a: Int, b: Int): Int{
return a + b
}
fun multiplica (a: Int, b: Int): Int{
return a * b
}
const operations = {
suma(),
multiplica()
}
fun main() {
println("sua suma:: ${suma(2, 3)}")
println("sua multiplica:: ${multiplica(2, 3)}")
println("sua multiplica:: ${operations.multiplica(2,3)}")
}
It can be done using object keyword, like following:
fun suma(a: Int, b: Int): Int {
return a + b
}
fun multiplica(a: Int, b: Int): Int {
return a * b
}
fun main() {
val operations = object {
val _suma = ::suma
val _multiplica = ::multiplica
}
println("sua suma:: ${operations._suma(2, 3)}")
println("sua multiplica:: ${operations._multiplica(2, 3)}")
}
The only disadvantage is that you have to choose a name for operations.XXXXX that is different from the name of referenced function: note var _suma vs fun suma. Otherwise the compiler will consider it recursive problem
"Only Primitives and String are allowed" for const
The operator you're looking for is :: (Double colon)
An enum example of this is
enum class Operations(function: KFunction<Int>) {
Suma(function = ::suma),
Multiplica(function = ::multiplica)
}
The main issues here are that Kotlin is a statically typed language, and doesn't have the same idea of Objects that can contain arbitrary attributes, like Javascript does.
I'm kinda new to Kotlin, so there may be a better way to do this, but what I'd do is define a Map to do the same sort of thing:
fun suma(a: Int, b: Int): Int{
return a + b
}
fun multiplica (a: Int, b: Int): Int{
return a * b
}
val operations: Map<String, (a: Int, b:Int) -> Int> = hashMapOf(
"suma" to ::suma,
"multiplica" to ::multiplica)
fun main() {
println("sua suma:: " + operations.get("suma")?.invoke(2, 3))
println("sua multiplica:: " + operations.get("multiplica")?.invoke(2, 3))
}
Result:
sua suma:: 5
sua multiplica:: 6
Note that operations is an immutable Map ('const' kinda) in that its contents can't be changed once it's defined.
The access and having to use invoke seems kinda messy. This seems to be due to the fact that a Map can only contain nullable types. That's the main reason I think there's probably a better way to do this.

Function that takes variable number of typed args and a closure with same number of typed args?

I'd like to make a function that takes a variable number of arguments of different types, and a closure, and call the closure with the same number of arguments, each corresponding to a type in the original argument list:
fun <A, B, ...>mergeWhenValid(
arg1: Either<Problem, A>,
arg2: Either<Problem, B>,
...,
closure: (A, B, ...) -> T
): Either<Problem, T> {
// do stuff and call closure(a, b, ...)
}
How might I accomplish this?
If your mergeWhenValid just returns closure result if all eithers are right and firstProblem.left() otherwise, you should use Either.fx<Problem, T> instead of your function. Example:
Either.fx<Problem, String> { "${eitherInt.bind()} ${eitherDouble.bind()} ${eitherFloat.bind()}" }
If your logic is more complex and you need somehow handle all eithers, you can do it either by creating special merging DSL:
fun <R> mergeWhenValid(block: MergeWhenValidScope.() -> R): R = MergeWhenValidScope().block()
class EitherProblem<out T>(internal val either: Either<Problem, T>)
class MergeWhenValidScope {
private val eithers = mutableListOf<Either<Problem, *>>()
operator fun <T> Either<Problem, T>.component1(): EitherProblem<T> {
eithers += this
return EitherProblem(this)
}
private fun doStuff(): Option<Problem> {
// you can use `eithers` here and choose one of their problems or create a new one
// if you return None, it will assume that all `eithers` are right,
// otherwise, problem will be wrapped in Either and returned
return eithers.asSequence().mapNotNull { it.swap().getOrElse { null } }.firstOption()
}
fun <R> combine(block: CombinerScope.() -> R): Either<Problem, R> =
doStuff().map { it.left() }.getOrElse { CombinerScope.block().right() }
object CombinerScope {
operator fun <T> EitherProblem<T>.invoke() = either.getOrHandle {
error("Unexpected problem $it")
}
}
}
Use case:
mergeWhenValid {
val (int) = eitherInt
val (double) = eitherDouble
val (float) = eitherFloat
combine { "${int()} ${double()} ${float()}" }
}
Or by pipelining functions which add all your eithers to some object:
fun <T> mergeWhenValid() = MergeWhenValidInit<T>()
class MergeWhenValidInit<T> {
operator fun <A> invoke(either: Either<Problem, A>): MergeWhenValid<A, T, T> =
MergeWhenValid(either, listOf(either)) { it }
}
class MergeWhenValid<A, B, C>(
private val either: Either<Problem, A>,
private val eithers: List<Either<Problem, *>>,
private val previous: (B) -> C // is allowed to be called only if all `eithers` are right
) {
private fun doStuff(): Option<Problem> {
// you can use `eithers` here and choose one of their problems or create a new one
// if you return None, it will assume that all `eithers` are right,
// otherwise, problem will be wrapped in Either and returned
return eithers.asSequence().mapNotNull { it.swap().getOrElse { null } }.firstOption()
}
operator fun invoke(block: (A) -> B): Either<Problem, C> =
doStuff().map { it.left() }.getOrElse { requireC(block).right() }
operator fun <D> invoke(either: Either<Problem, D>): MergeWhenValid<D, (A) -> B, C> =
MergeWhenValid(either, eithers + either) { next -> requireC(next) }
private fun requireC(next: (A) -> B): C = previous(next(either.getOrHandle {
error("Unexpected problem $it")
}))
}
Use case:
mergeWhenValid<String>()(eitherInt)(eitherDouble)(eitherFloat)() { float ->
{ double -> { int -> "$int $double $float" } }
}
Note: the last approach reverses the order of arguments and also forces you to write { c -> { b -> { a -> ... } } } instead of { c, b, a -> ... }.

Is it possible to write a "double" extension method?

In Kotlin, it is possible to write
class A {
fun B.foo()
}
and then e.g. write with (myA) { myB.foo() }.
Is it possible to write this as an extension method on A, instead? My use case is writing
with (java.math.RoundingMode.CEILING) { 1 / 2 }
which I would want to return 1, the point being that I want to add operator fun Int.div(Int) to RoundingMode.
No it's not possible. operator div is required to have Int as a receiver.
You can't add also RoundingMode as receiver, since there can only be single function receiver.
What you can do, though, is use Pair<RoundingMode, Int> as a receiver:
operator fun Pair<RoundingMode, Int>.div(i: Int): BigDecimal =
BigDecimal.valueOf(second.toLong()).divide(BigDecimal.valueOf(i.toLong()), first)
with(RoundingMode.CEILING) {
println((this to 1) / 2) // => 1
}
That's not possible, Int already has a div function, thus, if you decide to write an extension function div, you won't be able to apply it, because member functions win over extension functions.
You can write this though:
fun RoundingMode.div(x: Int, y: Int): Int {
return if (this == RoundingMode.CEILING) {
Math.ceil(x.toDouble() / y.toDouble()).toInt()
} else {
Math.floor(x.toDouble() / y.toDouble()).toInt()
}
}
fun main(args: Array<String>) {
with(java.math.RoundingMode.CEILING) {
println(div(1,2))
}
}
It's not possible for a couple of reasons:
There's no "double extension functions" concept in Kotlin
You can't override a method with extension functions, and operator div is already defined in Int
However you can workaround these issues with
A context class and an extension lambda (e.g. block: ContextClass.() -> Unit)
Infix functions (e.g. use 15 div 4 instead of 15 / 4)
See the example below:
class RoundingContext(private val roundingMode: RoundingMode) {
infix fun Int.div(b: Int): Int {
val x = this.toBigDecimal()
val y = b.toBigDecimal()
val res = x.divide(y, roundingMode)
return res.toInt()
}
}
fun <T> using(roundingMode: RoundingMode, block: RoundingContext.() -> T): T {
return with(RoundingContext(roundingMode)) {
block()
}
}
// Test
fun main(args: Array<String>) {
using(RoundingMode.FLOOR) {
println(5 div 2) // 2
}
val x = using(RoundingMode.CEILING) {
10 div 3
}
println(x) // 4
}
Hope it helps!

Is there a less ugly way to return function in Kotlin?

This declaration works, but is not the most beautiful code. Is there a way to return functions less ugly? I tried (s: String) -> writer.println(s) but this didn't work.
val writeStuff: (PrintWriter) -> (String) -> Unit = {
val writer = it
val f: (String) -> Unit = {
writer.println(it)
}
f
}
PrintWriter("test").use { writeStuff(it)("TEST") }
EDIT: a bit more concrete example:
val writeStuff: (PrintWriter) -> (String) -> Unit = { writer ->
{ writer.println(it) }
}
val sendStuff: (Any) -> (String) -> Unit = { sender ->
{ sender.equals(it) }
}
#Test fun test1() {
val li = listOf("a", "b", "c")
val process: List<(String) -> Unit> =
listOf(writeStuff(PrintWriter("a")), sendStuff(Object()))
process.map { li.map(it) }
}
First, you can simplify your code using lambda syntax with explicit parameter and inlining val f:
val writeStuff: (PrintWriter) -> (String) -> Unit = { writer ->
{ writer.println(it) }
}
But since Kotlin supports local function declarations, you can even make writeStuff a local fun instead of a val.
This would lead to the following code:
fun writeStuff(writer: PrintWriter): (String) -> Unit {
return { writer.println(it) }
}
Or, using the single expression syntax,
fun writeStuff(writer: PrintWriter): (String) -> Unit = { writer.println(it) }
The usage, however, will be the same:
PrintWriter("...").use { writeStuff(it)("...") }
I stumbled across this question while trying to figure out how to return a Function (the java interface) in Kotlin. While this doesn't directly answer the question, hopefully it'll help someone else who has the same query:
override fun myFun(param1: Object): Function<in Object, out String?> {
if (!param1.meetsCriteria())
return Function { obj -> null }
return Function { obj ->
"success"
}
}
In this case, I was overriding a method in a java interface that required me to return a Function instance. (Note that since the param is not used in my particular implementation above, I could remove it and just have the return result. eg return Function { null })
Edit: After some research, it turns out Kotlin covers this subject with their discussion on "SAM (single abstract method) conversions" here and here, though it may not be the most intuitive thing to look up when figuring out how to return Functions.