noobie question here, what's the recommended way t...
# arrow
t
noobie question here, what's the recommended way to combine different monads. Say you have 3,4 methods each returning different Kind<F,A>, what's the best syntax to merge them to another Kind?
b
You mean if you have Kind<ForOption, A> and Kind<ForIO, A>, for instance?
t
yup
b
Usually composition is accomplished by parameterizing over the
F
- that is, leaving everything as
Kind<F, A>
and providing the appropriate (typeclass) lower-bound for
F
that the class/trait/method in question require
Then it's just a matter of assembling everything with the data class or effect (usually
IO
) for
F
at the edge of the world
For types that don't fall into the effect hierarchy then composition is usually done via transformers (e.g. the
T
in
ReaderT
) or custom combinators
t
And how do you leverage comprehensions when using Kind?
k
I think you can only use a comprehension for type
Monad<F>
. But I don’t know how to derive a
Kind
from that.
Figured it out:
Copy code
fun <F, A> doSomething(): Kind<F, A> = TODO()
fun <F, A> doSomethingElse(): Kind<F, A> = TODO()

fun <F, A> doAComprehension(myMonad: Monad<F>): Kind<F,A> = 
  myMonad.fx.monad {
    doSomething<F, A>().bind()
    doSomethingElse<F, A>().bind()
  }
j
Not sure if this has already been answered: In general you cannot get a value out of a polymorphic
Kind<F, A>
, that is impossible unless you know exactly what
F
is. The reason is pretty simply:
Kind<F, A>
is not a wrapped
A
! It is an action that produces an
A
with effects (effects meaning things like
ForOption = Might be missing value effect
,
ListK = Might have multiple effect
,
IO = Anything, including side-effects
etc. You can also view this as context, or other metaphors which are all equally bad 😄). Only if you know exactly what
F
is you can run those effects and get your
A
value out of it. (
Option.fold(...)
,
Either.fold(...)
, etc) And from some, like
List
or
IO
you "cannot" even get the value out because it has mutliple values or it has side-effects. In those cases typeclasses are needed to operator over the inside values. Now if you have a completly polymorphic
F, G, H
you cannot compose them unless you have a few typeclasses that add such generic methods like
Traverse
(allows
Kind<F, Kind<G, A>> => Kind<G, Kind<F, A>>
) and a few others. And lastly, if you have mutliple of those effects and you find yourself wrapping and unwrapping them constantly, it might be worth taking a look at mtl, but that is quite a bit more complex, but allows you to create stacks like
Kind<F, Kind<G, A>> => Kind<Kind<F, G>, A>
which also combines the types in a way and allows for easier use in comprehensions.
t
Does this have to do with ReaderT and WriterT?I have seen some explanations for these transformers but, tbh I think I'm going too fast. I need to get the basics first xD.
So... I'm trying to find a way to return a list of entities that are read from mongo using ktor. Here's the code so far. I'm not sure if I'm missing something, this somehow looks a bit more complicated than I think it should.
Here's a simplified contract for repositories
And here's another go without IO comprehensions.
j
Does this have to do with ReaderT and WriterT?I have seen some explanations for these transformers but, tbh I think I'm going too fast. I need to get the basics first xD.
Definitly basics first 😄 mtl is a good tool to use when you know how, but it can be hard to understand and in kotlin (until arrow-meta is in use) is not nice to use... Regarding the code: Not sure I understand what exactly the problem is, but a few things that might improve it anyway: • Avoid
val (_) = ...
This will not work as expected in comprehensions, which is why we are deprecating it soon 😕 (The compiler thinks it can just run the coroutine without ever yielding back to the comprehension if the result is ignored) • instead of
attempt().flatMap { e -> e.fold(...) }
why not
handleErrorWith { e -> ... }
t
@Jannis Thank you for the insight. Just one more question, how should I go about the
val (_) = ...
bit? in other words, how do I force an IO to run inside a comprehension without binding it?
j
Just use
bind
instead. Everything else that binds in comprehension is just an alias for that anyway
t
oh, thanks! 😄