implement a monad comprehension on a list in kotlin using a coroutine - kotlin

I wonder if it is possible to implement something similar to the do-notation of Haskell in Kotlin on Lists or List-Like structures with monadic properties.
Take following example:
fun <A, B> cartesianProduct(xs: List<A>, ys: List<B>): List<Pair<A, B>> =
xs.flatMap { x -> ys.flatMap { y -> listOf(x to y) } }
It would be nice if I could write something like
suspend fun <A, B> cartesianProduct(xs: List<A>, ys: List<B>): List<Pair<A, B>> =
list {
val x = xs.bind()
val y = xs.bind()
yield(x to y)
}
Arrow-Kt defines similar comprehensions using coroutines for either, nullable, option and eval. I looked at the implementation and also its Effect documentation, but I have trouble to translate the concept to Lists. Is this even possible in kotlin?

It's not possible at the moment to implement monad comprehension for List, Flow, and other non-deterministic data structures that emit more than one value. The current implementation of continuations in Kotlin is single shot only. This means a continuation can resume a program with a single emitted value. Resuming the program more than once requires hijacking the continuation stack labels with reflection in order to replay their state in the second resumption. Additionally replaying a block in which a multishot data type is binding would replay all effects previous to the bind since the block has to emit again.
list {
println("printed 3 times and not cool")
val a = listOf(1, 2, 3).bind()
a
}
The arrow-continuations library already includes a MultiShot delimited scope for reset/shift but it's currently internal since is not safe until Kotlin suspension or continuations provide the ability to multishot without replaying the current block. Alternatively we would need real for comprehensions or a similar structure to enforce binds happen before other code which would also solve the block replaying issue.
The Effect interface ultimately delegates to one of these scopes for its implementation. The current versions of Reset.suspended and Reset.restricted are single shot.

Related

Is there a way to use monad comprehensions with Kotlin Flow

Kotlin coroutines and Arrow are a nice way to avoid nesting flatmaps, introducing monadic comprehensions in Kotlin. However Kotlin's Flow type still relies on declarative flatmapping, so we get into a mixture of direct and declarative styles:
override suspend fun findAll(page: Pageable): Either<BusinessException, Flow<PageElement<ClientOut>>> = either {
val count = clientRepository.count().awaitSingle().bind()
return clientRepository.findByIdNotNull(page).asFlow()
.flatMapMerge { client ->
flow { emit(mapDetailedClientOut(client)) }
}
}
val count has been bound inside the either {...} comprehension. However, there doesn't seem to be a way to do the same with Flow, forcing us to nest a flatmapMerge().
Is there a way to do it, or is it planned to be somehow included in the near future?
Sadly there is currently no way to build comphrehensions for the KotlinX Flow datatype, since Coroutines in Kotlin only support for single-shot emission/bind.
Therefore it's only possible to build comphrensions for data types with 0..1 elements such as Either or Nullable, but not 0..N like the Flow or List data types.

Relation between Arrow suspend functions and monad comprehension

