Quarktum
Quarktum

Reputation: 709

Kotlin - Composition of multiples IO

I'm new to Kotlin's Arrow Framework and I have a couple of questions:

Lets suppose

fun getUser(id: Int): IO<Option<User>>
fun getCards(user: User): IO<List<Card>>


fun getUserAndCards(id: Int): IO<Option<Pair<User, List<Card>>>> = IO.fx {
    when (val user = !userRepository.get(id)) {
        is None -> None
        is Some -> {
            val cards = !cardRepository.get(user.t.id)
            Some(Pair(user.t, cards))
        }
    }
}

How can I achieve the same functionality in an "arrow stylish" manner?

I manage to get:

fun getUserAndCards(id: Int): IO<Option<Pair<User, List<Card>>>> = IO.fx {
    userRepository.get(id).bind().map { user ->
        val cards = cardRepository.get(user.id).bind()
        Pair(user, cards)
    }
}

But I obtain Suspension functions can be called only within coroutine body in the second bind().

EDIT: I saw this post with the same question. In the answer provided, it says The problem is that the left/none option isn't covered. But IT IS covered, when applying map to a None it is expected to obtain a None.

Upvotes: 1

Views: 168

Answers (1)

nomisRev
nomisRev

Reputation: 2136

With the new 0.11.0 release coming up soon, the most idiomatic way would be to use Arrow Fx Coroutines.

Rewriting the example to Arrow Fx Coroutines would be:

suspend fun getUser(id: Int): Option<User>
suspend fun getCards(user: User): List<Card>


suspend fun getUserAndCards(id: Int): Option<Pair<User, List<Card>>> =
  option {
    val user = !userRepository.get(id)
    val cards = !cardRepository.get(user.t.id)
    Pair(user.t, cards)
  }

Where you can now rely on a option { } DSL to extract the values from Option.

The problem is that the left/none option isn't covered. But IT IS covered, when applying map to a None it is expected to obtain a None.

You're correct that it's covered, but ! is a suspending function, and map is currently not inlined so you're not allowed to call ! inside. In the 0.11.0 release the operators from the data types in Arrow-Core are inline, to improve support for suspend functions and this would solve the Suspension functions can be called only within coroutine body error.

In other functional languages such as Haskell monad transformers are often used (OptionT), but in Kotlin using suspend is a much better fit which also has quite some performance benefits over wrapping monad transfomers.

As mentioned in the other post, you can also always use traverse or sequence to turn two containers around. Option<IO<User>> -> IO<Option<User>>

Upvotes: 2

Related Questions