Arrow Core
Arrow is the functional companion to Kotlin's Standard Library
Arrow FX
Arrow FX is a next-generation Typed FP Effects Library built to tame side effectful APIs
Arrow Optics
Arrow Optics helps by transforming and computing over immutable data models in Kotlin
Arrow Meta
Arrow Meta is the functional companion to the Kotlin Compiler and its plugin ecosystem
Functional companion to Kotlin's Standard Library
import arrow.core.Either
import arrow.core.Left
import arrow.core.Right
import arrow.core.computations.either
object Lettuce
object Knife
object Salad
sealed class CookingException {
object LettuceIsRotten: CookingException()
object KnifeNeedsSharpening: CookingException()
data class InsufficientAmount(val quantityInGrams : Int): CookingException()
}
typealias NastyLettuce = CookingException.LettuceIsRotten
typealias KnifeIsDull = CookingException.KnifeNeedsSharpening
typealias InsufficientAmountOfLettuce = CookingException.InsufficientAmount
fun takeFoodFromRefrigerator(): Either<NastyLettuce, Lettuce> = Right(Lettuce)
fun getKnife(): Either<KnifeIsDull, Knife> = Right(Knife)
fun prepare(tool: Knife, ingredient: Lettuce): Either<InsufficientAmountOfLettuce, Salad> = Left(InsufficientAmountOfLettuce(5))
suspend fun main() {
//sampleStart
suspend fun prepareLunch(): Either<CookingException, Salad> =
either<CookingException, Salad> {
val lettuce = !takeFoodFromRefrigerator()
val knife = !getKnife()
val lunch = !prepare(knife, lettuce)
lunch
}
println(prepareLunch())
//sampleEnd
}
import arrow.fx.IO
import arrow.fx.extensions.fx
import kotlinx.coroutines.newSingleThreadContext
//sampleStart
val Computation = newSingleThreadContext("Computation")
val BlockingIO = newSingleThreadContext("Blocking IO")
val UI = newSingleThreadContext("UI")
suspend fun main(): Unit = IO.fx {
continueOn(Computation)
val t1 = !effect { Thread.currentThread().name }
continueOn(BlockingIO)
val t2 = !effect { Thread.currentThread().name }
continueOn(UI)
val t3 = !effect { Thread.currentThread().name }
!effect { println("$t1 ~> $t2 ~> $t3") }
}.suspended()
//sampleEnd
package example
import arrow.core.*
import arrow.optics.*
data class Street(val number: Int, val name: String) {
companion object {
val name: Lens<Street, String> = Lens(Street::name)
{ street, name -> street.copy(name = name) }
}
}
data class Address(val city: String, val street: Street) {
companion object {
val street: Lens<Address, Street> = Lens(Address::street)
{ address, street -> address.copy(street = street) }
}
}
data class Company(val name: String, val address: Address) {
companion object {
val address: Lens<Company, Address> = Lens(Company::address)
{ company, address -> company.copy(address = address) }
}
}
data class Employee(val name: String, val company: Company?) {
companion object {
val company: Optional<Employee, Company> = Optional(
getOption = { it.company.toOption() },
set = { source, focus -> source.copy(company = focus) }
)
}
}
fun main() {
//sampleStart
val john =
Employee("Audrey Tang",
Company("Arrow",
Address("Functional city",
Street(42, "lambda street"))))
val modify = Employee.company.address.street.name
.modify(john, String::toUpperCase)
println(modify)
//sampleEnd
}
val <A> Optional<A, Company>.address: Optional<A, Address>
get() = this compose Company.address
val <A> Optional<A, Address>.street: Optional<A, Street>
get() = this compose Address.street
val <A> Optional<A, Street>.name: Optional<A, String>
get() = this compose Street.name
val Meta.comprehensions: Plugin
get() =
"comprehensions" {
meta(
quote(KtDotQualifiedExpression::containsFxBlock) { fxExpression ->
Transform.replace(
replacing = fxExpression,
newDeclaration = toFlatMap(fxExpression).expression
)
}
)
}
//sampleStart
+ service1().flatMap { result1 ->
+ service2(result1).flatMap { result2 ->
+ service3(result2).map { result3 ->
+ Result(result3)
+ }
+ }
+ }
- val result1 by service1()
- val result2 by service2(result1)
- val result3 by service3(result2)
- Result(result3)
//sampleEnd