How to check method in when in kotlin - kotlin

I wrote the code below.
fun check(list){ list.forEach{ when(it){
is Int -> something()
is String -> something()
//is method -> ???
else -> consume{it}
}}}
inline fun consume(f: () -> Unit){ f() }
But I don't want to check the method in else.
Is there any way to check the method in when?

As far as I know you can just omit the else -> consume{it} part. forEach function's signature is as follows:
inline fun <T> Iterable<T>.forEach(action: (T) -> Unit)
See action. It takes a function with Unit as output. This means that you do not need to return anything (Unit is Java's void equivalent).
So, in conclusion your code can look like this:
fun check(list: List<*>) {
list.forEach {
when (it) {
is Int -> something()
is String -> something()
//is method -> ???
}
}
}

I asked the question, but I combined the contents of the replies and what I dealt with, and I attached the results that I was trying to do.
var arr = arrayOf{"String", {method()}}
fun check(arr : Array<Any?>?){ arr.forEach{ when(it){
is Int -> println("int")
is String -> println("str")
else -> #Suppress("UNCHECKED_CAST") (it as () -> Unit)()
}}}
thanks

Related

Kotlin - Automatically match overriden function type?

I'm trying to write a function that is essentially a wrapper method around some other functionality, for instance, some logging function.
I've tried several combinations of inline, generic, reified, etc., but nothing seems to work.
My function looks like this:
fun log(note: String, block: () -> Unit): () -> Unit {
print(note)
return block
}
My idea here is to perform some simple operation on the incoming note, and then just return that incoming function to be used as it was originally.
However, I want to do this around overridden functions like so:
override fun onClick(clicked: View) = log("Green Button") {
// here the regular onClick functionality goes
}
Here, I get an error "Return type is () -> Unit, which is not a subtype of overridden". This makes sense enough, as the function signatures do not match.
However, when I do this with other random functions:
fun test() = log("foo") { ... }
fun otherTest(a: String, b: Int) = log("bar") { ... }
I get no errors, and the interpreter somehow seems fine with this. I also tried looking at something like GlobalScope.launch to take that approach, but I couldn't figure it out.
Is what I'm trying to do possible? If not, is there something close?
I think
inline fun log(note: String, block: () -> Unit): Unit {
print(note)
return block()
}
should do what you want. It can be generalized to
inline fun <T> log(note: String, block: () -> T): T {
print(note)
return block()
}
I get no errors, and the interpreter somehow seems fine with this.
Why is that surprising? Those functions just return () -> Unit. If you do e.g.
fun test() = log("foo") { print("bar") }
then calling test() won't print bar; calling test()() will.
Tell me if my understanding is wrong. This is my approach
Extension function:
fun View.onClickWithLog(str: String, l: () -> Unit) {
setOnClickListener { Log.d("LogTag", str); run(l) }
}
Usage (from Activity):
btnTest.onClickWithLog("My Button String"){
Log.d("Actions from Activity", "Content")
finish()
}
and the output is
D/LogTag: My Button String
D/Actions from Activity: Content
which prints your note first, and execute the actions in the lambda expression.
When you use the = operator to assign something to a fun, the expression on the right hand side is supposed to return the return type of that fun
The original fun onClick(clicked:View) : Unit has return type Unit. When you write
override fun onClick(clicked:View) = ... , the ... is what you get when you call onClick(v) so it should be a Unit instead of a View -> Unit (Not even () -> Unit as in your code)
Take a simpler example. Let say you have fun sum(a:Int,b:Int) : Int. When you write override fun sum(a:Int,b:Int) = ... , ... must be an Int instead of a (Int,Int) -> Int since you expect to get an Int immediately when you call sum(a,b). If you somehow got a let say
val someOtherWayToSum : (Int,Int) -> Int = {...}
and want to use it, you can write
override fun sum(a:Int,b:Int) = someOtherWayToSum(a,b)
In your case, you better just do
override fun onClick(clicked:View){
/* some operation (e.g your log)*/
/* the regular onClick functionality */
}
since you are overriding it and implementing its regular functionality right there anyway.

How can you call different versions of similar extension methods with kotlin

I have the following functions to simulate the ternary operator for kotlin
fun Boolean.then(action: () -> Unit): Boolean {
if (this)
action.invoke()
return this
}
fun Boolean.otherwise(action: () -> Unit) {
if (!this)
action.invoke()
}
fun <T> Boolean.then(func: () -> T): T? {
if (this)
return func.invoke()
return null
}
fun <T> T?.otherwise(action: () -> T): T {
return this ?: action.invoke()
}
they are supposed to be used like this :
(check).then { doHello() }.otherwise { doWorld() }
val answer = (check).then { "hello" }.otherwise { "world" }
however when I try to assign a value using the above operators like this:
val visibility: Int = (show).then { View.VISIBLE }.alt { View.GONE }
I get an error saying that the required reply was Int but it actually got Unit which means that it called the first version of the methods instead of the second
Other than renaming the methods (when I changed the first two to thenDo and otherwiseDo it worked), can I write the above code in some way so that the compiler will know to call the second version?
I don't think you need both overloads. If you remove the ones that return Unit, then both your lines of code work:
(check).then { doHello() }.otherwise { doWorld() }
val answer = (check).then { "hello" }.otherwise { "world" }
That's because the first line, where the lambdas return Unit, e.g. doHello(), can still use the generic versions of then and otherwise, as they are still considered functions with a return value, namely Unit.
Although I agree with some the comments above: do you really need this? Why not just use if, which is an expression which returns a value (like the ternary operator). See discussion here for more info.

How can I create a "forEach" that returns an object that is a receiver to the consuming function?

I'm trying to do something like this in a long chain of "stream" operations.
fun main(args: Array<String>) {
"test1, test2, test3".split(", ")
.toCustomString(StringBuilder(), StringBuilder::append)
}
fun <T, R>Iterable<T>.toCustomString(obj: R, thing: R.(T) -> Unit): R {
this.forEach {
obj.thing(it)
}
return obj
}
But this doesn't work it says none of the functions found for StringBuilder::append can't be applied here. Is there a way I can make something like this work?
You are trying to use a method reference with a different signature for a receiver function. You can make it work with supplying a lambda instead. Or as other answers point out, changing the signature of your receiver function.
fun main(args: Array<String>) {
"test1, test2, test3".split(", ")
.toCustomString(StringBuilder(), { item -> append(item) })
}
There's no problem to use a method reference in that case and it should work perfectly.
Just ensure you use kotlin class StringBuilder an change this:
fun <T, R>Iterable<T>.toCustomString(obj: R, thing: R.(T) -> Unit)
by this one:
fun <T, R>Iterable<T>.toCustomString(obj: R, thing: R.(T) -> R)
In order to use StringBuilder::append as a function reference, thing should have type R.(T) -> R instead of R.(T) -> Unit because StringBuilder.append(String) will return StringBuilder. However, since toCustomString does not handle the return value from StringBuilder.append, what #Marko suggested is more appropriate.
FYR, here is an alternative way to do it without an extra extension function:
val customString = StringBuilder().apply {
"test1, test2, test3"
.split(", ")
.forEach { this.append(it) }
}

Discard return value to return Unit?

I am just starting with kotlin, so, forgive me if this is a basic question, I did do some googling, but that didn't turn up anything useful.
The question is how do I convert a value to Unit.
For example, in scala, if I write something like this:
def foo: Int = ???
def bar(x: String): Unit = x match {
case "error" => println("There was an error")
case _ => foo
}
The return type of the match expression is Any, but it is discarded by the compiler and Unit is returned by the function.
But doing something like this in kotlin:
fun bar(x: String): Unit = when(x) {
"error" -> println("There was an error")
else -> foo()
}
it complains about the foo part: inferred type is Int but Unit was expected
I know, that in this case, I can just get rid of the =, and put the body inside a block instead, that works, but I am looking for a general solution. What I was able to come with so far is just foo.let {}, but it seems kinda clumsy, especially if there are many cases like this where it needs to be done.
You can create an extension method on Any object and call it. I just prefer to use the name discard() rather than toUnit(), since I feel it conveys better the intent:
fun Any?.discard() = Unit
fun foo(): Int = 3
fun bar(x: String): Unit = when (x) {
"error" -> println("There was an error")
else -> foo().discard()
}
There's no way to do that out of the box, but you can make an extension function for this:
fun Any?.unit() = Unit
Then use it as:
fun bar(x: String): Unit = when(x) {
"error" -> println("There was an error")
else -> foo().unit()
}
Alternatively, make when a statement and not an expression:
fun bar(x: String) {
when(x) {
"error" -> println("There was an error")
else -> foo()
}
}
There are three solutions to your problem, which come to my mind:
Make when a statement instead of expression, so its result does not get returned:
fun bar(x: String) {
when(x) {
"error" -> println("There was an error")
else -> foo()
}
}
Use an extension to convert values to Unit:
fun Any?.asUnit() = Unit
Usage:
fun bar(x: String): Unit =
when (x) {
"error" -> println("There was an error")
else -> foo().asUnit()
}
Wrap the call into higher-order function call that returns Unit, e.g. with:
fun bar(x: String): Unit = with(x){
when (x) {
"error" -> println("There was an error")
else -> foo()
}
}

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.