Skip to content

receivers-vs-flatmap has inaccurate example of monadic composition #423

@rschwiebert

Description

@rschwiebert

In receivers-flatmap the following passage appears:

Alas, code written with monads tends to break under the weight of all the flatMaps in the way and the nesting associated with them.

doOneThing().flatMap { x ->
  doAnotherThing(x).flatMap { y ->
    // imagine if your code was even longer!
    return x to y
  }
}

Firstly, if it is really using monadic style with a monad M, doOneThing would produce a value of some type wrapped with M, which each subsequent application of flatMap would transform into other types wrapped with M. In the example here, the last application of flatMap returns a Pair, which doesn't (natively anyway) have a monadic interface.

Secondly, doing this using a monad should never suffer from nesting because of associativity. For example nobody would write:

println(
        Either.Right(5)
            .flatMap {
                Either.Right(it * 3)
                    .flatMap {
                        Either.Right(it + 1)
                            .flatMap { Either.Right(it.toString()) }
                    }
            }
    )

when associativity means it is equivalent to

    println(
        Either.Right(5)
            .flatMap { Either.Right(it * 3) }
            .flatMap { Either.Right(it + 1) }
            .flatMap { Either.Right(it.toString()) }
    )

It's difficult to know what the author had in mind without knowing the return values of the flatMaps. Perhaps they were thinking of mixing monads of different types, which indeed would allow the flatMaps, and would suffer the breakdown of associativity. But there's already a section about the lack of composition between general monads, so it's misplaced.

In short, the suggestion that properly written monadic code suffers from too much nesting is, in principle, unfounded. It's more of an example of a pseudo-monadic style that uses some syntax of monadic interface without actually properly using the monad.

I was thinking of suggesting a concrete replacement here along the lines of what I've written above, but I wanted to raise the issue here first to see what the feedback would be. The example as currently written seems misleading and of little value, to me.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions