Monads for Kotlin

Overview

kotlin-monads

An attempt to implement monads in Kotlin, deeply inspired by Haskell monads, but restricted within the Kotlin type system.

The monad type

Monadic types are represented by the Monad<M, T> interface, where M should be the type of the implementation with only its T star-projected. Examples: Maybe<T> : Monad<Maybe<*>, T>, State<S, T> : Monad<State<S, *>, T>.

With Monad defined in this way, we are almost able to say in terms of the Kotlin type system that a function returns the same Monad implementation but with a different type argument R instead of T:

fun <T, R, M : Monad<M, *>> Monad<M, T>.map(f: (T) -> R) = bind { returns(f(it)) }

val m = just(3).map { it * 2 } as Maybe

We still need the downcast as Maybe, but at least it's checked.

Usage

Add as a dependency:

repositories {
    ...
    maven { url 'https://jitpack.io' }
}

dependencies {
    ...
    compile 'com.github.h0tk3y:kotlin-monads:0.5'
}

See the usage examples in tests.

How to implement a monad

Monad<M, T> is defined as follows:

interface Return<M> {
    fun <T> returns(t: T): Monad<M, T>
}

interface Monad<This, out T> {
    infix fun <R> bind(f: Return<This>.(T) -> Monad<This, R>): Monad<This, R>
}

The monad implementation should only provide one function bind (Haskell: >>=), no separate return is there, instead, if you look at the signature of bind, you'll see that the function to bind with is f: Return<This>.(T) -> Monad<This, R>. It means that a Monad<M, T> implementation should provide the Return<M> as well and pass it to f each time, so that inside f its returns could be used:

just(3) bind { returns(it * it) }

There seems to be no direct equivalent to Haskell return, which could be used outside any context like bind blocks. Outside the bind blocks, you should either wrap the values into your monads manually or require a Return<M>, which can wrap T into Monad<M, T> for you.

Mind the monad laws. A correct monad implementation follows these three rules (rephrased in terms of kotlin-monads):

  1. Left identity: returns(x) bind { f(it) } should be equivalent to f(x)

  2. Right identity: m bind { returns(it) } should be equivalent to m

  3. Associativity: m bind { f(it) } bind { g(it) } should be equivalent to m bind { f(it) bind { g(it) } }

Also, it's good to make the return type of bind narrower, e.g. bind of Maybe<T> would rather return Maybe<R> than Monad<Maybe<*>, R>, it allows not to cast the result of a bind called on a known monad type.

val m = monadListOf(1, 2, 3) bind { monadListOf("$it", "$it") } // already `MonadList<String>`, no need to cast

Example implementation:

sealed class Maybe<out T> : Monad<Maybe<*>, T> {
    class Just<T>(val value: T) : Maybe<T>()
    class None : Maybe<Nothing>()

    override fun <R> bind(f: Binder<Maybe<*>, T, R>): Maybe<R> = when (this) {
        is Just -> f(MaybeReturn, value) as Maybe
        is None -> None()
    }
}

object MaybeReturn : Return<Maybe<*>> {
    override fun <T> returns(t: T) = Maybe.Just(t)
}

Monads implementations bundled

  • Maybe<T>
  • Either<F, T>
  • MonadList<T>
  • Reader<E, T>
  • Writer<T> (no monoid for now, just String)
  • State<S, T>

Do notation

With the power of Kotlin coroutines, we can even have an equivalent of the Haskell do notation:

Simple example that performs a monadic list nondeterministic expansion:

val m = doReturning(MonadListReturn) {
    val x = bind(monadListOf(1, 2, 3))
    val y = bind(monadListOf(x, x + 1))
    monadListOf(y, x * y)
}

assertEquals(monadListOf(1, 1, 2, 2, 2, 4, 3, 6, 3, 9, 4, 12), m)

Or applied to an existing monad for convenience:

val m = monadListOf(1, 2, 3).bindDo { x ->
    val y = bind(monadListOf(x, x + 1))
    monadListOf(y, x * y)
}

This is effectively equivalent to the following code written with only simple bind:

