Arrow of outrageous fortune
Functional Programming in Kotlin
David Rawson
What is Kotlin?
class Author(val firstName: String,
val lastName: String) extends Comparable[Author] {
override def compareTo(that: Author) = {
val lastNameComp = this.lastName compareTo that.lastName
if (lastNameComp != 0) lastNameComp
else this.firstName compareTo that.firstName
}
}
object Author {
def loadAuthorsFromFile(file: java.io.File): List[Author] = ???
}
What is Kotlin?
class Author(val firstName: String,
val lastName: String) extends Comparable[Author] {
override def compareTo(that: Author) = {
val lastNameComp = this.lastName compareTo that.lastName
if (lastNameComp != 0) lastNameComp
else this.firstName compareTo that.firstName
}
}
object Author {
def loadAuthorsFromFile(file: java.io.File): List[Author] = ???
}
What is Kotlin?
import java.io.File
class Author(
val firstName: String,
val lastName: String
) : Comparable<Author> {
override fun compareTo(that: Author) {
val lastNameComp = this.lastName.compareTo(that.lastName)
return if (lastNameComp != 0) {
lastNameComp
} else {
this.firstName.compareTo(that.firstName)
}
}
}
object Authors {
fun loadAuthorsFromFile(file: File): List<Author> = TODO()
}
What is Kotlin?
import java.io.File
class Author(
val firstName: String,
val lastName: String
) : Comparable<Author> {
override fun compareTo(that: Author) = compareValuesBy(
this,
that,
Author::lastName,
Author::firstName
)
}
object Authors {
fun loadAuthorsFromFile(file: File): List<Author> = TODO()
}
Why Kotlin?
- Tooling
Why Kotlin?
- Tooling
Why Kotlin?
- Tooling
plugins {
java
kotlin("jvm") version "1.4.10"
}
group = "org.example"
version = "1.0-SNAPSHOT"
repositories {
mavenCentral()
}
dependencies {
implementation(kotlin("stdlib"))
testCompile("junit", "junit", "4.12")
}
Why Kotlin?
- Tooling
- Compilation targets
Why Kotlin?
- Tooling
- Compilation targets
Why Kotlin?
- Tooling
- Targets
Why Kotlin?
- Tooling
- Targets
- Sympathetic to FP
Why Kotlin?
- Sympathetic to FP
Why Kotlin?
- Sympathetic to FP
Why Kotlin?
- Sympathetic to FP
Why Kotlin?
- Sympathetic to FP
Why Kotlin?
- Sympathetic to FP
Why Kotlin?
- Sympathetic to FP
fun main() {
val immutable = listOf(1, 2, 3)
val mutable = mutableListOf(1, 2, 3)
val david = Person(Name("David"), Age(21))
val olderDavid = david.copy(age = Age(22))
}
Why Kotlin?
- Sympathetic to FP
Why Kotlin?
- Sympathetic to FP
Why not Kotlin?
Why not Kotlin?
- Slower builds than POJ
Why not Kotlin?
- Must be carefully introduced into Java project
- Slower builds than POJ
Why not Kotlin?
- Must be carefully introduced into Java project
- Language features can be abused
- Slower builds than POJ
FP in Kotlin?
FP in Kotlin?
https://arrow-kt.io/
FP in Kotlin?
Our initial positive impressions of Arrow were confirmed when using it to build applications that are now in production.
FP in Kotlin?
core
FP in Kotlin?
fx
FP in Kotlin?
optics
FP in Kotlin?
meta
FP in Kotlin?
Arrow Core
FP in Kotlin?
-
Typeclass-o-pedia
Arrow Core
FP in Kotlin?
-
Typeclass-o-pedia
Arrow Core
FP in Kotlin?
-
Typeclass-o-pedia
Arrow Core
FP in Kotlin?
-
Typeclass-o-pedia
Arrow Core
FP in Kotlin?
-
Typeclass-o-pedia
Arrow Core
FP in Kotlin?
-
Typeclass-o-pedia
Arrow Core
-
Error handling
data class Name(val name: String)
data class Age(val age: Int)
data class Person(val name: Name, val age: Age)
object TestClassic {
fun makeName(name: String?): Name {
if (name == null || name == "") {
throw IllegalArgumentException("Name cannot be null or empty")
}
return Name(name)
}
fun makeAge(age: String): Age {
val parsedAge = age.toInt()
require(0 <= parsedAge) {
"Ages must be non-negative"
}
return Age(parsedAge)
}
}
fun main(args: Array<String>) {
try {
val name = TestClassic.makeName(args[0])
val age = TestClassic.makeAge(args[1])
val person = Person(name, age)
println(person)
} catch (e: Exception) {
println("Could not make a person!")
}
}
import arrow.core.Either
object TestEither {
fun makeName(name: String?): Either<Throwable, Name> {
return when (name) {
null, "" -> return Either.Left(IllegalArgumentException())
else -> Either.Right(Name(name))
}
}
fun makeAge(age: String): Either<Throwable, Age> =
Either.catch {
age.toInt()
}.flatMap {
when {
it <= 0 -> Either.Left(IllegalArgumentException())
else -> Either.Right(it)
}
}.map {
Age(it)
}
}
import arrow.core.Either
fun main(args: Array<String>) {
TestEither.makeName(args[0]).flatMap { name ->
TestEither.makeAge(args[1]).map { age ->
Person(name, age)
}
}.fold(
ifLeft = {
"Could not make a person!"
},
ifRight = {
it.toString()
}
).let {
println(it)
}
}
import arrow.core.computations.either
suspend fun main(args: Array<String>) {
either<Throwable, Person> {
val name = TestEither.makeName(args[0]).bind()
val age = TestEither.makeAge(args[1]).bind()
Person(name, age)
}.fold(
ifLeft = {
"Could not make a person!"
} ,
ifRight = {
it
}
).let {
println(it)
}
}
https://medium.com/@heyitsmohit/writing-kotlin-compiler-plugin-with-arrow-meta-cf7b3689aa3e
https://medium.com/@heyitsmohit/writing-kotlin-compiler-plugin-with-arrow-meta-cf7b3689aa3e
intercepts
data class Age(val age: Int)
object TestClassic {
fun makeAge(age: String): Age {
val parsedAge = age.toInt()
require(0 < parsedAge) {
"Ages must be positive"
}
return Age(parsedAge)
}
}
data class PositiveInt(val value: Int) {
init {
require(0 < value) {
"Should be positive"
}
}
}
@Refinement
inline class PositiveInt(val value: Int) {
companion object : Refined<Int, PositiveInt> {
override val target : (Int) -> PositiveInt = ::PositiveInt
override val validate: Int.() -> Map<String, Boolean> = {
mapOf(
"Should be > 0" to (this > 0)
)
}
}
}
@Coercion
fun Int.positive(): PositiveInt? =
PositiveInt.from(this)
Functional Programming in Kotlin
By David Rawson
Functional Programming in Kotlin
- 849