| Copyright | (C) 2016-2026 David M. Johnson |
|---|---|
| License | BSD3-style (see the file LICENSE) |
| Maintainer | David M. Johnson <code@dmj.io> |
| Stability | experimental |
| Portability | non-portable |
| Safe Haskell | None |
| Language | Haskell2010 |
Miso.Lens.Generic
Contents
Description
Overview
Miso.Lens.Generic derives Lens values for record fields
at compile time using GHC.Generics and GHC.Records, without
Template Haskell. Fields are addressed by name via OverloadedLabels
or the explicit field combinator.
Enable the required extensions:
{-# LANGUAGE OverloadedLabels, DeriveGeneric #-}
import GHC.Generics (Generic)
import Miso.Lens.Generic (HasLens, field)
import Miso.Lens (Lens, view, set, (.=), (++=))
Quick start
data Counter = Counter { _count :: Int, _label :: MisoString }
deriving (Generic)
-- Label syntax (requires OverloadedLabels):
countLens :: Lens Counter Int
countLens = #_count
-- Explicit syntax (works without OverloadedLabels):
labelLens :: Lens Counter MisoString
labelLens = field @"_label"
update :: Action -> Effect p props Counter Action
update Increment = #_count += 1
update (SetLabel l) = #_label .= l
How it works
The HasLens instance is resolved via HasField for the
getter and a generic traversal (GSet) for the setter. A type-level
TotalityCheck produces a descriptive compile error if the field name
is absent from (or inconsistent across) the constructors.
Comparison with Template Haskell
- Overloaded labels
- Miso.Lens.Generic — no TH; derives via
Generic - Template Haskell
- Miso.Lens.TH —
makeLenses/makeClassy
See also
- Miso.Lens —
Lens,lens, operators - Miso.Lens.TH — Template Haskell alternative
Synopsis
- class Generic s => HasLens (name :: Symbol) s a | name s -> a where
- field :: forall (name :: Symbol) s a. HasLens name s a => Lens s a
- class GSet (name :: Symbol) typ (f :: Type -> Type) where
- gSet :: typ -> f x -> f x
- type family GetFieldType (field :: Symbol) (f :: Type -> Type) :: Maybe Type where ...
- type family TotalityCheck (name :: Symbol) r a (res :: Maybe Type) where ...
- type family And (l :: Maybe Type) (r :: Maybe Type) :: Maybe Type where ...
- type family Or (l :: Maybe Type) (r :: Maybe Type) :: Maybe Type where ...
Documentation
class Generic s => HasLens (name :: Symbol) s a | name s -> a where Source #
Provides a Lens onto the field named name of the record s.
The single instance is derived from s's Generic
representation, so any record with a Generic instance gets
lenses for free. Also backs the OverloadedLabels syntax #fieldName.
Since: 1.11.0.0
Instances
| (HasField name s a, TotalityCheck name s a (GetFieldType name (Rep s)), GSet name a (Rep s), Generic s) => HasLens name s a Source # | |
Defined in Miso.Lens.Generic | |
class GSet (name :: Symbol) typ (f :: Type -> Type) where Source #
Internal: writes the field named name into a Generic
representation. Drives the setter half of HasLens; you should not need to
write instances.
Since: 1.13.0.0
Instances
| (GSet name typ a, GSet name typ b) => GSet name typ (a :*: b) Source # | |
| (GSet name typ a, GSet name typ b) => GSet name typ (a :+: b) Source # | |
| GSet name typ f => GSet name typ (C1 x f) Source # | |
| GSet name typ f => GSet name typ (D1 x f) Source # | |
| GSet name typ (S1 ('MetaSel ('Just anotherName) b c d) x) Source # | |
| GSet name typ (S1 ('MetaSel ('Just name) b c d) (Rec0 typ)) Source # | |
type family GetFieldType (field :: Symbol) (f :: Type -> Type) :: Maybe Type where ... Source #
Internal: looks up the type of the field named field in a
Generic representation, yielding when found.
Products search both sides (Just tOr); sums require agreement (And).
Since: 1.13.0.0
Equations
| GetFieldType field (S1 ('MetaSel ('Just field) _1 _2 _3) (Rec0 t)) = 'Just t | |
| GetFieldType field (l :*: r) = Or (GetFieldType field l) (GetFieldType field r) | |
| GetFieldType field (l :+: r) = And (GetFieldType field l) (GetFieldType field r) | |
| GetFieldType field (C1 _1 f) = GetFieldType field f | |
| GetFieldType field (D1 _1 f) = GetFieldType field f | |
| GetFieldType field x = 'Nothing :: Maybe Type |
type family TotalityCheck (name :: Symbol) r a (res :: Maybe Type) where ... Source #
Internal: turns a missing field into a readable TypeError rather than an
unsolved-constraint message. Fails when GetFieldType found no field of
that name, or found one that is not present in every constructor.
Since: 1.13.0.0
Equations
| TotalityCheck _1 _2 _3 ('Just _4) = () | |
| TotalityCheck name r a ('Nothing :: Maybe Type) = TypeError ((('ShowType r ':<>: 'Text ": ") ':<>: 'Text name) ':<>: 'Text " field missing or not in all constructors") :: Constraint |
type family And (l :: Maybe Type) (r :: Maybe Type) :: Maybe Type where ... Source #
Internal: combines two GetFieldType results across a sum, yielding
only when both branches agree on Just tt — a field that is absent
from some constructor is not addressable.
Since: 1.13.0.0
type family Or (l :: Maybe Type) (r :: Maybe Type) :: Maybe Type where ... Source #
Internal: combines two GetFieldType results across a product, taking
whichever branch found the field.
Since: 1.13.0.0