val m = monadListOf(1, 2, 3).bind { x ->
    monadListOf(x, x + 1).bind { y -> 
        monadList(y, x * y)
    }
}

Note that, with simple bind, each transformation requires another inner scope if it uses the variables bound outside, which would lead to some kind of callback hell. This problem is effectively solved using the Kotlin coroutines: the compiler performs the CPS transformation of a plain code block under the hood. However, this coroutines use case is somewhat out of conventions: it might resume the same continuation several times and uses quite a dirty hack to do that.

The result type parameter (R in Monad<M, R>) is usually inferred, and the compiler controls the flow inside a do block, but still you need to downcast the Monad<M, R> to your actual monad type (e.g. Monad<Maybe<*>, R> to Maybe), because the type system doesn't seem to allow this to be done automatically (if you know a way, please tell me).

Be careful with mutable state in do blocks, since all continuation calls will share it, sometimes resulting into counter-intuitive results:

 val m = doReturning(MonadListReturn) {
     for (i in 1..10)
         bind(monadListOf(0, 0))
     returns(0)
 } as MonadList

One would expect 1024 items here, but the result only contains 11! That's because i is mutable and is shared between all the calls that bind makes.

You might also like...
RoomJetpackCompose is an app written in Kotlin and shows a simple solution to perform CRUD operations in the Room database using Kotlin Flow in clean architecture.
RoomJetpackCompose is an app written in Kotlin and shows a simple solution to perform CRUD operations in the Room database using Kotlin Flow in clean architecture.

RoomJetpackCompose is an app written in Kotlin and shows a simple solution to perform CRUD operations in the Room database using Kotlin Flow in clean architecture.

Create an application with Kotlin/JVM and Kotlin/JS, and explore features around code sharing, serialization, server- and client
Create an application with Kotlin/JVM and Kotlin/JS, and explore features around code sharing, serialization, server- and client

Practical Kotlin Multiplatform on the Web 본 저장소는 코틀린 멀티플랫폼 기반 웹 프로그래밍 워크숍(강좌)을 위해 작성된 템플릿 프로젝트가 있는 곳입니다. 워크숍 과정에서 코틀린 멀티플랫폼을 기반으로 프론트엔드(front-end)는 Ko

Create an application with Kotlin/JVM and Kotlin/JS, and explore features around code sharing, serialization, server- and client
Create an application with Kotlin/JVM and Kotlin/JS, and explore features around code sharing, serialization, server- and client

Building a Full Stack Web App with Kotlin Multiplatform 본 저장소는 INFCON 2022에서 코틀린 멀티플랫폼 기반 웹 프로그래밍 핸즈온랩을 위해 작성된 템플릿 프로젝트가 있는 곳입니다. 핸즈온 과정에서 코틀린 멀티플랫폼을

Kotlin library for Android
Kotlin library for Android

KAndroid Kotlin library for Android providing useful extensions to eliminate boilerplate code in Android SDK and focus on productivity. Download Downl

Type-safe time calculations in Kotlin, powered by generics.

