Setup:
interface AProvider {
fun getA(): String
}
interface BProvider {
fun getB(): String
}
fun a(block: AProvider.() -> Unit) {}
fun b(block: BProvider.() -> Unit) {}
With this, I can nest the two functions as follows
val x = a { b { getA(); getB() } }
Now I would like to abstract this particular pattern to a higher level function so that I can pass the inner block and call both getA() and getB() in it, e.g. something like this:
val y = { l: ??? -> a { b(l) } }
val z = y { getA(); getB() }
The questions are
Is this possible with the proposed definition of y?
If yes, what should be the type of l?
If not, is there some other definition where I can get access to multiple dispatch receivers inside a lambda?
Note: This is related to my other SO question
You can do this with context receivers, which allows you to specify multiple receivers for one lambda. The type of y would be:
(context(AProvider, BProvider) () -> Unit) -> Unit
That is, a function that takes another function as a parameter, and returns Unit. The function parameter that it takes also returns Unit, but has AProvider and BProvider as its context receivers.
val y: (context(AProvider, BProvider) () -> Unit) -> Unit = { l ->
a { b { l(this#a, this#b) } }
}
val z = y { getA(); getB() }
Notice that when we call l, we pass the context receivers, this#a and this#b, as if they are regular parameters.
This makes z a Unit as well, which is kind of weird. y returns whatever a returns after all, so perhaps you did not intend a to return Unit.
Related
I have a function in Kotlin which takes a particular string as input. Depending on the input, I want to create a variable of a specific type and do some computations on it.
For example,
fun compute(input: String): Any{
if(input=="2d"){
var point: Point2D;// Points2D - x: int, y: int
//initilize and do some computations
return point.findDistanceFromOrigin()
}else if(input=="2d-1"){
var point: Point2DWithP1AsOrigin;// Point2DWithP1AsOrigin - x: int, y: int
//initilize and do some computations
return point.findDistanceFromOrigin()
}else if(input=="2d-2"){
var point: Point2DWithP2AsOrigin;
//initilize and do some computations
return point.findDistanceFromOrigin()
}
.
.
.
}
You can see in the above example, I want to initilize the type of point depending on the input and do computation and return.
All the if-else conditions have the same code except for the definition of the variable. How can I put all this in a single block with something like this:
var point: if(input=="2d) Point2D::class else if(input=="2d-1") Point2DWithP1AsOrigin::class.....
How can I do that?
You could do something like this
fun compute(input: String): Any{
val point: MyPoint = when(input) {
"2d" -> Point2D()
"2d-1" -> Point2DWithP1AsOrigin()
"2d-2" -> Point2DWithP2AsOrigin()
else -> Point2D() //fallback is necessary
}
//initilize and do some computations
return point.findDistanceFromOrigin()
}
But then it's essential that all those classes share the same interface. Because they need to have the same methods in order to do the same operations on them.
For example like this:
class Point2D : MyPoint {
override fun findDistanceFromOrigin() = 5
}
class Point2DWithP1AsOrigin : MyPoint{
override fun findDistanceFromOrigin() = 6
}
class Point2DWithP2AsOrigin : MyPoint{
override fun findDistanceFromOrigin() = 7
}
interface MyPoint {
fun findDistanceFromOrigin() : Int
}
You can store constructor references and then invoke required one
fun main() {
val constructors = mapOf(
"2d" to ::Point2D,
"2d-1" to ::Point2DWithP1AsOrigin,
"2d-2" to ::Point2DWithP2AsOrigin,
)
val type = "2d-2"
val constructor = constructors[type] ?: throw IllegalArgumentException("$type not supported")
val point = constructor()
println(point::class)
}
Output
class Point2DWithP2AsOrigin
I've looked at the source code of let function:
#kotlin.internal.InlineOnly
public inline fun <T, R> T.let(block: (T) -> R): R {
contract {
callsInPlace(block, InvocationKind.EXACTLY_ONCE)
}
return block(this)
}
i clearly understand that using it keyword inside of the block (code) i'm sending to
let function will refer to the object that called the let function.
But on which object does this keyword will refer if i will use it inside the block (code).
The way i see it, it should be the same object that it refer to, but i'm not sure.
My computer gone bad so i can't test it by myself.
Can someone not only give me the answer but also will explain me the logic behind the this keyword in the situation above?
example of code:
val p:Preson = Person()
p.let{**this**}
this in the let lambda (block) means the same thing as it does outside the let lambda. The let lambda does not suddenly "change" what this means.
For example:
class Foo {
fun foo() {
// here, "this" refers to the receiver of foo(), i.e. some instance of Foo
1.let {
// here, "it" refers to 1,
// and "this" refers to the receiver of foo() too
}
}
}
fun main() {
// here, "this" does not mean anything, main() has no receiver
2.let {
// here, "it" refers to 2,
// and "this" does not mean anything either
}
}
This is because this refers to the receiver of a function, but the function type of the parameter that let takes has no receiver:
public inline fun <T, R> T.let(block: (T) -> R): R {
^^^^^^^^
Compare that to run, which is very similar to let, except its receiver as the receiver of the block parameter:
public inline fun <T, R> T.run(block: T.() -> R): R {
return this.block() // instead of block(this) like let
}
which means that:
1.let {
// here, "this" refers to 1
// "it" does not mean anything because the function type T.() -> R has no parameters
}
Another example that uses both this and it:
fun usesBoth(x: Int, y: String, block: Int.(String) -> Unit) {
x.block(y)
}
If you call usesBoth with a lambda,
usesBoth(1, "x") {
println(this) // prints 1
println(it) // prints x
}
this in the lambda would refer to 1, because block is called on x in usesBoth, and it would refer to "x", because y is passed to block as a parameter.
this is just one of parameters (called a "receiver") that could be passed to the lambda by a calling code. If lambda doesn't support a receiver, as in the let example, this will be just this of the enclosing code:
class Foo {
fun foo() {
val p = Person()
p.let{ this } // `this` is `Foo`
}
}
If lambda has a receiver then it depends what will be passed to it as this. For example, a very similar function to let, but passing the object as this is apply:
class Foo {
fun foo() {
val p = Person()
p.apply{ this } // `this` is `Person`
}
}
Of course, this doesn't have to be the object that we invoked the function on. It could be just any object, it depends on what the function will pass to the lambda. For example, in Kotlin stdlib we have a buildString() function that instantiates a StringBuilder, runs the lambda passing the StringBuilder as this and then returns a string that was built by the lambda. We use this function like this:
val s = buildString {
// `this` is `StringBuilder`
append("hello") // implicit this.append()
append("world")
}
By looking into buildString() signature we can see that the lambda receives StringBuilder as its receiver:
public inline fun buildString(builderAction: StringBuilder.() -> Unit): String {
You can read more about lambdas with receivers in the documentation: https://kotlinlang.org/docs/lambdas.html
I'd like to have an applyif to work like:
builder.applyif(<condition expression>) {
builder.set...
}
to be equal with:
builder.apply {
if (<condition expression>) {
builder.set...
}
}
Is that possible?
Yes, of course. You can nearly program anything, but don't reinvent the wheel. Look at the bottom of the answer to see a standard Kotlin approach without own extension function(s) which may already suffice your needs (not exactly applyIf though).
Now, however, lets see how an applyIf might be implemented:
inline fun <T> T.applyIf(predicate: T.() -> Boolean, block: T.() -> Unit): T = apply {
if (predicate(this))
block(this)
}
Don't forget the inline if you are implementing extension functions with lambdas.
Here is an example usage of the above.
// sample class
class ADemo {
fun isTrue() = true
}
// sample usage using method references
ADemo().applyIf(ADemo::isTrue, ::println)
// or if you prefer or require it, here without
ADemo().applyIf( { isTrue() } ) {
println(this)
}
If you just want to supply a boolean instead, you can use the following extension function:
inline fun <T> T.applyIf(condition : Boolean, block : T.() -> Unit) : T = apply {
if(condition) block(this)
}
and call it with:
val someCondition = true
ADemo().applyIf(someCondition) {
println(this)
}
And now a possible Kotlin standard way with which more people could be familiar:
ADemo().takeIf(ADemo::isTrue)
?.apply(::println)
// or
ADemo().takeIf { it.isTrue() }
?.apply { println(this) }
If they do remember (I actually didn't until I saw Marko Topolniks comment) they should immediately know what's going on.
However, if you require the given value (i.e. ADemo()) after calling takeIf this approach might not work for you as the following will set the variable to null then:
val x = ADemo().takeIf { false }
?.apply { println(this) /* never called */ }
// now x = null
whereas the following will rather set the variable to the ADemo-instance:
val x = ADemo().applyIf(false) { println(this) /* also not called */ }
// now x contains the ADemo()-instance
Chaining the builder calls might not be so nice then. Still you can also accomplish this via standard Kotlin functions by combining the takeIf with apply or also (or with, let, run, depending on whether you want to return something or not or you prefer working with it or this):
val x = builder.apply {
takeIf { false }
?.apply(::println) // not called
takeIf { true }
?.apply(::println) // called
}
// x contains the builder
But then again we are nearly there where you were already in your question. The same definitely looks better with applyIf-usage:
val x = builder.applyIf(false, ::println) // not called
.applyIf(true) {
println(this) // called
}
// x contains the builder
Sure you can, you just need an extension function so you can call it on the builder, and you need it to take a Boolean parameter and the lambda to execute.
If you look at the source of the apply function itself, it will help with most of the implementation:
public inline fun <T> T.apply(block: T.() -> Unit): T {
block()
return this
}
Based on this, applyIf can be as simple as:
inline fun <T> T.applyIf(condition: Boolean, block: T.() -> Unit): T {
return if (condition) this.apply(block) else this
}
Usage looks like this:
builder.applyIf(x > 200) {
setSomething()
}
fun <T> T.applyIf(condition: Boolean, block: T.() -> T) = if (condition) block() else this
fun main() {
println("a".applyIf(true) { uppercase() }) // A
println("a".applyIf(false) { uppercase() }) // a
}
Suppose I want to have a function that checks whether a property value differs in two given objects. If so, a handler lambda should be executed.
fun <P>onChange(oldObj: P, newObj: P, select: (P) -> Any, handler: (P) -> Unit) {
val newValues = select(newObj)
val oldValues = select(oldObj)
if (oldValues != newValues) { handler(newObj) }
}
It works totally fine for the following use case:
data class TestObj(
val foo: String,
val bar: String? = null
)
val oldObj = TestObj(foo = "foo")
val newObj = TestObj(foo = "foo1", bar = "bar")
onChange(oldObj, newObj, { it.foo }) {
print("foo did change: ${it.foo}")
}
Now I want to be able to check if bar has changed.
If I try to to overload the function like
fun <P>onChange(oldObj: P, newObj: P, select: (P) -> Any?, handler: (P) -> Unit) {
...
}
the compiler throws an Duplicate method name&signature exception.
How do I overload the function or modify the signature in order to support a select lambda that is able to return an optional?
(P) -> Any? is a subtype of (P) -> Any, so if you just have
fun <P>onChange(oldObj: P, newObj: P, select: (P) -> Any?, handler: (P) -> Unit)
you can pass a (P) -> Any there. Of course, with overloading you can have two different implementations, but if behavior is actually different, this seems like a very bad idea :)
Change the name. They have the same JVM signature because nullable types don't exist at the byte code level. They are a Kotlin feature enforced at compile time.
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.