# arrow

Nathan Bedell

03/02/2024, 5:44 PM
Hey all, I'm curious, is it possible in Kotlin to build a monad comprehension syntax for the monad
Free (Map a)
? I want to say my gut intuition is "no" as the
Map a
branching would probably require multi-shot continuations, but I thought I'd ask anyway.


03/02/2024, 6:19 PM
I'm curious what your use-case is, or if it's curiosity 😉 Also, maybe I misunderstood your question.. 😅
typically disappears in Arrow DSL, and
typically as well.
has couple of uses, but multi-shot is impossible so all multi-shot
are out of the way. Although you can still do a lot of cool things with them in Kotlin, but design becomes very complex. Sweet spot seems relying on
depending on what kind of multi-shot effect you need, and mix what we don't have an official label for but scope/dsl or effect. Let's take
, which is like
, it's a
and a
but we dropped that hierarchy `map`/`flatMap`. So all that remains is raising an error, and recovering from an error. So
, and
, everything else is derived from that. Similarly with
, all you need is
to register a "acquisition" action, and a "finalizer" action. Everything else is derived from there, like registering cancellation, or error callbacks, etc. So it feels a bit similar to designing in Free to me. In Free you typically also think about the operations, and you do the same here but instead of thinking of them in an ADT. You need to think of them in terms of operations.
Copy code
sealed class Resource<A>
data class Install<A>(
  val acquire: suspend () -> A,
  val release: suspend (ExitCase, A) -> Unit
): Resource<A>
Copy code
interface ResourceScope {
  fun <A> install(
    acquire: suspend () -> A,
    release: suspend (ExitCase, A) -> Unit
  ): A
If you look at the signatures, they're identical. Next you typically write the interpeter, loop, eval, ... for your Free ADT. (Regardless if you're using a Free abstraction, or not). In the case of the DSLs, you are stuck writing a
implementation. So you often have to resort to
instead of
since you loose some of the structure. The reason for doing it this way is because this is compatible with the entire Kotlin eco-system. It'll play incredible nice with context parameters, and it's easy to support coroutine cancellation and everything. While this doesn't sounds great initially I actually found that my implementation are often much simpler, and easy to maintain. Might depend on the use-case of course, but you can often create smaller DLSs and compose them into bigger functionality as well. This composes, while monads or free still require transformers, etc.

Nathan Bedell

03/02/2024, 6:24 PM
@simon.vergauwenYeah, so for clarity, essentially what I want a comprehension syntax for is something like:
Copy code
sealed interface FreeMap<A, B> {
    data class Pure(val result: B) : FreeMap<Any?, B>
    data class Continue(val next: Map<A, FreeMap<A, B>>) : FreeMap<A,B>
The motivation for this is essentially to get a monad comprehension syntax for a "coroutine" -- not in the sense of Kotlin coroutines, but as in the Haskell package monad-coroutine ( In particular, I've been exploring this kind of data structure as a sort of dual to the idea of "coroutines as spaces". The idea is something of type
FreeMap<Action, Result>
is a function which requires user input (in the form of an
) at "suspension points", but also at each suspension point has a list of allowable
(hence the
Map<Action, FreeMap<Action, Result>>
). For instance, imagine a UI Workflow like a sequence of dialogs that at each stage the user has a number of different `Action`s for how to proceed -- and we want to view this as a composable process that eventually returns a
. That's the sort of thing I'm trying to represent here. And the advantage of being able to explicitly represent the "set of allowed actions" at each stage is that it makes it really easy to property test it (e.x. test a bunch of possible paths of user actions, assert some invariants / temporal properties / etc...) 🙂