Time This library is made for you if you have ever written something like this: val duration = 10 * 1000 to represent a duration of 10 seconds(in mill

📒 NotyKT is a complete 💎Kotlin-stack (Backend + Android) 📱 application built to demonstrate the use of Modern development tools with best practices implementation🦸.
📒 NotyKT is a complete 💎Kotlin-stack (Backend + Android) 📱 application built to demonstrate the use of Modern development tools with best practices implementation🦸.

NotyKT 🖊️ NotyKT is the complete Kotlin-stack note taking 🖊️ application 📱 built to demonstrate a use of Kotlin programming language in server-side

A Kotlin DSL wrapper around the mikepenz/MaterialDrawer library.
A Kotlin DSL wrapper around the mikepenz/MaterialDrawer library.

MaterialDrawerKt Create navigation drawers in your Activities and Fragments without having to write any XML, in pure Kotlin code, with access to all t

Kotlin-based modern RecyclerView rendering weapon
Kotlin-based modern RecyclerView rendering weapon

Read this in other languages: 中文, English, Changelog Yasha Item introduction: Kotlin-based modern RecyclerView rendering weapon Item Features: No Adap

{ } Declarative Kotlin DSL for choreographing Android transitions
{ } Declarative Kotlin DSL for choreographing Android transitions

Transition X Kotlin DSL for choreographing Android Transitions TransitionManager makes it easy to animate simple changes to layout without needing to

Comments
  • Add license

    Add license

    I just noticed that there is no explicit license for this project. Would you consider to add an Apache or similar license on it so it can be used on other projects? Thanks!

    opened by jrgonzalezg 2
  • 1.1.0 release

    1.1.0 release

    Hey @h0tk3y, awesome project!

    I'm goint to use it in a POC to teach some friends FP here https://github.com/antoniolg/Bandhook-Kotlin/pull/28

    Looks like there is no release for Kotlin 1.1.0 and I can't depend on the current release. Any chances we can have a release for 1.1.0? Thanks!

    opened by raulraja 1
Releases(0.5)
Owner
Sergey Igushkin
Kotlin/Java Programmer
Sergey Igushkin
Real life Kotlin Multiplatform project with an iOS application developed in Swift with SwiftUI, an Android application developed in Kotlin with Jetpack Compose and a backed in Kotlin hosted on AppEngine.

Conferences4Hall Real life Kotlin Multiplatform project with an iOS application developed in Swift with SwiftUI, an Android application developed in K

Gérard Paligot 98 Dec 15, 2022
Android + Kotlin + Github Actions + ktlint + Detekt + Gradle Kotlin DSL + buildSrc = ❤️

kotlin-android-template ?? A simple Github template that lets you create an Android/Kotlin project and be up and running in a few seconds. This templa

Nicola Corti 1.5k Jan 3, 2023
LifecycleMvp 1.2 0.0 Kotlin is MVP architecture implementation with Android Architecture Components and Kotlin language features

MinSDK 14+ Download Gradle Add to project level build.gradle allprojects { repositories { ... maven { url 'https://jitpack.io' }

Robert 20 Nov 9, 2021
Opinionated Redux-like implementation backed by Kotlin Coroutines and Kotlin Multiplatform Mobile

CoRed CoRed is Redux-like implementation that maintains the benefits of Redux's core idea without the boilerplate. No more action types, action creato

Kittinun Vantasin 28 Dec 10, 2022
👋 A common toolkit (utils) ⚒️ built to help you further reduce Kotlin boilerplate code and improve development efficiency. Do you think 'kotlin-stdlib' or 'android-ktx' is not sweet enough? You need this! 🍭

Toolkit [ ?? Work in progress ⛏ ?? ??️ ?? ] Snapshot version: repositories { maven("https://s01.oss.sonatype.org/content/repositories/snapshots") }

凛 35 Jul 23, 2022
An app architecture for Kotlin/Native on Android/iOS. Use Kotlin Multiplatform Mobile.

An app architecture for Kotlin/Native on Android/iOS. Use Kotlin Multiplatform Mobile. 项目架构主要分为原生系统层、Android/iOS业务SDK层、KMM SDK层、KMM业务逻辑SDK层、iOS sdkfra

libill 4 Nov 20, 2022
Provides Kotlin libs and some features for building Kotlin plugins

Kotlin Plugin Provides Kotlin libs and some features for building awesome Kotlin plugins. Can be used instead of CreeperFace's KotlinLib (don't use to

null 3 Dec 24, 2021
Notes-App-Kotlin - Notes App Built Using Kotlin

Notes-App-Kotlin Splash Screen Home Page Adding New Notes Filter Feature Search

Priyanka 4 Oct 2, 2022
Kotlin-client-dsl - A kotlin-based dsl project for a (Client) -> (Plugin) styled program

kotlin-client-dsl a kotlin-based dsl project for a (Client) -> (Plugin) styled p

jackson 3 Dec 10, 2022
A Kotlin Native program to show the time since a date, using Kotlin LibUI

TimeSince A Kotlin Native program to show the time since a date, using Kotlin LibUI Report Bug . Request Feature About The Project TimeSince is a Kotl

Russell Banks 2 May 6, 2022