| Copyright | (c) 2026 The Polytropos Authors |
|---|---|
| License | EUPL-1.2 OR Apache-2.0 |
| Maintainer | nadia.yvette.chambers@ik.me |
| Stability | experimental |
| Portability | non-portable (GHC extensions) |
| Safe Haskell | None |
| Language | GHC2021 |
Polytropos.Microlens
Description
Class-based optics integration for developers who use microlens
specifically to avoid transitive dependency bloat: this package depends
strictly on base, polytropos-core, and microlens.
Architectural Overview & Rationale
Unlike full lens, which brings in a large dependency graph, microlens
provides the core lens combinators in a lightweight package.
In polytropos-microlens:
* Name-based field access goes through fieldLens, whose FieldIndex
constraint is solved to a literal slot index by polytropos-plugin.
* The each instance traverses every field of a homogeneous record (where
all fields have the same type, witnessed by AllFieldsAre) in a single
pass over the flat SmallArray.
When to Use This Module
- When your project prioritizes fast build times and minimal transitive
dependencies, and requires basic lens access to
polytroposrecords.
When Not to Use This Module
- When you need prisms over sum variants or advanced indexed optics; use Polytropos.Optics or Polytropos.Lens instead.
Worked Example
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE TypeApplications #-}
{-# OPTIONS_GHC -fplugin=Polytropos.Plugin #-}
import Lens.Micro ((^.), (%~), (&))
import Polytropos
import Polytropos.Microlens
-- A genuinely heterogeneous record:
type User = Record '["name" ':= String, "score" ':= Int]
user :: User
user = REC { name = "Alice", score = 95 }
-- Accessing a field via heterogeneous fieldLens:
userName :: String
userName = user ^. fieldLens (Field @"name") -- "Alice"
-- Modifying a field:
user' :: User
user' = user & fieldLens (Field @"score") %~ (+ 5) -- score = 100Synopsis
- fieldLens :: forall (n :: Symbol) (r :: Row) a. FieldIndex n r a => Field n -> Lens' (Record r) a
- type family AllFieldsAre a (r :: Row) where ...
Field Lenses
fieldLens :: forall (n :: Symbol) (r :: Row) a. FieldIndex n r a => Field n -> Lens' (Record r) a Source #
Lens on a record field by name.
fieldLens#foo ::FieldIndex"foo" r a =>Lens'(Recordr) a
The FieldIndex dictionary carries the slot index as a literal, computed
by the TcPlugin; there is no runtime name lookup and no type-level search.
Homogeneity Evidence
type family AllFieldsAre a (r :: Row) where ... Source #
Evidence that every field of the row r has type a.
Closed type family, so a heterogeneous row fails to reduce and the
dependent instances simply do not apply. This is what lets each be a
genuine traversal with a single element type instead of an erased
forall-traversal.
Equations
| AllFieldsAre _1 ('[] :: [Pair Symbol Type]) = () | |
| AllFieldsAre a ((n ':= a) ': rest) = AllFieldsAre a rest |
Orphan instances
| AllFieldsAre a r => Each (Record ((n ':= a) ': r)) (Record ((n ':= a) ': r)) a a Source # | Homogeneous traversal across all record fields.
The instance head fixes the element type to the row's field type, which is
what the An empty record has no element type to traverse; |