is there a more functional version of this typical...
# announcements
i
is there a more functional version of this typical "while loop with early exit"?
Copy code
fun foo(start: Foo): Result<List<MyRes>> {
    val list: MutableList<MyRes> = mutableListOf()
    var bar = start
    while (bar < condition) {
        when (val result = retrieveSomething(bar)) {
            is Success -> list.addAll(result.success)
            is Failure -> return Failure(Throwable("Error: ${result.error}"))
        }
        bar = bar.next()
    }
    return Success(list)
}
d
I would've suggested using
generateSequence
but that failure case is quite troublesome.
Copy code
generateSequence(start) { it.next() }
    .takeWhile { it < condition }
    .map { retrieveSomething(it) }
    .flatMap {
        when (it) {
            is Success -> it.success
            is Failure -> throw Throwable("Error: ${it.error}")
        }
    }
    .toList() // Catch exception here and wrap in Failure.
i
interesting. It's more functional (the throwing of the exception may be discussable)
but I'd probably not replace my version. It seems easier to understand.
s
I greatly prefer Dominic’s version, but I’m looking through ruby classes but I think idiomatic ruby is close to idiomatic kotlin
Glasses
i
I don't know, throwing an exception seems like cheating. But that's probably because I dislike throwing exceptions in general
and "catch exception here" means that I have to wrap the whole thing in a try block, right? Or is there a better way?
👌 1
d
This might work in theory.
Copy code
generateSequence(start) { it.next() }
    .takeWhile { it < condition }
    .map { retrieveSomething(it) }
    .asIterable()
    .flatMap {
        when (it) {
            is Success -> it.success
            is Failure -> return Failure(Throwable("Error: ${it.error}"))
        }
    }
i
ooh
well, I like this one haha
copying it 🙂 thanks!
d
Haha, don't thank me yet. It might not compile 😆 .
i
right, I noticed the "in theory" later
it seems not to work indeed. It says "return not allowed here"
d
Try
return@YourFunctionsName
.
i
though wait a sec
okay, it's compiling without the
@YourFunctionsName
. I just had to add a `
Copy code
.let { Success(it) }
at the end
so... awesome, thanks (this time for real) 🙂
(I'm not sure if this return without
@YourFunctionsName
is returning from the function or only flatmap though. Will have to confirm that. It also compiles with it)
d
It definitely can't be only flatmap because we'd need to return a
List<T>
to flatmap and we're not.
👍 1
d
You may be able to use `tailrec`:
Copy code
tailrec fun foo(start: Foo, prior: List<MyRes> = mutableListOf()): Result<List<MyRes>> {
        return if (start < condition) {
            when (val result = retrieveSomething(start)) {
                is Result.Success -> foo(start.next(), prior.apply { addAll(result.success) })
                is Result.Failure -> Failure(Throwable("Error: ${result.error}"))
            }
        } else {
            Result.Success(prior)
        }
    }
j
A slight improvement to Dominic's functional approach would be to have a sequence function with the signature
Copy code
fun <A> sequence(xs: List<Result<A>>): Result<List<A>>
This should capture the short circuiting and also replace the when expression. There's no reason to map the failure, you are only losing type information if you do it that way. I'm also curious if you are using
kotlin.Result
since the implementation doesn't currently like for it to be used as a return value. BTW, as an alternative to Result, arrow's Either has the short circuiting semantics that you want (including a sequence operation already defined), but learning how to actually use arrow properly has been challenging at least for me. I'll also mention the condition is a bit suspect. I realize the code is intentionally obfuscated, but remember to avoid referencing anything outside the function to keep it pure.