How can I `return` from inside of a call to `use`? - kotlin

In Kotlin, this code compiles:
private fun bar(): Boolean = TODO()
fun works(): Int {
while (true) {
if (bar()) {
return 5
}
}
}
(This is a pared down example of my real code to illustrate the issue I'm running into.)
I actually need to use a file during this loop, and close on exit:
fun openFile(): InputStream = TODO()
fun doesnt_work(): Int {
openFile().use { input ->
while (true) {
if (bar()) {
return 5
}
}
}
} // line 42
This doesn't compile. I get the error:
Error:(42, 5) Kotlin: A 'return' expression required in a function with a block body ('{...}')
I've found two ways to work around this, but both are kind of awkward.
One way is to use a variable to hold the result, and break from the loop right when it's set:
fun works_but_awkward(): Int {
openFile().use { input ->
val result: Int
while (true) {
if (bar()) {
result = 5
break
}
}
return result
}
}
This is especially awkward in my real code, as I have a nested loop, and so I need to use a labelled break.
The other way to work around this is to have a named function for the loop:
fun workaround_with_named_function(): Int {
fun loop(input: InputStream): Int {
while (true) {
if (bar()) {
return 5
}
}
}
return openFile().use { loop(it) }
}
This seems a bit better, but I'm still surprised that the use abstraction is so leaky that I can't do an early return from within a loop. Is there a way to use use with an early return in a loop that's less awkward?

Cause Kotlin compiler isn't smart enough to undestand that use with code inside will return something from the function. The reason of such behavior is inability to guarantee compiler that lambda will be called exactly once.
Another way to workaround this is throwing exception in the end of the function:
fun doesnt_work(): Int {
openFile().use { input ->
while (true) {
if (bar()) {
return 5
}
}
}
throw IllegalStateException("Something goes wrong")
}
P.S. I am not sure, but seems it can be compiled without any hacks when contract system will be added to Kotlin. And it is probably going to be in version 1.3

This should work.
fun openFile(): InputStream = TODO()
fun doesnt_work(): Int {
return openFile().use { input ->
while (true) {
if (bar()) {
return#use 5
}
}
-1 // unreachable return value
// just to help Kotlin infer the return type
}
}
Remember, use is a function whose return value is exactly the same with the return value of the lambda. So returning the value (here it's 5) in the lambda and return the return value of use should work.
Also, if I were you, I'll write the function like this:
fun doesnt_work() = openFile().use { input ->
while (true) if (bar()) return#use 5
-1
}

Related

Kotlin Flow two dependant request but return the first

I have two functions, the first one that returns a Result with a model, and the second one that returns a Result with another model.
fun flow1(): Flow<Result<Model1>>
fun flow2(id: String): Flow<Result<Model2>>
What i want is went the flow1() call is success, then do the flow2() call and some logic when is success but at the end return the flow1() result.
And for the moment i just trying something like this:
flow1().flatMapLatest { flow1Result ->
flow1Result.onSuccess {
flow2(it.id).map { flow2Result ->
flow2Result.onSuccess {
//Some logic
}
}
}.onFailure {
// return error
}
}
I have two problems the first one that inside the flatMapLatest give an error because say that i return a Result instead of a Flow. And how i can return the Flow1 result?
Thank you!
Trying something similar to this response Chain kotlin flows depends on Result state
I guess you need something like this
fun main() {
flow1().flatMapLatest { flow1Result ->
// should return flow with flow1Result element emited
flow1Result
.onSuccess {
// flow<flow1Result>, that we return
flow2(it.id).map { flow2Result ->
flow2Result
.onSuccess{
TODO("some logic")
}.onFailure{
// only emit flow1Result when flow2Result = Success
throw RuntimeError()
}
// convert flow<flow2Result> to flow <flow1Result>
flow1Result
}
}
.onFailure {
// there can be other flow<flow1Result>
throw RuntimeError()
}
}
}

How to execute a program with Kotlin and Arrow

I'm trying to learn a bit of Functional Programming using Kotlin and Arrow and in this way I've already read some blogposts like the following one: https://jorgecastillo.dev/kotlin-fp-1-monad-stack, which is good, I've understand the main idea, but when creating a program, I can't figure out how to run it.
Let me be more explicit:
I have the following piece of code:
typealias EitherIO<A, B> = EitherT<ForIO, A, B>
sealed class UserError(
val message: String,
val status: Int
) {
object AuthenticationError : UserError(HttpStatus.UNAUTHORIZED.reasonPhrase, HttpStatus.UNAUTHORIZED.value())
object UserNotFound : UserError(HttpStatus.NOT_FOUND.reasonPhrase, HttpStatus.NOT_FOUND.value())
object InternalServerError : UserError(HttpStatus.INTERNAL_SERVER_ERROR.reasonPhrase, HttpStatus.INTERNAL_SERVER_ERROR.value())
}
#Component
class UserAdapter(
private val myAccountClient: MyAccountClient
) {
#Lazy
#Inject
lateinit var subscriberRepository: SubscriberRepository
fun getDomainUser(ssoId: Long): EitherIO<UserError, User?> {
val io = IO.fx {
val userResource = getUserResourcesBySsoId(ssoId, myAccountClient).bind()
userResource.fold(
{ error -> Either.Left(error) },
{ success ->
Either.right(composeDomainUserWithSubscribers(success, getSubscribersForUserResource(success, subscriberRepository).bind()))
})
}
return EitherIO(io)
}
fun composeDomainUserWithSubscribers(userResource: UserResource, subscribers: Option<Subscribers>): User? {
return subscribers.map { userResource.toDomainUser(it) }.orNull()
}
}
private fun getSubscribersForUserResource(userResource: UserResource, subscriberRepository: SubscriberRepository): IO<Option<Subscribers>> {
return IO {
val msisdnList = userResource.getMsisdnList()
Option.invoke(subscriberRepository.findAllByMsisdnInAndDeletedIsFalse(msisdnList).associateBy(Subscriber::msisdn))
}
}
private fun getUserResourcesBySsoId(ssoId: Long, myAccountClient: MyAccountClient): IO<Either<UserError, UserResource>> {
return IO {
val response = myAccountClient.getUserBySsoId(ssoId)
if (response.isSuccessful) {
val userResource = JacksonUtils.fromJsonToObject(response.body()?.string()!!, UserResource::class.java)
Either.Right(userResource)
} else {
when (response.code()) {
401 -> Either.Left(UserError.AuthenticationError)
404 -> Either.Left(UserError.UserNotFound)
else -> Either.Left(UserError.InternalServerError)
}
}
}.handleError { Either.Left(UserError.InternalServerError) }
}
which, as you can see is accumulating some results into an IO monad. I should run this program using unsafeRunSync() from arrow, but on javadoc it's stated the following: **NOTE** this function is intended for testing, it should never appear in your mainline production code!.
I should mention that I know about unsafeRunAsync, but in my case I want to be synchronous.
Thanks!
Instead of running unsafeRunSync, you should favor unsafeRunAsync.
If you have myFun(): IO<A> and want to run this, then you call myFun().unsafeRunAsync(cb) where cb: (Either<Throwable, A>) -> Unit.
For instance, if your function returns IO<List<Int>> then you can call
myFun().unsafeRunAsync { /* it (Either<Throwable, List<Int>>) -> */
it.fold(
{ Log.e("Foo", "Error! $it") },
{ println(it) })
}
This will run the program contained in the IO asynchronously and pass the result safely to the callback, which will log an error if the IO threw, and otherwise it will print the list of integers.
You should avoid unsafeRunSync for a number of reasons, discussed here. It's blocking, it can cause crashes, it can cause deadlocks, and it can halt your application.
If you really want to run your IO as a blocking computation, then you can precede this with attempt() to have your IO<A> become an IO<Either<Throwable, A>> similar to the unsafeRunAsync callback parameter. At least then you won't crash.
But unsafeRunAsync is preferred. Also, make sure your callback passed to unsafeRunAsync won't throw any errors, at it's assumed it won't. Docs.

How To await a function call?

So I have some asynchronous operations happening, I can create some lambada, call a function and pass that value to them. But what i want is not to have the result of the operation as a parameter, I want to return them.
As a example, I have a class A with some listeners, if there is a result all listeners are notified. So basically the asyncFunction should return a result if there is one otherwise be suspended.
object A {
val listeners = mutableListOf<(Int) -> Unit>()
fun onResult(value: Int) {
listeners.forEach { it(value) }
}
}
fun asyncFunction(): Deferred<Int> {
return async {
A.listeners.add({ result ->
})
return result
}
}
What I'm thinking right now (maybe I'm completely on the wrong track), is to have something like a Deferred, to which i can send the result and it returns. Is there something like that? Can I implement a Deffered myself?
class A {
private val awaiter: ??? // can this be a Deferred ?
fun onResult(result: Int) {
awaiter.putResult(result)
}
fun awaitResult(): Int {
return awaiter.await()
}
}
val a = A()
launch {
val result = a.awaitResult()
}
launch {
a.onResult(42)
}
So I do know that with callbacks this can be handled but it would be cleaner and easier to have it that way.
I hope there is a nice and clean solution im just missing.
Your asyncFunction should in fact be a suspendable function:
suspend fun suspendFunction(): Int =
suspendCoroutine { cont -> A.listeners.add { cont.resume(it) } }
Note that it returns the Int result and suspends until it's available.
However, this is just a fix for your immediate problem. It will still malfunction in many ways:
the listener's purpose is served as soon as it gets the first result, but it stays in the listener list forever, resulting in a memory leak
if the result arrived before you called suspendFunction, it will miss it and hang.
You can keep improving it manually (it's a good way to learn) or switch to a solid solution provided by the standard library. The library solution is CompletableDeferred:
object A {
val result = CompletableDeferred<Int>()
fun provideResult(r: Int) {
result.complete(r)
}
}
suspend fun suspendFunction(): Int = A.result.await()

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.

It it possible to break from foreachline

is it possible to break from foreachline. my code :
fun test() {
bufferedReader.forEachLine {
val nameParam = it.split(":")[0]
if (name == "test")
return // here i wan to return from function
}
}
I've tried 'return#foreachline' but it just continue to next line
No, it's not: non-local returns are only supported for inline functions, and forEachLine { ... } is not an inline one, so you can only use return#forEachLine that exits the lambda.
An alternative that allows it is to read the lines first and then iterate over them:
bufferedReader.lines().use { lines ->
for (it in lines) {
val nameParam = it.split(":")[0]
if (name == "test")
break
}
}
Here, .use { ... } ensures that the lazy Stream created by .lines() is closed once it is not needed anymore.
Break and continue for custom control structures are not implemented yet.
You could use println().
The following simple hack works perfectly fine:
val fileToScann = File("file.txt")
fileToScan.forEachLine {
if( it.contains("12345") ) {
throw Exception("line found:"+it)
}
}
throw Exception("line not found")
}