Skill: ivangdavila/kotlin (version 1.0.1)

--- name: Kotlin slug: kotlin version: 1.0.1 description: Build robust Android and multiplatform apps with Kotlin idioms, coroutines, and null safety. metadata: {"clawdbot":{"emoji":"🟠","requires":{"bins":["kotlin"]},"os":["linux","darwin","win32"]}} --- ## Quick Reference | Topic | File | |-------|------| | Null safety operators and patterns | `nullsafety.md` | | Coroutines, flows, structured concurrency | `coroutines.md` | | Collections, sequences, data classes | `collections.md` | | Scope functions, extensions, sealed classes | `idioms.md` | | Java interop and common Kotlin mistakes | `interop.md` | | Android lifecycle, Compose state | `android.md` | | Delegation, inline, reified, multiplatform | `advanced.md` | ## Critical Rules ### Null Safety - `!!` asserts non-null — crashes on null, use only when you've already checked - Platform types from Java are risky — add null checks or use `@Nullable`/`@NonNull` annotations - Elvis with `return`/`throw` for early exit — `val name = user?.name ?: return` ### Coroutines - `viewModelScope` auto-cancels on ViewModel clear — don't use `GlobalScope` in Android - Structured concurrency: child coroutine failure cancels parent — use `supervisorScope` to isolate - `StateFlow` needs initial value and never completes — `SharedFlow` for one-shot events - Inject dispatchers for testability — don't hardcode `Dispatchers.IO` ### Collections & Data Classes - `first()` throws on empty — use `firstOrNull()` for safe access - Only constructor properties in `equals`/`hashCode` — body properties ignored - `mutableStateListOf` for Compose — wrapping `mutableListOf` in state won't track changes ### Scope Functions & Extensions - Don't nest scope functions — readability drops fast, extract to named functions - Extensions are resolved statically — not polymorphic, receiver type matters at compile time ### Android/Compose - `repeatOnLifecycle(STARTED)` for flow collection — `launchWhenStarted` is deprecated - `remember` survives recomposition only — use `rememberSaveable` for config changes - `collectAsStateWithLifecycle` is the gold standard — lifecycle-aware + Compose state ### Java Interop - `==` is structural equality in Kotlin — `===` for reference, opposite of Java - SAM conversion only for Java interfaces — Kotlin interfaces need explicit `fun interface` - `@JvmStatic`, `@JvmOverloads`, `@JvmField` for Java-friendly APIs

path: advanced.md
content: # Advanced Kotlin

## Delegation

- `by lazy` is thread-safe by default (SYNCHRONIZED) — use `LazyThreadSafetyMode.NONE` if single-threaded
- `by Delegates.observable` for property change listeners — callback on every set
- `by Delegates.vetoable` can reject value changes — return false to keep old value
- `by map` for delegating to map entries — useful for config objects
- Custom delegates: implement `getValue`/`setValue` operators — reuse property patterns

## Inline Functions

- `inline` copies function body at call site — eliminates lambda allocation overhead
- `noinline` params not inlined, can be stored — needed if passing lambda to another function
- `crossinline` prevents non-local returns — use in lambdas passed to other contexts
- Don't inline large functions — increases bytecode size, hurts performance

## Reified Types

- `reified` only works in inline functions — type info preserved at runtime
- Use for `is` checks without passing `Class<T>` — `inline fun <reified T> isType(x: Any) = x is T`
- Can't use reified on non-inline — type erasure applies
- Common use: `Gson().fromJson<MyType>(json)` without class parameter

## Kotlin Multiplatform

- `expect`/`actual` for platform-specific code — expect in common, actual per platform
- Common code can't use platform APIs directly — abstract behind expect declarations
- `actual typealias` for simple platform mappings — when platform class matches expect
- Gradle source sets: commonMain, androidMain, iosMain — dependencies flow down
- Expect classes can have default implementations — actual only overrides what differs

## Contracts

- `contract { returns() implies (value != null) }` — smart cast after function call
- Only experimental in stdlib — but safe to use established ones
- `callsInPlace(EXACTLY_ONCE)` — enables initialization in lambdas for `run`, `let`, etc.


path: android.md
content: # Android-Specific Kotlin

## Lifecycle-Aware Coroutines

- `repeatOnLifecycle(STARTED)` restarts collection when lifecycle resumes — stops on STOPPED
- Don't collect flows in `onCreate` without lifecycle awareness — leaks if Activity destroyed
- `flowWithLifecycle` for single flow observation — extension on Flow, less boilerplate
- `Lifecycle.launchWhenStarted` is deprecated — use `repeatOnLifecycle` instead
- `lifecycleScope` tied to Activity/Fragment — auto-cancels on destroy

## Compose State

- `remember` survives recomposition, not configuration change — use `rememberSaveable` for rotation
- `rememberSaveable` serializes state — only works with `Saver` or Parcelable types
- `derivedStateOf` for computed state — recalculates only when dependencies change
- Snapshot system tracks reads during composition — triggers recomposition on change
- `mutableStateListOf` not `mutableListOf` wrapped in `mutableStateOf` — won't track internal changes

## Compose Performance

- `key()` helps identify items in lists — prevents unnecessary recomposition
- Lambda stability matters — use `remember` for lambdas with captures
- `@Stable` and `@Immutable` annotations help compiler skip recomposition
- Avoid reading state in composition scope if only needed in callbacks — defer reads

## ViewModel Integration

