Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
44 changes: 44 additions & 0 deletions .agents/skills/cats-effect-io/SKILL.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
---
name: cats-effect-io
description: Scala functional programming with Cats Effect IO and typeclasses. Use for wrapping side effects, modeling purity, choosing Sync/Async/Temporal/Concurrent, handling blocking I/O, and composing resources, fibers, and concurrency safely.
---

# Cats Effect IO (Scala)

## Quick start
- Treat every side effect as an effect value: return `IO[A]`, `SyncIO[A]`, or `F[A]` with `F[_]: Sync`/`Async`/`Temporal` as needed.
- Wrap Java blocking calls with `IO.blocking` or `IO.interruptible` (or `Sync[F].blocking`/`interruptible`).
- Use `Resource` to acquire/release resources and `IOApp` for program entry points.
- Prefer structured concurrency (`parTraverse`, `parMapN`, `background`, `Supervisor`) over manual fiber management.
- Read `references/cats-effect-io.md` for concepts, recipes, and FAQ guidance.
- For deeper `Resource` guidance, use the `cats-effect-resource` skill (install: `npx skills add https://github.com/alexandru/skills --skill cats-effect-resource`).

## Workflow
1. Classify side effects and choose the effect type: `IO` directly or polymorphic `F[_]` with the smallest required Cats Effect typeclass (`Sync`, `Async`, `Temporal`, `Concurrent`).
2. Wrap side-effectful code using `IO(...)`, `IO.blocking`, `IO.interruptible`, or `IO.async` (or their `Sync`/`Async` equivalents).
3. Manage resources with `Resource` or `bracket` and keep acquisition/release inside effects.
4. Compose effects with `flatMap`/for-comprehensions and collection combinators (`traverse`, `parTraverse`).
5. Use concurrency primitives (`Ref`, `Deferred`, `Queue`, `Semaphore`, `Supervisor`) and structured concurrency to avoid fiber leaks.

## Side-effect rules (apply to `IO`, `SyncIO`, and to `F[_]: Sync/Async`)
- All side-effectful functions must return results wrapped in `IO` (or `F[_]` with Cats Effect typeclasses).
- Side-effects include all non-determinism (call sites are not referentially transparent):
- Any I/O (files, sockets, console, databases).
- `Instant.now()`, `Random.nextInt()`.
- Any read from shared mutable state (the read itself is the side effect).
- Returning mutable data structures (for example, `Array[Int]`).

## Blocking I/O rules
- Java blocking methods must be wrapped in `IO.blocking` or `IO.interruptible` (or `Sync[F].blocking`/`interruptible`) so they run on the blocking pool.
- Prefer `IO.interruptible` for methods that may throw `InterruptedException` or `IOException`, but not for resource disposal.
- Use `IO.blocking` for cleanup/disposal (`Closeable#close`, `AutoCloseable#close`).

## Output expectations
- Make side effects explicit in signatures (`IO`/`SyncIO` or `F[_]: Sync/Async`); the guidance here applies equally to concrete `IO` and polymorphic `F[_]`.
- Use the smallest typeclass constraint that supports the needed operations.
- Keep effects as values; do not execute effects in constructors or top-level vals.

## References
- Load `references/cats-effect-io.md` for documentation summary and patterns.
- For concrete samples, read `references/cats-effect-io.md`.
- Use the `cats-effect-resource` skill for Resource-specific workflows and patterns (install: `npx skills add https://github.com/alexandru/skills --skill cats-effect-resource`).
96 changes: 96 additions & 0 deletions .agents/skills/cats-effect-io/references/cats-effect-io.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
# Cats Effect IO and Typeclasses (Scala)

Sources:
- https://typelevel.org/cats-effect/docs/tutorial
- https://typelevel.org/cats-effect/docs/concepts
- https://typelevel.org/cats-effect/docs/recipes
- https://typelevel.org/cats-effect/docs/faq