I am new to Arrow and try to establish my mental model of how its effects system works; in particular, how it leverages Kotlin's suspend system. My very vague understanding is as follows; if would be great if someone could confirm, clarify, or correct it:
Because Kotlin does not support higher-kinded types, implementing applicatives and monads as type classes is cumbersome. Instead, arrow derives its monad functionality (bind and return) for all of Arrow's monadic types from the continuation primitive offered by Kotlin's suspend mechanism. Ist this correct? In particular, short-circuiting behavior (e.g., for nullable or either) is somehow implemented as a delimited continuation. I did not quite get which particular feature of Kotlin's suspend machinery comes into play here.
If the above is broadly correct, I have two follow-up questions: How should I contain the scope of non-IO monadic operations? Take a simple object construction and validation example:
suspend fun mkMessage(msgType: String, appRef: String, pId: String): Message? = nullable {
val type = MessageType.mkMessageType(msgType).bind()
val ref = ApplRefe.mkAppRef((appRef)).bind()
val id = Id.mkId(pId).bind()
Message(type, ref, id)
}
In Haskell's do-notation, this would be
mkMessage :: String -> String -> String -> Maybe Message
mkMessage msgType appRef pId = do
type <- mkMessageType msgType
ref <- mkAppRef appRef
id <- mkId pId
return (Message type ref id)
In both cases, the function returns the monad type (a nullable value, resp. Maybe). However, while I can use the pure function in Haskell anywhere I see fit, the suspend function in Kotlin can only be called from within a suspend function. In this way, a simple, non-IO monad comprehension in Arrow behaves like an IO monad that must be threaded throughout my code base; I suppose this results because the suspend mechanism was designed for actual IO operations. What is the recommended way to implement non-IO monad comprehensions in Arrow without making all functions into suspend functions? Or is this actually the way to go?
Second: If in addition to non-IO monads (nullable, reader, etc.), I want to have IO - say, reading in a file and parsing it - how would i combine these two effects? Is it correct to say that there would be multiple suspend scopes corresponding to the different monads involved, and I would need to somehow nest these scopes, like I would stack monad transformers in Haskell?
The two questions above probably mean that I am still lacking a mental model that bridges between the continuation-based implementation atop the Kotlin's suspend mechanism with the generic monad-as-typeclass implementation in Haskell.
schuster,
You're correct that Arrow uses the suspension feature from Kotlin to encode something like monad comphrensions.
To answer your first question:
Kotlin has suspend in the language (and Kotlin Std), by default suspend can only be called from other suspend code. However, the compiler also has a feature called RestrictsSuspension, this disallows for mixing suspend scopes and thus disallows the ablity to combine IO and Either for example. We expose a secondary DSL, either.eager which is encoded using RestrictsSuspension and it disallows calling foreign suspend functions.
This allows you to encode mkMessage :: String -> String -> String -> Maybe Message.
fun mkMessage(msgType: String, appRef: String, pId: String): Message? = nullable.eager {
val type = MessageType.mkMessageType(msgType).bind()
val ref = ApplRefe.mkAppRef((appRef)).bind()
val id = Id.mkId(pId).bind()
Message(type, ref, id)
}
To answer your second question:
IO as a data type is not needed in Kotlin, since suspend can implement all IO operations in a referential transparent way like it works in Haskell.
The compiler also makes a lot optimisations in the runtime, just like Haskell does for IO.
So the signature suspend fun example(): Either<Error, Value> is the equivalent of EitherT IO Error Value in Haskell.
The IO operations are however not implemented in the Kotlin Std, but in a library KotlinX Coroutines, and Arrow Fx Coroutines also offers some data types and higher-level operations such as parTraverse defined on top of KotlinX Coroutines.
It's slightly different than in Haskell, since we can mix effects instead of stacking them with monad transformers. This means that we can call IO operations from within Either operations. This is due to special functionality, and optimisations the compiler can make in the suspension system. This blog explains how that optimisation works, and why it's so powerful. https://nomisrev.github.io/inline-and-suspend/
Here is also some more background on Continuations, and tagless encodings in Kotlin. https://nomisrev.github.io/continuation-monad-in-kotlin/
I hope that fully answers your question.
I don't think I can answer everything you asked, but I'll do my best for the parts that I do know how to answer.
What is the recommended way to implement non-IO monad comprehensions in Arrow without making all functions into suspend functions? Or is this actually the way to go?
you can use nullable.eager and either.eager respectively for pure code. Using nullable/either (without .eager) allows you to call suspend functions inside. Using eager means you can only call non-suspend functions. (not all effectual functions in kotlin are marked suspend)
Second: If in addition to non-IO monads (nullable, reader, etc.), I want to have IO - say, reading in a file and parsing it - how would i combine these two effects? Is it correct to say that there would be multiple suspend scopes corresponding to the different monads involved, and I would need to somehow nest these scopes, like I would stack monad transformers in Haskell?
You can use extension functions to emulate Reader. For example:
suspend fun <R> R.doSomething(i: Int): Either<Error, String> = TODO()
combines Reader + IO + Either. You can find a bigger example here from Simon, an Arrow maintainer.

How to keep use cases and entity layer in Functional Clean Architecture pure when having database read/write interactions?

