Discard return value to return Unit? - kotlin

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()
}
}

Related

Kotlin - Infer type for one of two generic parameters

I am trying to create a function that has two generic types: one reified, and another derived from the context of its usage (since it is an extension function):
inline fun <reified E, A> Either<Throwable, A>.bypassLeft(transformation: Throwable.() -> A): Either<Throwable, A> =
when (this) {
is Either.Left -> when (value) {
is E -> value.transformation().right()
else -> this
}
else -> this
}
The idea would be to call the function just mentioning the reified type, something like:
a.bypassLeft<NoResultException> { "" }
In which "a" is an object of type Either<Throwable,String>
But the compiler is not letting me go away with it, and requires me to specify both generic types, instead of deriving the second one form the object calling the function.
It seemed quite a reasonable thing to be possible, but maybe I am wrong...
Is this possible to achieve? If so, what am I doing wrong?
It's not currently possible with a function to ascribe a single type argument and leave the other inferred. You can achieve what you want if you type the lambda arguments by changing your implementation to not use a receiver type.
I threw in there an additional impl that shows how type args can also be partially applied with a class or other surrounding scope.
import arrow.core.Either
import arrow.core.right
inline fun <reified E : Throwable, A> Either<Throwable, A>.bypassLeft(
transformation: (E) -> A //changed to regular arg not receiver
): Either<Throwable, A> =
when (this) {
is Either.Left -> when (val v = value) { //name locally for smart cast
is E -> transformation(v).right()
else -> this
}
else -> this
}
class Catch<A>(val f: () -> A) { //alternative impl with partial type app
inline fun <reified E : Throwable> recover(
recover: (E) -> A
): Either<Throwable, A> =
Either.catch(f).fold(
{
if (it is E) Either.Right(recover(it))
else Either.Left(it)
},
{
Either.Right(it)
}
)
}
suspend fun main() {
val x: Either<Throwable, Int> = Either.Left(StackOverflowError())
val recovered = x.bypassLeft {
s: StackOverflowError -> //here infers E
0 // here infers A
}
println(recovered) // Either.Right(0)
val notRecovered: Either<Throwable, Int> =
Catch {
throw NumberFormatException()
1
}.recover<StackOverflowError> { 0 }
println(notRecovered) // Either.Left(java.lang.NumberFormatException)
}
This is possible as of Kotlin v1.7.0 with the underscore operator.
The underscore operator _ can be used for type arguments. Use it to automatically infer a type of the argument when other types are explicitly specified:
interface Foo<T>
fun <T, F : Foo<T>> bar() {}
fun baz() {
bar<_, Foo<String>>() // T = String is inferred
}
In your example, it would be possible like this:
a.bypassLeft<NoResultException, _> { "" }

Kotlin extension function - compiler cannot infer that nullable is not null

Let's say I have a simple class Foo with a nullable String?
data class Foo(
val bar: String?
)
and I create a simple function capitalize
fun captitalize(foo: Foo) = when {
foo.bar != null -> runCatching { foo.bar.capitalize() }
else -> ""
}
which works fine, because the compiler infers that foo.bar cannot be null eventhough it's type is nullable. But then I decide to write the same function as an extension of Foo
fun Foo.captitalize2() = when {
bar != null -> runCatching { bar.capitalize() }
else -> ""
}
and all of a sudden the compiler is no longer able to infer that bar is not null, and IntelliJ tells me that "only safe (?.) or non-null asserted (!!.) calls are allowed on a nullable reciever of type String?"
Can anyone explain why?
I think it's because in the first case you are calling this function:
public inline fun <R> runCatching(block: () -> R): Result<R> {
return try {
Result.success(block())
} catch (e: Throwable) {
Result.failure(e)
}
}
but in the second case you are calling function with receiver:
public inline fun <T, R> T.runCatching(block: T.() -> R): Result<R> {
return try {
Result.success(block())
} catch (e: Throwable) {
Result.failure(e)
}
}
For me, it looks like an issue in the Kotlin compiler because if you inline code of this function by yourself it will work fine:
fun Foo.captitalize2() = when {
bar != null -> try {
Result.success(bar.capitalize())
} catch (e: Throwable) {
Result.failure<String>(e)
}
else -> ""
}
btw, if I were you I would like to write my capitalize2 function like this :)
fun Foo.captitalize2() = bar?.capitalize() ?: ""
So, finally I found an alternative approach that allows us to use runCatching without having the problem you shows.
As in my comment to the answer of #Andrei Tanana, in your code type parameters of fun <T, R> T.runCatching(block: () -> R) : Result<R> are inferred as <Foo, String> and the compiler can't use the information that this.bar is not null.
If you rewrite the capitalize2 function as follows
fun Foo.capitalize2(): Serializable = when {
bar != null -> bar.runCatching { capitalize() }
else -> ""
}
T is inferred as String (thanks of the bar != null case of the when expression) and the compiler does not complain about this.capitalize() invocation in the block passed to runCatching.
I hope this can help you, both as an approach than allows you to solve the problem and as explanation of the problem itself.

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 to check method in when in 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

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.