## Core ideas
- **Effects as values**: `IO[A]` (or `F[A]`) describes side effects; nothing runs until the effect is evaluated.
- **Fibers** are lightweight threads; use structured concurrency (`parMapN`, `parTraverse`) instead of manual `start`/`join`.
- **Cancelation** is cooperative and always runs finalizers; use `Resource` to ensure cleanup under success, error, or cancel.
- **Asynchronous vs synchronous**: `IO.async` uses callbacks; `IO.delay`/`IO.blocking`/`IO.interruptible` use synchronous execution.

## Blocking and interruptibility
- `IO.blocking` (or `Sync[F].blocking`) moves blocking JVM calls onto the blocking pool.
- `IO.interruptible` allows cancelation via thread interruption when the underlying API supports it.
- Many `java.io` reads ignore interruption; use explicit cancelation protocols when available.

## Resource safety
- Prefer `Resource` over manual `try/finally` for acquisition/release.
- Use `Resource.fromAutoCloseable` for simple `AutoCloseable` lifecycles; use `Resource.make` when you need custom release handling.

## Common recipes
- **Background work**: use `Supervisor` for start-and-forget fibers with safe cleanup.
- **Effectful loops**: use `traverse`/`traverse_` and `parTraverse` for sequencing or parallelism.
- **Shared state**: use `Ref`, `Deferred`, and other std primitives (avoid mutable state).

## API samples (IO and F[_])

Side effects as values (IO vs F[_]):
```scala
import cats.effect.{IO, Sync}

def nowIO: IO[Long] = IO(java.time.Instant.now().toEpochMilli)

def nowF[F[_]: Sync]: F[Long] =
Sync[F].delay(java.time.Instant.now().toEpochMilli)
```

Polymorphic side effects:
```scala
import cats.effect.Sync

def readEnv[F[_]: Sync](key: String): F[Option[String]] =
Sync[F].delay(sys.env.get(key))
```

Blocking vs interruptible:
```scala
import cats.effect.{IO, Sync}

import java.io.FileInputStream

def readByteIO(path: String): IO[Int] =
IO.blocking(new FileInputStream(path)).bracket { in =>
IO.interruptible(in.read())
} { in =>
IO.blocking(in.close())
}

val blockingCall: IO[Unit] = IO.blocking {
java.nio.file.Files.list(java.nio.file.Paths.get("/tmp")).close()
}

def interruptibleSleep[F[_]: Sync]: F[Unit] =
Sync[F].interruptible(Thread.sleep(250))
```

Resource usage:
```scala
import cats.effect.{IO, Resource}
import java.io.FileInputStream

def inputStream(path: String): Resource[IO, FileInputStream] =
Resource.fromAutoCloseable(IO.blocking(new FileInputStream(path)))

def readFirstByte(path: String): IO[Int] =
inputStream(path).use(in => IO.interruptible(in.read()))
```

Structured concurrency:
```scala
import cats.effect.{IO, IOApp}
import cats.syntax.all._

object ParallelExample extends IOApp.Simple {
val run: IO[Unit] =
(IO.println("A"), IO.println("B")).parTupled.void
}
```

## FAQ highlights
- If an `IO` is created but not composed, it does not run; compiler warnings can help catch this.
- `IO(...)` may run on a blocking thread in some optimized cases; this is normal.
- Starvation warnings often indicate accidental blocking without `IO.blocking`.
29 changes: 29 additions & 0 deletions .agents/skills/cats-effect-resource/SKILL.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
---
name: cats-effect-resource
description: Scala resource lifecycle management with Cats Effect `Resource` and `IO`. Use when defining safe acquisition/release, composing resources (including parallel acquisition), or designing resource-safe APIs and cancellation behavior for files, streams, pools, clients, and background fibers.
---

# Cats Effect Resource (Scala)

## Quick start
- Model each resource with `Resource.make` or `Resource.fromAutoCloseable` and keep release idempotent.
- Compose resources with `flatMap`, `mapN`, `parMapN`, or helper constructors; expose `Resource[F, A]` from APIs.
- Use `Resource` at lifecycle boundaries and call `.use` only at the program edges.
- Read `references/resource.md` for patterns, best practices, and API notes.

