nw-fp-kotlin

Kotlin language-specific patterns with Arrow, Raise DSL, and coroutine-based effects

322 stars

Best use case

nw-fp-kotlin is best used when you need a repeatable AI agent workflow instead of a one-off prompt.

Kotlin language-specific patterns with Arrow, Raise DSL, and coroutine-based effects

Teams using nw-fp-kotlin should expect a more consistent output, faster repeated execution, less prompt rewriting.

When to use this skill

  • You want a reusable workflow that can be run more than once with consistent structure.

When not to use this skill

  • You only need a quick one-off answer and do not need a reusable workflow.
  • You cannot install or maintain the underlying files, dependencies, or repository context.

Installation

Claude Code / Cursor / Codex

$curl -o ~/.claude/skills/nw-fp-kotlin/SKILL.md --create-dirs "https://raw.githubusercontent.com/nWave-ai/nWave/main/nWave/skills/nw-fp-kotlin/SKILL.md"

Manual Installation

  1. Download SKILL.md from GitHub
  2. Place it in .claude/skills/nw-fp-kotlin/SKILL.md inside your project
  3. Restart your AI agent — it will auto-discover the skill

How nw-fp-kotlin Compares

Feature / Agentnw-fp-kotlinStandard Approach
Platform SupportNot specifiedLimited / Varies
Context Awareness High Baseline
Installation ComplexityUnknownN/A

Frequently Asked Questions

What does this skill do?

Kotlin language-specific patterns with Arrow, Raise DSL, and coroutine-based effects

Where can I find the source code?

You can find the source code on GitHub using the link provided at the top of the page.

SKILL.md Source

# FP in Kotlin -- Functional Software Crafter Skill

Cross-references: [fp-principles](../nw-fp-principles/SKILL.md) | [fp-domain-modeling](../nw-fp-domain-modeling/SKILL.md) | [pbt-jvm](../nw-pbt-jvm/SKILL.md)

## When to Choose Kotlin

- Best for: gentlest FP onboarding for Java teams | Android | coroutines | pragmatic FP with great IDE support
- Not ideal for: teams wanting compiler-enforced purity | full effect systems | higher-kinded type abstractions

## [STARTER] Quick Setup

```bash
mkdir 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
```

## [STARTER] Type System for Domain Modeling

### Choice Types (Sealed Hierarchies)

```kotlin
sealed 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.

### Record Types and Domain Wrappers

```kotlin
data 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.

### [INTERMEDIATE] Validated Construction with Raise DSL

```kotlin
import 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)
}
```

## [INTERMEDIATE] Composition Style

### Scope Functions as Lightweight Pipelines

```kotlin
val result = rawOrder
    .let(::validateOrder)
    .map(::priceOrder)
    .map(::confirmOrder)
```

### Arrow Raise DSL (Direct-Style Typed Errors)

```kotlin
import 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`.

### Accumulating Errors

```kotlin
import 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) }
}
```

## [INTERMEDIATE] Effect Management

Kotlin is impure by default. `suspend` marks functions performing side effects or async work.

### Pure Core / Imperative Shell with Coroutines

```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)
    }
}
```

### [ADVANCED] Hexagonal Architecture with Interfaces

```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.

## [INTERMEDIATE] Testing

**Frameworks**: Kotest (test framework + PBT) | jqwik (PBT on JUnit 5) | MockK (coroutine-aware mocking). See [pbt-jvm](../nw-pbt-jvm/SKILL.md) for detailed PBT patterns.

### Property Test (Kotest)

```kotlin
import 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
            }
        }
    }
})
```

### Custom Generator

```kotlin
val 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")
}
```

## [ADVANCED] Idiomatic Patterns

### Null Safety as Option Alternative

```kotlin
fun 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 Maturity Caveat

Arrow is capable with active development, but carries honest risks:

- **Context receivers/parameters** (Raise DSL relies on) are still experimental in Kotlin. Feature renamed and redesigned multiple times.
- Arrow's API changed significantly between 1.x and 2.x, requiring substantial rewrites.
- Smaller contributor base than ZIO or Cats Effect.
- For long-term stability needs, evaluate whether Arrow's API surface will remain stable for your project's lifetime.

## Common Pitfalls

1. **Arrow API instability**: Pin versions carefully. Read migration guides before major upgrades.
2. **No higher-kinded types**: Use concrete types or Arrow's Raise DSL as abstraction mechanism.
3. **FP is library-dependent**: Without Arrow, Kotlin FP limited to null safety, sealed classes, and extensions.
4. **Coroutine complexity**: `CoroutineScope`, `Dispatcher`, `SupervisorJob` interactions are subtle. Test with `runTest`.

Related Skills

nw-ux-web-patterns

322
from nWave-ai/nWave

Web UI design patterns for product owners. Load when designing web application interfaces, writing web-specific acceptance criteria, or evaluating responsive designs.

nw-ux-tui-patterns

322
from nWave-ai/nWave

Terminal UI and CLI design patterns for product owners. Load when designing command-line tools, interactive terminal applications, or writing CLI-specific acceptance criteria.

nw-ux-principles

322
from nWave-ai/nWave

Core UX principles for product owners. Load when evaluating interface designs, writing acceptance criteria with UX requirements, or reviewing wireframes and mockups.

nw-ux-emotional-design

322
from nWave-ai/nWave

Emotional design and delight patterns for product owners. Load when designing onboarding flows, empty states, first-run experiences, or evaluating the emotional quality of an interface.

nw-ux-desktop-patterns

322
from nWave-ai/nWave

Desktop application UI patterns for product owners. Load when designing native or cross-platform desktop applications, writing desktop-specific acceptance criteria, or evaluating panel layouts and keyboard workflows.

nw-user-story-mapping

322
from nWave-ai/nWave

User story mapping for backlog management and outcome-based prioritization. Load during Phase 2.5 (User Story Mapping) to produce story-map.md and prioritization.md.

nw-tr-review-criteria

322
from nWave-ai/nWave

Review dimensions and scoring for root cause analysis quality assessment

nw-tlaplus-verification

322
from nWave-ai/nWave

TLA+ formal verification for design correctness and PBT pipeline integration

nw-test-refactoring-catalog

322
from nWave-ai/nWave

Detailed refactoring mechanics with step-by-step procedures, and test code smell catalog with detection patterns and before/after examples

nw-test-organization-conventions

322
from nWave-ai/nWave

Test directory structure patterns by architecture style, language conventions, naming rules, and fixture placement. Decision tree for selecting test organization strategy.

nw-test-design-mandates

322
from nWave-ai/nWave

Four design mandates for acceptance tests - hexagonal boundary enforcement, business language abstraction, user journey completeness, walking skeleton strategy, and pure function extraction

nw-tdd-review-enforcement

322
from nWave-ai/nWave

Test design mandate enforcement, test budget validation, 5-phase TDD validation, and external validity checks for the software crafter reviewer