Search code examples
kotlinarrow-kt

Destructuring instead of .bind() doesn't work in an Arrow Monad comprehension


According to Arrow's Javadoc there are three ways of binding over a monad:

/**
 * All possible approaches to running [Kind] in the context of [Fx]
 *
 * ```
 * fx {
 *   val one = just(1).bind() // using bind
 *   val (two) = just(one + 1) // using destructuring
 *   val three = !just(two + 1) // yelling at it
 * }
 * ```
 */

The first one and the last one work fine however for some reason destructuring doesn't, why?

In the next sample you can see I'm using destructuring, val (j) = helloJoey().k(),) but that value is interpreted as a Mono` instead of a String.

class HelloServiceImpl : HelloService {

    private val logger = LoggerFactory.getLogger(javaClass)

    override fun helloEverybody(): Mono<out String> {
        return MonoK.monad().fx.monad {
            val (j) = helloJoey().k()
            val a = !Mono.zip(helloJohn(), helloMary()).map { "${it.t1} and ${it.t2}" }.k()
            "$j and $a"
        }.fix().mono

    }

    override fun helloJoey(): Mono<String> {
        return Mono.defer {
            logger.info("helloJoey()")
            sleep(2000)
            logger.info("helloJoey() - ready")
            Mono.just("hello Joey")
        }.subscribeOn(Schedulers.elastic())
    }

    override fun helloJohn(): Mono<String> {
        return Mono.defer {
            logger.info("helloJohn()")
            sleep(5000)
            logger.info("helloJohn() - ready")
            Mono.just("hello John")
        }.subscribeOn(Schedulers.elastic())
    }

    override fun helloMary(): Mono<String> {
        return Mono.defer {
            logger.info("helloMary()")
            sleep(5000)
            logger.info("helloMary() - ready")
            Mono.just("hello Mary")
        }.subscribeOn(Schedulers.elastic())
    }

}

fun main() {
    val countDownLatch = CountDownLatch(1)
    HelloServiceImpl().helloEverybody().subscribe {
        println(it)
        countDownLatch.countDown()
    }
    countDownLatch.await()
}

Solution

  • This is a known problem and why we're moving away from this approach. They're marked as deprecated here.

    What happens is, a MonoK is a data class for which the destructure operator is already defined as returning the wrapped Mono. When used inside an fx block this destructuring takes precedence over the one defined on BindSyntax. Check and see if hinting your expected type works, otherwise use invoke or bind instead.