Install any skill in seconds. Free to start, no credit card required.
Get Started Free →Kotlin language-specific patterns with Arrow, Raise DSL, and coroutine-based effects
.claude/skills/nwave-ai-nw-fp-kotlin/SKILL.md| Test case | Without → With | Effect | Δ tokens | Δ turns |
|---|---|---|---|---|
| case-02 | ✓→✓ | = Same ✓ | 42% | 0% |
| case-07 | ✓→✓ | = Same ✓ | 56% | 0% |
| case-03 | ✓→✓ | = Same ✓ | 38% | 0% |
| case-04 | ✓→✓ | = Same ✓ | 49% | 0% |
| case-05 | ✓→✓ | = Same ✓ | 36% | 0% |
Cross-references: fp-principles | fp-domain-modeling | pbt-jvm
bashmkdir order-service && cd order-service gradle init --type kotlin-application --dsl kotlin # Add arrow-core, arrow-fx-coroutines, kotest-property to build.gradle.kts ./gradlew build && ./gradlew test
kotlinsealed interface PaymentMethod { data class CreditCard(val cardNumber: String, val expiryDate: String) : PaymentMethod data class BankTransfer(val accountNumber: String) : PaymentMethod data object Cash : PaymentMethod }
Exhaustive when expressions ensure all cases handled.
kotlindata class Customer( val customerId: CustomerId, val customerName: CustomerName, val customerEmail: EmailAddress ) @JvmInline value class OrderId(val value: Int) @JvmInline value class EmailAddress private constructor(val value: String) { companion object { fun from(raw: String): Either<ValidationError, EmailAddress> = if ("@" in raw) EmailAddress(raw).right() else InvalidEmail(raw).left() } }
Value classes inlined by compiler -- zero allocation overhead.
kotlinimport arrow.core.raise.Raise import arrow.core.raise.ensure context(Raise<ValidationError>) fun validateEmail(raw: String): EmailAddress { ensure(raw.contains("@")) { InvalidEmail(raw) } return EmailAddress.unsafeCreate(raw) }
kotlinval result = rawOrder .let(::validateOrder) .map(::priceOrder) .map(::confirmOrder)
kotlinimport arrow.core.raise.either fun placeOrder(raw: RawOrder): Either<OrderError, Confirmation> = either { val validated = validateOrder(raw).bind() val priced = priceOrder(validated).bind() confirmOrder(priced).bind() }
Looks imperative, behaves functionally. bind() short-circuits on Left.
kotlinimport arrow.core.raise.zipOrAccumulate fun validateCustomer(raw: RawCustomer): Either<NonEmptyList<ValidationError>, Customer> = either { zipOrAccumulate( { validateName(raw.name) }, { validateEmail(raw.email) }, { validateAddress(raw.address) } ) { name, email, address -> Customer(name, email, address) } }
Kotlin is impure by default. suspend marks functions performing side effects or async work.
kotlin// Pure domain logic (no suspend, no side effects) object OrderDomain { fun calculateDiscount(order: Order): Discount = if (order.lines.size > 10) Discount(0.1) else Discount(0.0) } // Imperative shell (suspend = side effects) class OrderService( private val orderRepo: OrderRepository, private val pricingService: PricingService ) { suspend fun placeOrder(raw: RawOrder): Either<OrderError, Confirmation> = either { val validated = OrderDomain.validateOrder(raw).bind() val priced = pricingService.price(validated).bind() orderRepo.save(priced) Confirmation(priced.orderId) } }
kotlin// Port: interface defining capability interface OrderRepository { suspend fun findOrder(id: OrderId): Order? suspend fun saveOrder(order: Order) } // Adapter: concrete implementation class PostgresOrderRepository(private val db: Database) : OrderRepository { override suspend fun findOrder(id: OrderId): Order? = db.query("SELECT * FROM orders WHERE id = ?", id.value) override suspend fun saveOrder(order: Order) = db.execute("INSERT INTO orders ...", order) }
DI: constructor injection via Koin, Dagger/Hilt, or manual wiring.
Frameworks: Kotest (test framework + PBT) | jqwik (PBT on JUnit 5) | MockK (coroutine-aware mocking). See pbt-jvm for detailed PBT patterns.
kotlinimport io.kotest.core.spec.style.FunSpec import io.kotest.property.forAll class OrderSpec : FunSpec({ test("serialization round-trips") { forAll(orderArb) { order -> deserialize(serialize(order)) == order.right() } } test("validated orders have positive totals") { forAll(rawOrderArb) { raw -> when (val result = validateOrder(raw)) { is Either.Left -> true is Either.Right -> result.value.total.value > 0 } } } })
kotlinval emailArb: Arb<EmailAddress> = arbitrary { val user = Arb.string(minSize = 1, maxSize = 10, codepoints = Codepoint.az()).bind() val domain = Arb.string(minSize = 1, maxSize = 8, codepoints = Codepoint.az()).bind() EmailAddress.unsafeCreate("$user@$domain.com") }
kotlinfun findCustomer(id: CustomerId): Customer? = ... val email = findCustomer(id)?.customerEmail?.value // Use Either/Raise when you need error context context(Raise<CustomerError>) fun getCustomer(id: CustomerId): Customer = findCustomer(id) ?: raise(CustomerNotFound(id))
Arrow is capable with active development, but carries honest risks:
CoroutineScope, Dispatcher, SupervisorJob interactions are subtle. Test with runTest.Other measured skills in the registry, with their headline benchmark lift.