## Workflow
1. Identify acquisition, use, and release steps; decide if acquisition is blocking.
2. Implement a `Resource[F, A]` constructor using the smallest needed typeclass.
3. Compose resources into higher-level resources and keep finalizers minimal.
4. Decide how cancelation and errors should influence release logic.
5. Run with `.use` at the boundary (IOApp, service startup) and avoid leaking raw `A`.

## Usage guidance
- Prefer `Resource` over `try/finally` or `bracket` when composition and cancelation safety matter.
- Use `IO.blocking` (or `Sync[F].blocking`) for acquisition and release when calling blocking JVM APIs.
- For background fibers, use `Resource` or `Supervisor` to ensure cleanup on cancelation.

## References
- Load `references/resource.md` for API details, patterns, and examples.
- For Kotlin/Arrow parallels, see the `arrow-resource` skill.
- Install this skill with `npx skills add https://github.com/alexandru/skills --skill cats-effect-resource`.
134 changes: 134 additions & 0 deletions .agents/skills/cats-effect-resource/references/resource.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
# Cats Effect Resource (Scala) - Practical Guide

Sources:
- https://typelevel.org/cats-effect/docs/std/resource
- https://github.com/typelevel/cats-effect/blob/series/3.x/kernel/shared/src/main/scala/cats/effect/kernel/Resource.scala

