Hi. Can anyone explain to how this function gives ...
# arrow
h
Hi. Can anyone explain to how this function gives me the ability to call a bind() function on an expression that returns an either, passed inside a lambda e.g. binding { val number = Either.right(2).bind() } When bind is not part of Eithers interface, and i can not call it when the expression is outside binding. Would also appreciate a link to Kotlin documentation that explains this Thanks 🙂
s
The
binding
lambda has a
this
receiver that probably has a type (don’t know which type) with an instance method
fun Either<L,B>.bind(): B
☝️ 1
s
The receiver type is
MonadContinuation
s
Thank you, @simon.vergauwen. The Kotlin 'trick' is that Kotlin allows for two receivers for a method (one 'this' for the instance of the class' method, the other 'this' for the extension-type instance)
h
I think I’m missing some fundamental knowledge to understand it. I can see that bind is method on MonadContinuation, but it doesn’t make sense to me how it becomes a member (or extension function) of Either, only in the context of binding { … } Is it coroutines i should dive into, to get a better understanding?
s
No, it has nothing to do with coroutines. It is a Kotlin feature. And you are correct in mentioning the word 'context'. You can make an extension function of one type (
Either
) available only in the the context of another type (
MonadContinuation
). E.g.
Copy code
class MonadContinuation<L,R> {
    ...
    fun Either<L,R>.bind(): R = ... // The function `bind` has two receivers: 1. An Either and 2. this MonadContinuation 
}

fun <L,R> binding(block: MonadContinuation<L,R>.() -> R) : R = MonadContinuation().block()
...
binding<Error, Int> { // 'this' is an instance of MonadContinuation here
    ...
    val result = Either.Some(5).bind() // The function `bind` is called on the Either in the context of 'this'.
    ...
    result
}
s
It’s the same as you do in Android or anywhere else.
Copy code
with(TextView()) {
   id = R.id.text_view
   text = "Hello World!"
}
with
enables a lambda in the form
T.() -> R
. Which basically is an anonymous extension function for
T
, here
View
. So any (instance) method available for
View
is now enabled in the
with
block.
It’s the same for
binding
. It takes an anonymous extension function or extension function with receiver of type
MonadContinuation
. And monad continuation has
bind
methods, so within
binding
you can use this method.
h
Ahh, so whenever an expression of “the given generic type” is in the “context of MonadContinuation” in this example, the following extension function of MonadContinuation is available: suspend fun <B> Kind<F, B>.bind(): B = bind { this }
💯 1
I think it makes sense to me now. Thanks guys
s
That is correct! 🙂
Anytime arrow
r
bind
is available for
either
because either is also a
Kind<ForEither, A>
and
bind
is available to all
Kind<F, A>
that is. If you have a wrapper type with one type argument which extends the marker interface
Kind<F, A>
you just made you first higer kinded type
higher kinded types let us write programs that are polymorphic and work over many kinds
bind
is one of them
Also in 0.9.0 you don't need to use bind anymore
because we have
component1
Copy code
val (one) = 1.right()
this is true for all monads just not Either
that is because all of them are higher kinded types
Changes to the arrow api's are for the most part symetric to all data types because they all have the same functions
we create such functionalities operating over kinds and not just directly over Either or any other ad-hoc concretion