- `collectAsStateWithLifecycle` is the gold standard — lifecycle-aware + Compose state
- Expose `StateFlow` from ViewModel, not `LiveData` — better for Compose
- `savedStateHandle` for process death survival — inject into ViewModel
- One-shot events: use `Channel` or `SharedFlow(replay=0)` — not StateFlow


path: collections.md
content: # Collections & Data Classes

## Immutability

- `listOf` is immutable — use `mutableListOf` if you need to modify
- `map`, `filter`, `reduce` are lazy on Sequences — use `.asSequence()` for large chains

## Safe Access

- `first()` throws on empty — use `firstOrNull()` for safe access
- `associate` and `groupBy` replace manual map building — cleaner than forEach with mutableMap
- Destructuring: `for ((key, value) in map)` — also works with data classes

## Data Classes

- `data class` auto-generates `equals`, `hashCode`, `copy`, `toString` — don't write manually
- Only constructor properties in `equals`/`hashCode` — body properties ignored
- `copy()` for immutable updates — `user.copy(name = "New")` keeps other fields
- Prefer data classes for DTOs and state — but not for entities with identity beyond data


path: coroutines.md
content: # Coroutines & Flows

## Basics

- `suspend` functions only callable from coroutines — don't block, use `withContext(Dispatchers.IO)` for IO
- `launch` for fire-and-forget — `async/await` when you need the result
- `viewModelScope` auto-cancels on ViewModel clear — don't use `GlobalScope` in Android

## Structured Concurrency

- Child coroutine failure cancels parent — use `supervisorScope` to isolate failures
- Always use scoped coroutines — GlobalScope leaks and never cancels
- `coroutineScope` suspends until all children complete — use for parallel decomposition

## Flows

- `flow` for reactive streams — collect in lifecycle-aware scope with `repeatOnLifecycle`
- Use `stateIn` or `shareIn` to share flows — avoid multiple upstream collections
- `flowOn` changes upstream context only — downstream stays on collector's dispatcher

## StateFlow & SharedFlow

- `StateFlow` requires initial value, never completes — use for UI state
- `SharedFlow` for events, no initial value — replay 0 means late collectors miss events
- Don't use `SharedFlow(replay=1)` as poor man's StateFlow — use StateFlow for state
- `MutableStateFlow.update {}` is atomic — use instead of `.value = .value + 1`

## Flow Operators

- `conflate` drops intermediate values under backpressure — UI only needs latest
- `buffer` decouples producer/consumer speed — prevents slow collector blocking emitter
- `distinctUntilChanged` skips duplicates — useful for StateFlow-like behavior
- `catch` only catches upstream exceptions — doesn't affect downstream

## Testing Coroutines

- `runTest` automatically skips delays — virtual time, tests run fast
- `StandardTestDispatcher` for manual time control — use `advanceTimeBy()`
- `UnconfinedTestDispatcher` runs coroutines eagerly — no need to advance
- `advanceUntilIdle()` processes all pending coroutines — call before assertions
- Inject dispatchers, don't hardcode `Dispatchers.IO` — test with `TestDispatcher`


path: idioms.md
content: # Kotlin Idioms

## Scope Functions

- `let` for null checks and transformations — `value?.let { use(it) }`
- `apply` for object configuration — `MyObject().apply { prop = value }` returns object
- `run` for scoped computation — `val result = obj.run { compute() }` returns result
- `also` for side effects — `value.also { log(it) }` returns original
- Don't nest scope functions — readability drops fast, extract to named functions

## Extension Functions

- Extend existing classes without inheritance — `fun String.isEmail(): Boolean`
- Keep extensions close to usage — don't scatter across codebase
- Extension on nullable: `fun String?.orEmpty()` — can be called on null
- Extensions are resolved statically — not polymorphic, receiver type matters at compile time

## Sealed Classes

- Exhaustive `when` — compiler ensures all subclasses handled
- Perfect for state machines and results — `sealed class Result<T> { Success, Error }`
- Subclasses must be in same file (or same package in Kotlin 1.5+) — intentional restriction
- `sealed interface` for multiple inheritance — when you need to implement other interfaces


path: interop.md
content: # Java Interop & Common Mistakes

## Common Mistakes

- `==` is structural equality in Kotlin — `===` for reference, opposite of Java
- String templates: `"$var"` or `"${expr}"` — no concatenation needed
- `lateinit` can't be primitive — use `by lazy` for computed initialization
- `object` is singleton — `companion object` for static-like members, not instance
- SAM conversion only for Java interfaces — Kotlin interfaces need explicit `fun interface`

## Java Interop Annotations

- `@JvmStatic` for companion methods callable as static — without it, need `Companion.method()`
- `@JvmOverloads` generates overloads for default params — Java doesn't see defaults otherwise
- `@JvmField` exposes property as field — without getter/setter for Java callers
- Nullability annotations propagate — annotate Java code for Kotlin safety


path: nullsafety.md
content: # Null Safety

## Operators

- `?.` safe call chains — `user?.address?.city` returns null if any is null
- `?:` Elvis for defaults — `name ?: "Unknown"` is cleaner than if-else
- `!!` asserts non-null — crashes on null, use only when you've already checked

## Patterns

- `let` for null-scoped operations — `user?.let { doSomething(it) }` only runs if non-null
- Combine with Elvis — `user?.name ?: return` for early exit

## Java Interop

- Platform types from Java are risky — add null checks or use `@Nullable`/`@NonNull` annotations
- Treat Java types as nullable by default — safer than assuming non-null


Security scan verdicts — ClawScan: clean | static analysis: clean | VirusTotal: clean | SkillSpector: clean (LOW)