EarthCitizen
01/08/2020, 7:45 PMEither.catch()
requires a suspend functionAttila Domokos
01/08/2020, 9:47 PMEitherT.monad<ForIO, AppError>(IO.monad()).fx.monad {
looks a bit scary. Is there a simpler way to express this?EarthCitizen
01/09/2020, 6:57 AMEither.catch()
being a suspend has turned out to be quite a pain …why is it unsafe to make my own catch()
function that is not a suspend version? …as in what problem will I run into …keep in mind that I am not using coroutines in the code in question …I have a large number of functions that wrap other functions to return their exceptions as a Left
. My code is getting quite ugly as I am having to weave a lot of runBlocking { }
into my call stacks …namely inside of flatMap { }
and handleErrorWith { }
Jorge Castillo
01/09/2020, 8:10 AMGopal S Akshintala
01/09/2020, 5:57 PMarm(ME: MonadError<F, NukeException>)
and aim(ME: MonadError<F, NukeException>)
are taking input params, but in the subsequent code those functions don’t have params while invoking
fun <F> MonadError<F, NukeException>.attack():Kind<F, Impacted> =
fx.monad {
val (nuke) = arm<F>()
val (target) = aim<F>()
val (impact) = launch<F>(target, nuke)
impact
}
kyleg
01/09/2020, 6:15 PMval x: Option<Any> = TODO()
fun y: IO<Any> = TODO()
IO.fx {
x.fold({}, { y.bind() })
}
is OK, but
val x: Option<Any> = TODO()
fun y: IO<Any> = TODO()
IO.fx {
x.map { y.bind() }
}
is not (the latter complains about bind
needing to be running in a co-routine? What’s the design decision backing this?
Just curious because I have a fn returning an Option
, and if it is None
I don’t need to do anything. Only if it’s Some
. I almost re-wrote some code when I initially tried map
and it gave me this error. Fortunately I tried fold
to see if it would work. Of course, it’s more verbose because of the empty lambda for isEmpty
. If I can understand the justification for this, it’ll likely help me avoid problems in the future.pakoito
01/09/2020, 8:50 PMApplicative.map
into an Either or an OptionEarthCitizen
01/10/2020, 10:01 AMEither.catch()
from inside Either.fx { }
? …I see an error about restricted suspending functions, but I don’t understand how to see what can be called from within Either.fx { }
…I am currently getting around the problem by doing this, but I don’t know if it is safe to do:
fun <L, R> Either.Companion.catchSafely(
mapper: (Throwable) -> L,
block: () -> R): Either<L, R> =
runBlocking {
catch(
suspend { block() }
)
}.mapLeft(mapper)
pakoito
01/10/2020, 4:25 PMAttila Domokos
01/10/2020, 11:31 PMIO
to model effects, do I need to use suspend
on the functions as well? I am looking at examples here (https://jorgecastillo.dev/kotlin-fp-2-monad-transformers) and I don't see it at all. When do I need to use suspend
?pakoito
01/10/2020, 11:36 PMAttila Domokos
01/10/2020, 11:59 PMpakoito
01/11/2020, 12:03 AMmmaillot
01/12/2020, 11:56 AMloadURL().flatMap { url -> callWS(url) }.flatMap { saveResulte(url: ???, response: it) }
How can I get the url to save it in the last flatMap ? Should I use a pair in the second flatmap .flatMap { url -> callWS(url).map { Pair(it, url) } }
?matiaslev
01/13/2020, 4:55 PMAttila Domokos
01/13/2020, 7:05 PMjulian
01/13/2020, 7:35 PMIn aIs this use of the wordtheMonad
function takes a pure value, and wraps it in anunit
structureF
F[A]
pure
related to its use in the term pure function
? If so, how?rcd27
01/14/2020, 10:14 AMarrow
talk wich is about "abstracting over implementations". What I mean: moving such decisions like "which Async implementation I want to use this time: RxJava or Coroutines" to one place. I remember there was something about it, but can't find which one.kyleg
01/15/2020, 1:05 AMfix()
. I thought it converted Kind<F, A>
into F<A>
, where (for example) F
is Option
. But then I have this, and the compiler disagrees that my type is correct:
import arrow.core.Either
import arrow.core.Option
import arrow.core.extensions.option.traverse.traverse
import arrow.core.k
import <http://arrow.fx.IO|arrow.fx.IO>
import arrow.fx.extensions.fx
import <http://arrow.fx.extensions.io|arrow.fx.extensions.io>.applicative.applicative
import arrow.fx.fix
import <http://java.io|java.io>.File
interface MediaId
interface Entity
interface Foo { val source: Option<File> }
val pathArgs: Foo = TODO()
fun bar(x: String): IO<Sequence<File>> = TODO()
fun transform(it: File): Entity = TODO()
fun baz(x: Entity): IO<Either<Throwable, MediaId>> = TODO()
val result: IO<Option<Sequence<Either<Throwable, MediaId>>>> = pathArgs.source.traverse(IO.applicative()) {
IO.fx {
val files = bar(it.canonicalPath).bind()
files.k().traverse(IO.applicative()) {
val entity = transform(it)
baz(entity)
}.fix().bind()
}
}.fix() // IO<Kind<ForOption, SequenceK<Either<Throwable, MediaId>>>> ???
The fix()
at the end does turn it from Kind<IO, …>
into IO<…
but why doesn’t the fix()
tagged onto the internal traverse
not turn the Kind<ForOption, SequenceK
into Option<SequenceK>
?rcd27
01/15/2020, 9:05 AMArrow
. "I've seen this all already somewhere" 🤔Sagar Suri
01/15/2020, 9:49 AMGopal S Akshintala
01/15/2020, 10:34 AMinterface FetcherDependencies<F>: MonadThrow<F> {
fun api(): ApiService
companion object {
operator fun invoke(ME: MonadThrow<F>, api: ApiService): FetcherDependencies =
object: FetcherDependencies, MonadThrow<F> by ME {
override fun api() = api
}
}
}
CLOVIS
01/16/2020, 7:51 AMsimon.vergauwen
01/16/2020, 9:07 AMBruno
01/16/2020, 10:40 AMBruno
01/16/2020, 10:41 AMinteract
function takes a function of type String->String
as its argument. The entire input from the standard input device is passed to this function as its argument, and the resulting string is output on the standard output device.
I got something like:
fun interact(f: (String) -> String): IO<Unit> = IO.fx {
f.apply { readLine() }
}
but now I am unsure how to proceed.Bob Glamm
01/16/2020, 1:58 PMEither.catch {}
) should make it into the channel topic description? 😄Jannis
01/16/2020, 2:05 PMPhBastiani
01/16/2020, 3:41 PMkyleg
01/17/2020, 3:46 AMclass UseCase(private val repository: MyDatabase) {
operator fun invoke(file: File): Long { repository.getSomeLong() }
}
fun test() {
val repo: MyDatabase = TODO()
val useCase = UseCase(repo)
// some hundreds of lines later
useCase(File("/path/to/foo"))
}
I haven’t used State
yet, but the descriptions I see largely seem like it’s somewhat similar in concept. You create an object with parameters representing things you’ll read/write to (like a repository) and later do something with it like DB insertions, retrievals, all to manage app state or something?
Is my understanding correct?kyleg
01/17/2020, 3:46 AMclass UseCase(private val repository: MyDatabase) {
operator fun invoke(file: File): Long { repository.getSomeLong() }
}
fun test() {
val repo: MyDatabase = TODO()
val useCase = UseCase(repo)
// some hundreds of lines later
useCase(File("/path/to/foo"))
}
I haven’t used State
yet, but the descriptions I see largely seem like it’s somewhat similar in concept. You create an object with parameters representing things you’ll read/write to (like a repository) and later do something with it like DB insertions, retrievals, all to manage app state or something?
Is my understanding correct?Christian Maus
01/17/2020, 10:38 AMJannis
01/17/2020, 10:54 AMReaderT
pattern, ZIO
(from scala) has a version of this built in in their IO
monad.
First of: a reader is a wrapper around a partially applied function: Reader<R, A> = (R) -> A
and the transformer version ReaderT<F, R, A> = (R) -> Kind<F, A>
. This means this pattern is no different from the code example @kyleg posted. The benefits are: typeclass instances (it is a monad (and a few other things), which means you can also do fx
comprehensions over it)
Second: Using a reader in arrow depends on how you use arrow:
If you use arrow fx, you should use ReaderT<ForIO, R, A>
to make this pattern work well with IO
. There is also plans to make this pattern in-built into IO
because it is so common, so using this for now and refractoring to the inbuilt version later should be easy as they are isomorphic.
If you don't use arrow fx using the normal Reader
should work fine ^^
The UseCase
class is technically no different from Reader<MyDatabase, (File) -> Long>
. This is better modeled as (File) -> Reader<MyDatabase, Long>
but those two work basically the same anyway. The benefit is, that it can be composed easilykyleg
01/18/2020, 1:15 AMJannis
01/18/2020, 2:12 AMReaderT = (R) -> Kind<F, A>
, WriterT = Kind<F, Tuple2<W, A>>
now lets compose them with `R == W`: ReaderT<WriterTPartial<F, R>, R, A> == (R) -> WriterT<F, R, A> == (R) -> Kind<F, Tuple2<R, A>> == StateT<F, R, A>
. So in theory they are the same, but the semantics of Writer
and Reader
are different which means the composition of the two does not work in practice (mainly because the output from the writer is never fed into the reader, so we actually have no changeable state). They are only equal in representation 😅
They also all evolved from similar problems and they are the most fundamental monad-transformers out there (Their Id
variants ReaderT<ForId, *, *> == Reader<*, *>
are also really easy to explain). So anyway here are the problems which led to ppl using those types:
• Constantly passing around parameters or partially applied functions for DI is solved by Reader
turning (A) -> B
into Reader<A, B>
which allows the function to use the context, but doesn't force callers to provide it (they just need to return a Reader<A, B>
like as well.
• Constantly passing around accumulated logs, values etc is solved by Writer
turning a function returning (W, B)
into a function returning Writer<W, B>
, this has the same benefits, but abstracts away the log.
• Constantly passing around and feeding changed state is solved by State
turning functions (S) -> (S, A)
into State<S, A>
again providing a nice abstraction.kyleg
01/18/2020, 3:55 AMaballano
01/18/2020, 11:55 PMJannis
01/19/2020, 12:14 AMReader
in IO
context? In that case Kleisli<ForIO, R, A>
will do just fine. ReaderT
is also just a typealias for Kleisli
anyway.kyleg
01/19/2020, 12:25 AMReader<A, B>
created with Reader { context -> returnsB() }
I have to add on .run { Id(this) }
at the end. Same goes for ReaderApi.ask<A>().map context -> returnsB() }
Did Arrow change the API for this? Because looking at this sample project using Arrow Reader, they don’t appear to have had to do this.
He actually imports Reader from arrow.data
but I have to import it from arrow.mtl
as it does not appear to exist in arrow-data
module anymore.typealias AppReader<T> = Reader<AppContext, T>
fun scanVideo(root: String): AppReader<IO<Sequence<File>>> = Reader { ctx ->
Id(IO.effect { /*snip*/ })
}
Jannis
01/19/2020, 12:42 AMReader
is an alias for ReaderT<ForId, R, A>
which is an alias for Kleisli<ForId, R, A>
(for category theory reasons I think). So using the constructor invokes the Kleisli
constructor which is f: (R) -> Kind<F, A>
. The easiest way to get into reader context is to use ReaderApi
as in the linked repo. Then you can also skip the Id(...)
IfReaderApi.ask<A>().map { context -> returnsB() }
returnsB
returns Reader<A, *>
(given that context
has type A
here) what you want would be ReaderApi.ask<A>().flatMap { ctx -> returnsB() }
. You could also do fx:
Reader.monad(Id.monad()).fx.monad { // Not sure if there is a shorthand somewhere, so I'll fully qualify it... You may also need more type params
val cts = ReaderApi.ask<A>().bind()
...
returnsB().bind()
}
Yes that is quite outdatedAlthough mtl hasn't changed too much in recent versions, so while stuff got moved, fx and some typeclasses changed, the functions from mtl are still similar, if not the same
kyleg
01/19/2020, 1:03 AM.andThen { Id(it) }
on the end of my Reader { … }
constructors. It doesn’t appear to be making use of the Reader(run: …) = ReaderT(run.andThen(Id(it)})
code that Arrow has.
I need to figure out why that is.Jannis
01/19/2020, 1:13 AM