## Table of Contents
- [Core model](#core-model)
- [Core APIs](#core-apis)
- [When to use Resource](#when-to-use-resource)
- [Patterns](#patterns)
- [Cancelation and error behavior](#cancelation-and-error-behavior)
- [Interop and blocking](#interop-and-blocking)
- [Checklist](#checklist)

## Core model
- `Resource[F, A]` encodes acquisition and release with a `use` phase.
- Release runs on success, error, or cancelation.
- Acquisition and finalizers are sequenced and run in a controlled scope; release is LIFO.

## Core APIs
- `Resource.make(acquire)(release)` for custom lifecycle.
- `Resource.fromAutoCloseable` for `AutoCloseable` lifecycles.
- `Resource.eval` to lift an effect into a resource.
- `.use` to run the resource and ensure release.
- `map`, `flatMap`, `mapN`, `parMapN`, `parZip` to compose resources.

## When to use Resource
- You need safe cleanup under cancelation.
- You need to compose resources and guarantee LIFO release.
- You want an API that makes lifecycle explicit and testable.

## Patterns

### 1) Resource constructors
Prefer functions that return `Resource[F, A]`:

```scala
import cats.effect.{Resource, Sync}

final class UserProcessor {
def start(): Unit = ()
def shutdown(): Unit = ()
}

def userProcessor[F[_]: Sync]: Resource[F, UserProcessor] =
Resource.make(Sync[F].delay { new UserProcessor().tap(_.start()) })(p =>
Sync[F].delay(p.shutdown())
)
```

### 2) Composing resources

```scala
import cats.effect.{Resource, Sync}
import cats.syntax.all._

final class DataSource { def connect(): Unit = (); def close(): Unit = () }
final class Service(ds: DataSource, up: UserProcessor)

def dataSource[F[_]: Sync]: Resource[F, DataSource] =
Resource.make(Sync[F].delay { new DataSource().tap(_.connect()) })(ds =>
Sync[F].delay(ds.close())
)

def service[F[_]: Sync]: Resource[F, Service] =
(dataSource[F], userProcessor[F]).mapN(new Service(_, _))
```

### 3) Parallel acquisition

```scala
import cats.effect.{Resource, Sync}
import cats.syntax.all._

def servicePar[F[_]: Sync]: Resource[F, Service] =
(dataSource[F], userProcessor[F]).parMapN(new Service(_, _))
```

### 4) File input stream

```scala
import cats.effect.{IO, Resource}

import java.io.FileInputStream

def inputStream(path: String): Resource[IO, FileInputStream] =
Resource.fromAutoCloseable(IO.blocking(new FileInputStream(path)))
```

### 5) Database pool + per-connection resource

```scala
import cats.effect.{Resource, Sync}

import javax.sql.DataSource

def pool[F[_]: Sync]: Resource[F, DataSource] = ???

def connection[F[_]: Sync](ds: DataSource): Resource[F, java.sql.Connection] =
Resource.make(Sync[F].blocking(ds.getConnection))(c =>
Sync[F].blocking(c.close())
)
```

### 6) Acquire in a loop
Use `Resource.make` per element and compose with `traverse`/`parTraverse`:

```scala
import cats.effect.{Resource, Sync}
import cats.syntax.all._

def acquireOne[F[_]: Sync](id: String): Resource[F, Handle] = ???

def acquireAll[F[_]: Sync](ids: List[String]): Resource[F, List[Handle]] =
ids.traverse(acquireOne[F])
```

## Cancelation and error behavior
- Finalizers run on success, error, or cancelation.
- If finalizers can fail, decide whether to log, suppress, or raise secondary errors.
- Keep finalizers idempotent and minimal to avoid cascading failures during release.

## Interop and blocking
- Wrap blocking acquisition or release in `blocking` to avoid compute starvation.
- Prefer `Resource.fromAutoCloseable` for Java interop; use `make` for custom release.
- If the API supports cooperative cancellation, combine it with `Resource` to ensure cleanup.

## Checklist
- Expose `Resource[F, A]` in public constructors.
- Keep release idempotent and tolerant of partial failures.
- Use `parMapN` only for independent resources.
- Avoid calling `.use` except at lifecycle boundaries.
- Use `IO.blocking`/`Sync[F].blocking` for blocking JVM APIs.
33 changes: 33 additions & 0 deletions .agents/skills/cats-mtl-typed-errors/SKILL.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
---
name: cats-mtl-typed-errors
description: Scala typed errors with Cats MTL Raise/Handle and allow/rescue. Use for designing custom domain error types without EitherT, while keeping Cats Effect and ecosystem composition. Covers Scala 2/3 syntax and IO-only or F[_] usage.
---

# Cats MTL Typed Errors (Scala)

## Quick start
- Define a domain error type; it may or may not extend Throwable depending on context.
- Use Cats MTL `Raise[F, E]` in functions that can raise errors.
- Use `Handle.allow`/`rescue` (Scala 3) or `Handle.allowF` (Scala 2) to introduce a scoped error capability and handle it like try/catch.
- Prefer Cats MTL over `IO[Either[E, A]]` and avoid `EitherT[IO, E, A]`; pure functions returning `Either[E, A]` are fine at API boundaries.
- `F[_]` is optional: you can write `IO`-specific code or keep `F[_]` for polymorphism, depending on the project.

## Workflow
1. Model domain errors as sealed ADTs (Scala 2) or enums (Scala 3)
2. For effectful code that can raise errors, require `Raise[F, E]` (and `Monad[F]` or `Applicative[F]`).
3. Raise errors with `.raise` and return successful values with `pure`.
4. At a boundary, use `Handle.allow` (Scala 3) or `Handle.allowF` (Scala 2) to create a scope where raises are valid.
5. Close the scope with `.rescue` to handle each error case explicitly.
6. Keep Cats Effect resource and concurrency semantics intact by staying in the monofunctor error channel.

## Patterns to apply
- **Typed errors in signatures**: treat the error type parameter `E` as the checked-exception channel in the function signature.
- **Scoped error capabilities**: require `Raise[F, E]` in functions that can fail; use `Handle[F, E]` when you also need to recover.
- **Scala 3 ergonomics**: prefer `using` and context functions with `allow`; type inference is significantly better.
- **Scala 2 compatibility**: use `allowF` and explicit implicit parameters; expect more braces and explicit types.
- **Interop with pure code**: use pure `Either[E, A]` for parsing/validation and lift into `F` where needed.
- **Avoid transformer stacks**: do not reach for `EitherT` just to get a typed error channel; Cats MTL provides the capability without the stack.
- **Avoid sealed-on-sealed inheritance**: model error hierarchies with composition (wrapper case classes), not sealed inheritance chains.

## References
- Load `references/custom-error-types.md` for detailed guidance, Scala 2/3 syntax, and rationale from the Typelevel article.
Loading