How can I run IO functions in this lambda? I'd lik...
# arrow
a
How can I run IO functions in this lambda? I'd like to get the commented out code to work: https://github.com/adomokos/kotlin-sandbox/blob/master/src/test/kotlin/sandbox/arrow/ReaderSpec.kt#L107-L116
j
Well a few things: First of you are not in an fx block, so you would need to wrap it in something like
RIOApi.monadError().fx.monad {...}
(not sure what the exact code is). That should enable the bind syntax for
Kind<EitherTPartialOf<ForIO, AppError>, *>
. After that you have
EitherT<ForIO, AppError, Int>
(assuming you added the two in the fx block), so you need to lift that into
ReaderT<EitherTPartialOf<ForIO, AppError>, GetAppContext>
, that should be as easy as doing
ReaderT.lift(RIOApi.monadError(), res)
(I am not 100% sure
ReaderT
has lift, but it should have something similar, if it does not work just use
ReaderT { res }
) This will be made easier when arrow has a
MonadTrans
typeclass which is made to deal with those lifting situations: https://github.com/arrow-kt/arrow/issues/1913 is the ticket tracking it with some discussion ^^ This is not how I'd do mtl tho, because the types are too concrete and it's a hassle to use. Imo a better way is do define things over polymorphic
F
and only fix the type at the very end. The functions then look like
fun <F> oneT(ME: MonadError<F, AppError>): Kind<F, Int>
and if you need context you do
fun <F> doSomething(MR: MonadReader<F, GetAppContext>): Kind<F, ??>
and so on. If you need more than one capability just add more parameters. With arrow-meta you will get those resolved implicitly soon anyway, so the cost of having to pass parameters will eventually go away. (Btw if you need
IO
you can use
MonadDefer
and above (
Async
,
Concurrent
etc) to do almost anything you need, but if you need to interface with actual hardcoded
IO
methods you need to wait for me to finish my
MonadIO
pr or use unsafe methods :/) Then top-level you can fill in your actual monad-stack. This is also great for testing because you can use a different monad-stack for tests and because of the laws it should work exactly the same ^^
a
I dig the idea of using more abstract classes, but I need to learn more about it. Is there a good tutorial or blog post on that? Thank you for your reply, I understand stacking IO, Either and Reader over this layer is cumbersome and complex, but I wanted to see what it looks like. I am already turned off by the complexity of the calls and lack of examples.
j
I am already turned off by the complexity of the calls and lack of examples
That I can totally understand 🙂 mtl in arrow needs a lot of work and the docs even more. If you want to make the stacking part manageable consider defining a new concrete datatype
class App<A>(val unApp: ReaderT<EitherTPartialOf<ForIO, AppError>, GetAppContext, A>)
and quickly define the typeclasses you need for it (by just delegating to the underlying value). Then just return
App
everywhere you need to be in the stack. Here is an example on how that would look for
WriterT
+
EitherT
+ polymorphic
M
. It is a bunch of boilerplate (even in haskell) But once you have set it up it's usable (just some upfront work). But here again I have abstracted the behaviour of the concrete type into the
MonadTest
typeclass to keep users from having to use a concrete stack. For resources and blogs, I wish I had good mtl resources, I'll look around if I find any, skim them and link it if they are any good. mtl is hard, and with all the boilerplate around higherkinds and typeclasses that kotlin imposes it's even harder to learn...
Oh I forgot to add something: Don't define a full new datatype when you only delegate to the below values, just define two typealiases (one for
F
and one for
F<A>
because in kotlin we can't partially apply types)
Don't expect any kotlin posts, there aren't any on mtl afaik (not as in full use with multiple transformers etc): http://dev.stephendiehl.com/hask/#mtl-transformers is gold, not just for mtl, everything haskell and fp related in there is good. It has one more complex example of mtl, but only mentions the typeclass approach shortly This quote refers to
fun <F, R> MonadReader<F, R>.ask(): Kind<F, R>
being the only used thing in modern haskell (compared to concrete reps)
In practice only the last one is used in modern Haskell.
This is a nice little example for how the typeclass approach leads to very nice testing (and it also shows that not even haskell solved the boilerplate problem, athough it's not too bad ^^) https://github.com/lexi-lambda/mtl-style-example I need to get going, so I'll top searching, as always just ask away with any question, and maybe I should at some point bookmark those threads because those questions are a good inspiration on what needs to be in the docs.