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.Jannis
01/09/2020, 6:19 PMfold
is usually defined as inline whereas that is not the case for most other combinators. map
etc were defined inline for a while tho, not sure what exactly the reason was. Either way it's probably for the better as the correct way to do such a thing would probably be to use traverse
and the likes.kyleg
01/09/2020, 6:21 PMtraverse
. First and foremost I want to use the right tool for the task. I just know map/reduce/forEach/fold/just because of the fact that they are so fundamental to broader programming. traverse
and functors and applicators and stuff are all things I need to learn still 🙂Jannis
01/09/2020, 6:22 PMJannis
01/09/2020, 6:26 PMfun <F, G, A, B> Kind<F, A>.traverse(f: (A) -> Kind<G, B>): Kind<G, Kind<F, B>>
fix this to option and IO and you get fun <A, B>Option<A>.traverse(f: (A) -> IO<B>): IO<Option<B>>
which you can then bind
to get exactly the same behaviour that map { f(it).bind() }
would give you. I left out some minor things that may be different but it's basically the same thing. @Imran/Malic also wrote a really good article in the docs about traverse
recently: https://next.arrow-kt.io/docs/apidocs/arrow-core-data/arrow.typeclasses/-traverse/Jannis
01/09/2020, 6:29 PMJannis
01/09/2020, 6:34 PMImran/Malic
01/09/2020, 7:00 PMJannis
01/09/2020, 7:00 PMJannis
01/09/2020, 7:01 PMImran/Malic
01/09/2020, 7:02 PMImran/Malic
01/09/2020, 7:14 PMtraverse
should do the job. #1527 was part of the roadmap for 0.10.0 regarding refactoring and publishing arrow libraries with fewer artifacts.kyleg
01/09/2020, 7:20 PMfun foo(): Try<Bar> =
fx.monad {...}.fix()
}
which my compiler complains about. I think I read this was changed to something else in a recent release. In foo
, I suppose you’d replace this with Try.fx { …}
and no .fix()
at the end. FWIW I know Try
is being deprecated, but in order to keep issues separate, I’m just updating the Try
code for the specific issue.kyleg
01/09/2020, 7:21 PMImran/Malic
01/09/2020, 7:22 PMdokka
and then runAnk
to typeCheck your exampleskyleg
01/09/2020, 7:23 PMJannis
01/09/2020, 7:23 PMJannis
01/09/2020, 7:24 PMkyleg
01/09/2020, 7:24 PMfx.monad
thing does not have ank annotated 🙂kyleg
01/09/2020, 7:24 PMkotlin
not kotlin:ank
Jannis
01/09/2020, 7:25 PM*:ank
it will be run through the kotlin script enginekyleg
01/09/2020, 7:26 PMkotlin:ank
. I’ve just added one and in a few minutes when I’ve got the docs edited for the issue, I’ll check with dokka
and runAnk
to see if it’s OK.Jannis
01/09/2020, 7:27 PMraulraja
01/09/2020, 9:09 PMkyleg
01/10/2020, 6:24 PMJannis
01/10/2020, 6:33 PMOption
encodes the effect of potentially not having a value
Either<L, *>
the effect of maybe having a left value of type L
.
List
encodes the effect of having multiple values (or none)
IO<A>
encodes side-effects (everything that the return type A
does not show)
In an fx block you "apply" the effect to get the value inside (which can be missing, but also can be called multiple times). In traverse
you run an effectfull computation for every element and use the applicative to combine all of those while keeping the structure. So if you use traverse
over a binary tree with a function that returns Option
traverse will apply the effect of optioniality over and over again (for Option that means it will combine the two values if they are both present and return None otherwise, and the combine operation in traverse always rebuilds the initial structure). This will leave you with an Option<BinaryTree<A>>
which is None
if you have return any None
value inside traverse
and Some
, with the same binary tree, otherwise. Not sure how intuitive this explanation is so ask away if this raises more questions 🙂kyleg
01/10/2020, 6:42 PMtraverse
is IO.applicative()
or Option.applicative()
etc.
Basically A.map(bFn) gives me A<B<Any>>
while A.traverse(bFn) gives me B<A<Any>>
Jannis
01/10/2020, 6:48 PMtraverse
are that it is structure preserving traverse(Id.applicative(), f) == map(f)
and it applies all effects listOf(1,2,3,4).traverse(IO.applicative(), { IO { println(it) } })
will print all values. traverse
is actually the more complicated operator of the Traverse
typeclass, sequence
is a bit nicer 🙂 Also the examples on next.arrow-kt.io are back so the docs that @Imran/Malic wrote will now be complete again ^^