Intro
I've been diving into functional programming in the last few months, and since I'm really intrigued by the Kotlin language, I've been using the Arrow library to toy around with a few things.
A few weeks ago, I've been researching for a guest lecture at university on Clean Architecture and while doing so, I stumbled upon this great blog Post by Mark Seemann, describing how using functional programming automatically leads to Clean Architecture (or with a language like Haskell may the compiler may even enforce Clean Architecture).
That inspired me to come up with a draft (checkout and build of the repo should be a breeze, if you're interested) of a restaurant reservation Software (staying true to Mark Seemann's domain ;) ). However, I'm not entirely sure if the use case layer in this draft can be called pure, and I wanted some feedback from people with more experience and knowledge with FP than myself.
Entity layer
A basic use case is trying to create a new reservation for a certain number of seats in our restaurant. I've modelled the entity layer for that the following way:
fun reservationPossible(
requestedSeats: Int,
reservedSeats: Int,
capacity: Int
): Either<RequestedTooManySeats, ReservationPossible> =
if (reservedSeats + requestedSeats <= capacity) {
ReservationPossible(requestedSeats + reservedSeats).right()
} else {
RequestedTooManySeats.left()
}
const val CAPACITY = 10
object RequestedTooManySeats : Error()
sealed class Error
data class ReservationPossible(val newNumberOfReservedSeats: Int)
Nothing too fancy going on here, just a function checking if a reservation with a certain number of requested seats would be possible or not. Some Error and Result classes are also down below as well as a (for the sake of simplicity) const val to model the capacity of our restaurant.
Frameworks/Adapters #1
To make sense in a real world application, some data would also need to be stored in and loaded from some sort of persistence layer. So, in the outermost layer of our onion architecture, there would be a Database which I mocked for this example:
suspend fun getCurrentlyReservedSeats(): Either<ReadError, Int> {
delay(1) // ... get stuff from db
return 4.right()
}
suspend fun saveReservation(value: String, reservationPossible: ReservationPossible): Either<WriteError, Long> {
delay(1) // ... writing something to db
return 42L.right() // newRecordId
}
abstract class DbError : Error()
object ReadError : DbError()
object WriteError : DbError()
Again, not too much going on here... Just stubs for Database read/write ops. Note however, that (by convention proposed by Arrow) these functions are marked with the suspend modifier as impure functions.
Use Case
So now for the use case, which basically describes our application flow:
get number of currently reserved seats from DB
check if the requested number of seats is still available
if so, persist the new reservation
and return the newly created reservation id
which is translated to code in the reservationUseCase function:
data class UseCaseData(
val requestedSeats: Int,
val reservationName: String,
val getCurrentlyReservedSeats: suspend () -> Either<ReadError, Int>,
val writeVal: suspend (String, ReservationPossible) -> Either<WriteError, Long>,
)
fun reservationUseCase(data: UseCaseData): suspend () -> Either<Error, UseCaseResultData> = {
data.getCurrentlyReservedSeats()
.flatMap { reservationPossible(data.requestedSeats, it, CAPACITY) }
.flatMap { data.writeVal(data.reservationName, it) }
.flatMap { UseCaseResultData(it).right() }
}
data class UseCaseResultData(val newRecordId: Long)
Here is the point, where it gets interesting: This function takes some UseCaseData as input and returns a suspend function to be executed at the program entry like this:
suspend fun main() {
reservationUseCase(
UseCaseData(
requestedSeats = 5,
reservationName = "John Dorian",
::getCurrentlyReservedSeats,
::saveReservation,
)
).invoke().fold(
ifLeft = { throw Exception(it.toString()) },
ifRight = { println(it.newRecordId) },
)
}
So now my questions are:
Can the reservationUseCase function itself be considered pure? I've read some blog post (taking F# as example language, however) suggesting that pure functions which receive impure functions as parameters could be pure, but cannot be guaranteed to be pure. reservationUseCase in this example clearly does receive impure functions with the UseCaseData.
If it can't be considered pure, how could one write a pure use case like the one described above in Kotlin and Arrow?
As you already assume, strictly speaking, reservationUseCase is not a pure function.
The only way I see how you could make it a pure function is to pass all the needed data directly instead of a function which provides access to that data but I doubt that this makes your code finally more clean or nicer to read.
This would lead to the conclusion that use case functions which orchestrate "workflows" can rarely be pure as almost always some interaction with some kind of repository is needed.
If you want some core logic to be pure you would have to extract those into functions which again only accept and return pure data.

design pattern to express that "container" over derived class is derived from container of a base class

