nw-fp-kotlin
Kotlin language-specific patterns with Arrow, Raise DSL, and coroutine-based effects
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
Manual Installation
- Download SKILL.md from GitHub
- Place it in
.claude/skills/nw-fp-kotlin/SKILL.mdinside your project - Restart your AI agent — it will auto-discover the skill
How nw-fp-kotlin Compares
| Feature / Agent | nw-fp-kotlin | Standard Approach |
|---|---|---|
| Platform Support | Not specified | Limited / Varies |
| Context Awareness | High | Baseline |
| Installation Complexity | Unknown | N/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
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
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
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
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
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
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
Review dimensions and scoring for root cause analysis quality assessment
nw-tlaplus-verification
TLA+ formal verification for design correctness and PBT pipeline integration
nw-test-refactoring-catalog
Detailed refactoring mechanics with step-by-step procedures, and test code smell catalog with detection patterns and before/after examples
nw-test-organization-conventions
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
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
Test design mandate enforcement, test budget validation, 5-phase TDD validation, and external validity checks for the software crafter reviewer