Christian Maus
01/04/2020, 1:43 PMimport arrow.core.Either
import arrow.core.ForEither
import arrow.core.Option
import arrow.core.Try
import arrow.fx.ForIO
import <http://arrow.fx.IO|arrow.fx.IO>
import <http://arrow.fx.extensions.io|arrow.fx.extensions.io>.applicative.applicative
import arrow.mtl.EitherT
import arrow.mtl.OptionT
typealias IOResult = EitherT<ForIO, Throwable, OptionT<ForEither, Int>>
val doesNotWork = IOResult.fromEither(IO.applicative(), Either.catch { Option.just(3) })
val doesWork = IOResult.fromEither(IO.applicative(), Try { Option.just(3)}.toEither())
raulraja
01/04/2020, 8:15 PMraulraja
01/04/2020, 8:17 PMChristian Maus
01/05/2020, 10:50 AMimport arrow.core.Either
import arrow.core.Option
import arrow.core.Tuple2
import arrow.fx.ForIO
import <http://arrow.fx.IO|arrow.fx.IO>
import <http://arrow.fx.extensions.io|arrow.fx.extensions.io>.monad.monad
import arrow.mtl.EitherT
import arrow.mtl.EitherTPartialOf
import arrow.mtl.OptionT
import arrow.mtl.extensions.eithert.monad.monad
import arrow.mtl.extensions.fx
typealias OptionInEitherInIO = OptionT<EitherTPartialOf<ForIO, Throwable>, String>
//some library function that is outside of my control, this function hits the db and throws exceptions
fun findInDB(id: String): String? = TODO()
//this is the type i think is appropriate for modelling -> Throwable should be replaced by a failure datatype in the end
fun makeSafe(f: (String) -> String?): (String) -> IO<Either<Throwable, Option<String>>> = TODO()
//the monad transformers should allow for monad comprehension
fun makeSafe2(f: (String) -> String?): (String) -> OptionInEitherInIO = TODO()
//this is what i try to accomplish
val safeFind = makeSafe2(::findInDB)
val monad = EitherT.monad<ForIO, Throwable>(IO.monad())
val result = OptionT.fx(monad) {
val s1 = safeFind("foo").bind()
val s2 = safeFind(s1).bind()
Tuple2(s1, s2)
}
raulraja
01/05/2020, 11:02 AMsuspend fun safeFindInDB(id: String): String? = findInDb(id)
val result: IO<Either<Throwable, String?> = IO.effect { safeBindInDB("someId") }.attempt()
// Use EitherT over values of result
val sink = Either<Throwable, String?> = result.unsafeRunSync() //don't do this in prod
Christian Maus
01/05/2020, 3:36 PMJannis
01/05/2020, 6:31 PMApplicative
Monad
instances required everywhere for your concrete monad-stack.
And keep a good typealias. If you want lookups that can return None, can also fail with an error E and need side-effects you are looking at something like OptionT<EitherTPartial<ForIO, E>, A>
which when unwrapped is equal to IO<Either<E, Option<A>>>
. Few good rules of thumb: Always have IO at the bottom of the stack and always mentally unwrap it to it's concrete rep to understand what is happening. EitherT<M, E, A> = Kind<M, Either<E, A>>
, OptionT<M, A> = Kind<M, Option<A>>
and other mtl instances are similar. Also something you will need is good support for lifting values, define a common function lift which goes from Kind<M, A>
to your monadstack (if it contains M that is^^)
There is also room for a pr to add something like a MonadTrans
class (i did start it a while back but got stuck on a few annoying parts about generic parameter order)Jannis
01/05/2020, 6:35 PMIO<E, A>
instead. If you have long chains of code where this optional behaviour you have with OptionT
persists it is definitly better then nullable types or using IO<E, Option<A>>
(you can also use OptionT<IO<E, A>>
there) but it will always come at a certain cost to do such a thing. I personally would only go for mtl if something like flatMap { it.flatMap { ... } }
happens over and over again or if I need to constantly wrap and unwrap values in fx blocks.Jannis
01/05/2020, 6:43 PMKind<*, *>
is also resolved. IO<E, A>
with still be a lot faster and easier to use than EitherT<ForIO, E, A>
, but the gap won't be that far ^^
With proper newtypes that actually fully unwrap at runtime, if that is possible, you can get the performance issues under control and then mtl becomes actually viable. Although mtl on it's own still has annoying parts and I like algebraic effects much more, but thats a different story.Christian Maus
01/06/2020, 12:01 PMJannis
01/06/2020, 12:26 PMIO<E, A>
is in the works and will come out in with arrow 0.11 in the next 1-3 months I think (don't quote me on that 🙈). Arrow meta is also under active development but that's a bit further outJannis
01/06/2020, 12:30 PMMtl however becomes easier to use with arrow-meta plugins:This would have been better phrased as "Mtl will however become easier to use with arrow-meta plugins:" ...
raulraja
01/06/2020, 12:51 PM