Skip to content

Instantly share code, notes, and snippets.

@bastman
Created July 27, 2023 14:57
Show Gist options
  • Save bastman/8045d7e6b664526e58c8423bef19b50a to your computer and use it in GitHub Desktop.
Save bastman/8045d7e6b664526e58c8423bef19b50a to your computer and use it in GitHub Desktop.
railway-oriented-programming (kotlin, playgorund)
package com.example.demo
// based on ...
// https://www.reddit.com/r/Kotlin/comments/15b16px/little_example_of_railway_oriented_programming_in/
// https://github.com/GabrielLasso/Kotlin-railway-example
fun main() {
val pipeline: (data: Either<Throwable, Person>) -> Either<Throwable, Response> =
(::validate `→` ::save `→` ::notify `→` ::buildResponse)
val validInput = Person("Gabriel", 26)
val validResponse: Either<Throwable, Response> = validInput
.let { Either.Right(it) }
.let(pipeline::invoke)
val invalidInput = Person("Invalid age", -3)
val invalidResponse: Either<Throwable, Response> = invalidInput
.let { Either.Right(it) }
.let(pipeline::invoke)
println("input: $validInput ==> response: $validResponse")
println("input: $invalidInput ==> response: $invalidResponse")
}
sealed class Either<out L, out R> {
data class Left<L>(val value: L) : Either<L, Nothing>()
data class Right<R>(val value: R) : Either<Nothing, R>()
}
typealias Arrow<A, B> = (A) -> B
infix fun <A, B, C> Arrow<A, B>.`→`(f: Arrow<B, C>): Arrow<A, C> {
return {
f(this(it))
}
}
data class Person(val name: String, val age: Int)
data class Response(val body: String, val code: Int)
fun validate(data: Either<Throwable, Person>): Either<Throwable, Person> {
if (data is Either.Left) {
return data
}
data as Either.Right
if (data.value.age < 0) {
return Either.Left(Exception("Age must be non negative"))
}
return data
}
fun save(data: Either<Throwable, Person>): Either<Throwable, Person> {
if (data is Either.Left) {
return data
}
println("Saved $data")
return data
}
fun notify(data: Either<Throwable, Person>): Either<Throwable, Person> {
if (data is Either.Left) {
return data
}
println("Notified user $data")
return data
}
fun buildResponse(data: Either<Throwable, Person>): Either<Throwable, Response> {
if (data is Either.Left) {
return data
}
return Either.Right(Response("Ok: $data", 200))
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment