Why is it that ```val x: Option<Any> = TODO(...
# arrow
k
Why is it that
Copy code
val x: Option<Any> = TODO()
fun y: IO<Any> = TODO()

IO.fx {
   x.fold({}, { y.bind() })
}
is OK, but
Copy code
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.
j
That is because
fold
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.
k
OK, I’ll look into
traverse
. 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 🙂
j
@Imran/Malic probably knows more: https://github.com/arrow-kt/arrow/pull/1527 he made a pr which removes a bunch of them
take it step by step 🙂 The reason this reminds me of traverse is that you are applying an effect in a map and that is literally the entire point of traverse
fun <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/
👆🏽 1
Runnable ank examples are broken....
https://github.com/arrow-kt/arrow/blob/master/modules/core/arrow-core-data/src/main/kotlin/arrow/typeclasses/Traverse.kt Is the source which contains the code. If you don't mind reading it as kdoc you'll get the examples there 🙂
i
@Jannis can you send a link for the broken ank examples?
j
@Imran/Malic any page on next.arrow-kt.io for me
Not related to any docs in particular, just next.arrow-kt.io not loading any code blocks
i
Same here. Let me report this. Thanks for noticing 🙂
👍 1
Hello @kyleg 😀 Yes, @Jannis is completely right.
traverse
should do the job. #1527 was part of the roadmap for 0.10.0 regarding refactoring and publishing arrow libraries with fewer artifacts.
k
Piggybacking off this because it’s a docs issue (I’m tacking a simple issue in the repo regarding documentation). Does the sample code in the docs need to compile for the release version the docs are attached to? I see usage like
Copy code
fun 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.
Also @Imran hello back at you 🙂
i
You can run
dokka
and then
runAnk
to typeCheck your examples
💯 1
k
Ah, OK. I was opening up a scratch and affiliating it with a project that has Arrow (0.10.3 I think) in it.
j
In short we are generating the docs with dokka and ank which is also made by the arrow-team then runs over all generated markdown files and executes every annotated code block against the current release version.
But some blocks are not annotated with ank so they are not checked 🙈
k
The one that has this
fx.monad
thing does not have ank annotated 🙂
just
kotlin
not
kotlin:ank
j
that is what I meant. as long as it has
*:ank
it will be run through the kotlin script engine
k
yes, I’m saying the one that my compiler didn’t seem to like did not have
kotlin: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.
👍 1
j
I misread that 😅
r
for a while the script engine manager that ank uses chocked on continuations with bindings but it may be fine now and we just need to enable ank on those
k
@Jannis when you say “applying an effect” does that mean “doing something that puts a datatype wrapper around data”? Like how traverse takes a function that puts an IO/Option/Either/etc. wrapper around whatever the input was?
j
almost: A few examples for effects:
Option
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 🙂
k
It makes sense if I think of Option, Either, IO as being “lists” with just one element that you traverse over like you would a list. And if I associate “effect” with “applicative” (since the first param passed into
traverse
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>>
j
This won't hold for all datatypes, but it'll get you far enough as a start 🙂 The important bits about
traverse
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 ^^