kubuszok/kindlings/hearth-collection-map
Claude Code and agent skill by kubuszok/kindlings
- community
Checking that the SKILL.md file is still in the repository.
Creating IsCollection and IsMap providers for external collection types (e.g. cats NonEmptyList, NonEmptyMap). Helper method pattern, runtime helper pattern, fromUntyped matching, SBT reload.
Install
npx vskill@latest install kubuszok/kindlings/hearth-collection-mapSKILL.md
Collection & Map Integration Skill
This document covers creating collection and map integrations — modules that teach Hearth's derivation system how to handle collection types from external libraries (e.g., cats NonEmptyList, NonEmptyMap) as collections or maps.
When to create a collection integration
Create a collection integration when:
- An external library defines collection-like types (e.g.,
NonEmptyList,Chain,NonEmptyMap) - These types should be treated as iterable collections (or key-value maps) by derivation macros
- Existing
IsCollectionProviderForScalaCollectiondoesn't match them (they don't extendIterable)
Do NOT create a collection integration for types that should be treated as:
- Value types (thin wrappers) — use a value type integration instead (see `hearth-value-types`)
- Case classes — these are derived structurally by default
Architecture
How IsCollection / IsMap works
StandardMacroExtensionimplementations registerIsCollection.Providerinstances- When a derivation rule encounters a type, it checks
IsCollection.unapply(tpe)(andIsMap.unapply(tpe)) - Providers are tried in registration order until one matches
- The matched
IsCollectionOf[A, E]provides:
asIterable: Expr[A] => Expr[Iterable[E]]— convert to iterable for encodingfactory: Expr[Factory[E, CtorResult]]— factory for building intermediate collectionbuild: CtorLikeOf[Builder[E, CtorResult], A]— construct final type from builder result
- For maps,
IsMapOf[A, (K, V)]extendsIsCollectionOfand adds:
key,value,pair— decompose/compose key-value pairsKey,Value— type members for key and value types
CtorLikeOf variants for collection construction
| Variant | Use case | |
|---|---|---|
PlainValue | Collection that always succeeds (e.g., Chain — can be empty) | |
EitherStringOrValue | Non-empty collections that reject empty input |
IsMap before IsCollection ordering
Map <: Iterable, so IsCollection matches maps too. Derivation rules always check IsMap before IsCollection. When implementing a map-like type (e.g., NonEmptyMap), register it via IsCollection.registerProvider but return an IsMapOf instance — the rule engine will detect it as a map.
Step-by-step: Creating a collection integration
1. Module structure
2. Build configuration
Same as value type integrations — create a projectMatrix in build.sbt with macroExtensionTraits := Seq("hearth.std.StandardMacroExtension"). See `hearth-value-types` § "Add build configuration".
Critical: After modifying build.sbt, run sbt --client "reload" — the running SBT server does not auto-detect build definition changes. Without reload, the SPI extension won't be on the classpath and type matching will silently fail.
3. Implement the provider
The provider class extends StandardMacroExtension and registers IsCollection.Provider instances in extend().
Key patterns
a) Helper method pattern — avoid path-dependent types in `Expr.quote`
Types from Type.Ctor1.unapply (like elem.Underlying) are path-dependent. Scala 2's reifier fails with "not found: value elem" when these appear inside Expr.quote.
Solution: Move Expr.quote code to helper methods where element types are regular type parameters:
b) Runtime helper pattern — avoid Newtype type aliases in `Expr.quote`
Some types (cats NonEmptyChain, NonEmptyMap, NonEmptySet) are Newtype aliases (*Impl.Type[A]). Scala 2's reifier fails with "not found: value data" when these types appear inside Expr.quote.
Solution: Create a runtime helper object that encapsulates the Newtype references. Quotes only call the helper methods:
The helper methods take/return Any where the Newtype aliases would appear, and the generated code casts to the correct types.
c) `Type.Ctor1.of` vs `fromUntyped`
Use plain Type.Ctor1.of[cats.data.NonEmptyList]: it is fine across compilation units / the extension SPI (re-measured on Hearth 0.4.3, see hearth-value-types skill), and since kubuszok/hearth#389 Scala 3 .of also decomposes SUBTYPES of the constructor correctly, so the former Type.Ctor1.fromUntyped(Type.Ctor1.of[F].asUntyped) wrapper is not needed.
d) Scala 3 tree invariant checker — keep correct types inside quotes
Scala 3's -Xcheck-macros verifies that the tree type matches the Expr[T] wrapper type. Using Any inside quotes and casting the outer Expr fails. Always use .asInstanceOf inside the Expr.quote to maintain correct tree types:
e) Summoning `Order` for sorted types
NonEmptyMap and NonEmptySet require cats.kernel.Order for construction. Summon it in the parse method and pass to the helper:
4. Implementing IsCollectionOf
Each collection provider returns an IsCollectionOf[A, E] implementation. The key members:
Using `List[E]` as `CtorResult`: The intermediate collection type doesn't have to match the final type. Using List avoids needing ClassTag or Ordering at the factory level. The conversion from List to the target type happens in build.
5. Implementing IsMapOf
For map-like types, extend IsMapOf instead of IsCollectionOf. The element type is (K, V):
6. Add tests to integration-tests
Create test case classes and spec files:
Non-empty collections should test that decoding rejects empty input.
7. Verify
Cross-compilation pitfalls summary
| Pitfall | Symptom | Fix | |
|---|---|---|---|
Path-dependent types in Expr.quote | Scala 2: "not found: value elem" | Helper method pattern (§3a) | |
Newtype aliases in Expr.quote | Scala 2: "not found: value data" | Runtime helper pattern (§3b) | |
| Fixed in kubuszok/hearth#389 | Plain .of (§3c) | ||
| Tree type mismatch | Scala 3: tree invariant checker failure | Cast inside quotes (§3d) | |
| SBT not reloaded after build.sbt change | SPI extension not loaded | sbt --client "reload" (§2) |
Reference implementation
- Cats integration:
cats-integration/src/main/scala/.../CatsCollectionAndMapProviders.scala— cross-compiled (Scala 2 + 3), coversNonEmptyList,NonEmptyVector,NonEmptyChain,Chain,NonEmptyMap,NonEmptySet - Runtime helpers:
cats-integration/src/main/scala/.../runtime/CatsConversions.scala— encapsulates Newtype type references - Hearth built-in:
hearth/hearth/src/main/scala/hearth/std/extensions/IsCollectionProviderForScalaCollection.scala— reference for standard library collections
Checklist for new collection integrations
- [ ] Module directory and
build.sbtconfiguration withmacroExtensionTraits - [ ]
StandardMacroExtensionclass withIsCollection.registerProvidercalls - [ ] Helper methods with regular type parameters (not path-dependent)
- [ ] Runtime helper object for Newtype aliases (if applicable)
- [ ]
@nowarn("msg=is never used")on methods with implicitTypefor cross-quotes - [ ]
EitherStringOrValuefor non-empty collections,PlainValuefor always-valid collections - [ ]
IsMapOf(notIsCollectionOf) for map-like types - [ ] Added to
rootaggregate andintegrationTests.dependsOn - [ ] Integration tests: encode, decode, decode-rejects-empty (for non-empty types)
- [ ] Tests pass:
sbt --client "test-jvm-2_13 ; test-jvm-3"
Other provider patterns: IsEither
This is the start of the file. Read the full SKILL.md on GitHub.
Security
- Security scanPassed, 100 of 100Pattern checks on the skill's files.
- Model reviewNot runA model review is scheduled for the next rescan.
Quality evaluation
- Task evaluationNot runNo evaluation yet. Run one in Skill Studio.
Source
- SKILL.md in the repositoryCheckingdocs/contributing/hearth-collection-map/SKILL.md
Popularity
- Repository stars
- 68
- Repository forks
- 7
- 7-day trend score
- 0
Stars and forks are for the whole GitHub repository, not this skill alone. Not refreshed yet.
Works with
Any agent vskill installs to: 56 targets in all. These 39 have a page on this site.
- Claude Code
- Cursor
- GitHub Copilot
- Windsurf
- Codex
- Gemini CLI
- Cline
- Amp
- Roo Code
- Goose
- Continue
- Kilo Code
- Junie
- Trae
- OpenHands
- Qwen Code
- AI Agent
- Tabnine Chat
- Sourcegraph Cody
- Amazon Q
- Firebase Genkit
- Sweep
- Devin
- Phind
- Mentat
- Claude Desktop
- Aider
- GPT Pilot
- Plandex
- bolt.new
- v0
- Replit Agent
- Anse
- TypingMind
- BetterChatGPT
- LibreChat
- Warp AI
- Pieces
- Fig AI
Embed badge
[](https://verified-skill.com/skills/kubuszok/kindlings/hearth-collection-map)