Reputation: 709
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
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