I have a class Track which holds a set of Points and represent person location in time. In order to get this result I run an iterative optimization routine combining different data. In order to do it I extend Point with a class OptimizedPoint which holds data for optimization for this point and current value. I also introduce OptimizedTrack which is a collection of OptimizedPoints and additional data needed for optimization associated with the whole track.
I run an optimization on OptimizedTrack and at the last iteration, I return to the user clean data (Track) which only has the result and doesn't have additional data. However, I can not find a way to express with OOP that OptimizedTrack is somehow an extension of the Track and introduce common routines for them. F.e getting a length of the track which should be available for both of them as it only uses data which can be found both in OptimizedTrack and Track
Right now I have such architecture Point{x, y, z}, OptimizedPoint extends Point {additional_data}. Track {array<Point>}, OptimizedTrack {array<OptimizedPoint>, additional_data}. I don't understand how to express that OptimizedTrack is an extension of Track as I can not express array<OptimizedPoint> extens array<Point>. So I can not introduce a common routine length which can be calculated for array and therefore also from array.
I do not insist on my current architecture. It's most probably wrong, I only write it here to express the problem I am facing. How would you propose to refactor my architecture in this case?
I believe that the basic premise of what you are trying to do is faulty if you are following what is considered to be proper use of inheritance to express subtyping relationships.
Inheritance can be used for various purposes and I am not wishing to pontificate upon the subject, but the opinion of most authorities is that inheritance is best and most safely used when used for subtyping. In short, an instance of a subclass should be able to be substituted for an instance of its base class without "breaking" the program (see: Liskov Substitution Principle).
Let us assume that OptimizedPoint is a subtype of Point. Then all the methods defined in class Point when invoked on an instance of OptimizedPoint will continue to function as expected. That means that OptimizedPoint cannot require any more stringent preconditions on any of these method invocations nor can it weaken any of the promissed postconditions that the contract Point has made.
But it is a common fallacy that just becuase OptimizedPoint is a subtype of Point that a container of OptimizedPoint, i.e. OptimizedTrack, is a subtype of a container of Point, i.e Track. This is because you cannot substitute an instance of OptimizedTrack for an instance of Track (due to your not being able to add an instance of Point to an instance of OptimizedTrack).
So, if you are trying to follow "good object-oriented design principles", it is disastrous trying to somehow make OptimizedTrack a subclass of Track, because it can certainly never be a subtype. You can, of course, reuse Track to build OptimizedTrack using composition, i.e. OptimizedTrack would contain within an instance of Track to which methods such as length would be delegated.
I'm not sure why you want to return a Track to your client code after the optimisation process, considering that OptimizedTrack is a Track itself. Below is a quick example of what I think you're trying to achieve (written in Kotlin because is less verbose).
You can achieve a lot more flexibility and solve the type issue if you consider Track to be an iterable object of points of type Point. This way, when you extend OptTrack from Track, you will be able to:
Substitute Track and OptTrack with no problem (even if your optimised track object has not computed a simplified Track object).
Simplify through optimize and return a Track from OptTrack with no issues (the optimize function on Point is irrelevant, you can return an OptPoint inside your Track because it extends the object Point)
open class Point(val x: Int, val y: Int, val z: Int) {
override fun toString(): String =
"Point(${this.x}, ${this.y}, ${this.z})"
}
data class OptPoint(val point: Point, val additional: Int):
Point(point.x, point.y, point.z) {
override fun toString(): String =
"OptPoint(${this.point}, ${this.additional})"
fun optimize(): Point {
return Point(this.x, this.y, this.z)
}
}
open class Track(private val points: Iterable<Point>): Iterable<Point> {
override operator fun iterator(): Iterator<Point> {
return this.points.iterator()
}
override fun toString(): String =
"Track(${this.points})"
}
data class OptTrack(private val points: Iterable<OptPoint>): Track(listOf()) {
override operator fun iterator(): Iterator<Point> {
return this.points.iterator()
}
fun optimize(): Track {
return Track(this.points.map{ it.optimize() })
}
}
fun main(args: Array<String>) {
val track: Track = OptTrack(listOf(
OptPoint(Point(1, 2, 3), 4))).optimize()
println(track)
// Track([Point(1, 2, 3)])
val other: Track = OptTrack(listOf(OptPoint(Point(1, 2, 3), 4)))
println(other)
// OptTrack(points=[OptPoint(Point(1, 2, 3), 4)])
}
In OOP you should prefer object composition to object inheritance. In your problem, I think creating interfaces for point and track could help. In order to achieve the proper result, I think, you should create two interfaces, IPoint & ITrack. Both Track and OptimizedTrack implement the ITrack interface and for common operations, you could create another class that both classes delegate the requests to it. After that you could create an strategy class, taking in an ITrack and returns another optimized ITrack. In the ITrack you could create GetPath which returns a list of objects of type IPoint.

Kotlin benifits of writing helper/util methods without wrapping in class

There are can be two ways of writing helper method in Kotlin
First is
object Helper {
fun doSomething(a: Any, b: Any): Any {
// Do some businesss logic and return result
}
}
Or simply writing this
fun doSomething(a: Any, b: Any): Any {
// Do some businesss logic and return result
}
inside a Helper.kt class.
So my question is in terms of performance and maintainability which is better and why?
In general, your first choice should be top-level functions. If a function has a clear "primary" argument, you can make it even more idiomatic by extracting it as the receiver of an extension function.
The object is nothing more than a holder of the namespace of its member functions. If you find that you have several groups of functions that you want to categorize, you can create several objects for them so you can qualify the calls with the object's name. There's little beyond this going in their favor in this role.
object as a language feature makes a lot more sense when it implements a well-known interface.
There's a third and arguably more idiomatic way: extension functions.
fun Int.add(b: Int): Int = this + b
And to use it:
val x = 1
val y = x.add(3) // 4
val z = 1.add(3) // 4
In terms of maintainability, I find extension functions just as easy to maintain as top-level functions or helper classes. I'm not a big fan of helper classes because they end up acquiring a lot of cruft over time (things people swear we'll reuse but never do, oddball variants of what we already have for special use cases, etc).
In terms of performance, these are all going to resolve more or less the same way - statically. The Kotlin compiler is effectively going to compile all of these down to the same java code - a class with a static method.