packages feed

lens-sop-0.3.0: src/Generics/SOP/Lens.hs

-- | Generalized lenses
--
-- Intended to be imported qualified
--
-- > import Generics.SOP.Lens as GLens
--
module Generics.SOP.Lens (
    -- * Generalized lenses
    GLens
  , lens
  , get
  , modify
  , set
    -- * Conversion
  , fromLens
  , fromIso
  , toLens
    -- * Generic computation of lenses for record type
  , glenses
    -- * Labels for the representation types
  , np
  , rep
  , sop
  , head
  , tail
  , i
  ) where

import Prelude hiding (id, (.), head, tail)

import Control.Category
import Control.Monad
import Data.Functor.Identity
import Data.Kind
import Generics.SOP
import Optics.Core (Optic', Is, A_Getter, A_Setter)

import qualified Optics.Core as Optics

{-------------------------------------------------------------------------------
  Generalized lens using two categories
-------------------------------------------------------------------------------}

-- | GLens generalizes a monomorphic lens by allowing for different monads
-- for the getter and modifier
data GLens (r :: Type -> Type) (w :: Type -> Type) a b =
    GLens (a -> r b) ((b -> w b) -> (a -> w a))

instance Monad r => Category (GLens r w) where
  id = GLens pure id
  (GLens f m) . (GLens g n) = GLens (f <=< g) (n . m)

lens :: (a -> r b) -> ((b -> w b) -> a -> w a) -> GLens r w a b
lens = GLens

get :: GLens r w a b -> a -> r b
get (GLens f _) = f

modify :: GLens r w a b -> (b -> w b) -> a -> w a
modify (GLens _ g) = g

set :: Monad w => GLens r w a b -> b -> a -> w a
set l = modify l . const . pure

{-------------------------------------------------------------------------------
  Conversion
-------------------------------------------------------------------------------}

fromOptics ::
     (Is k A_Getter, Is k A_Setter, Monad r, Monad w)
  => Optic' k is a b -> GLens r w a b
fromOptics l =
    GLens
      (return . Optics.view l)
      (\f a -> (\b -> Optics.set l b a) <$> f (Optics.view l a))

fromLens :: (Monad r, Monad w) => Optics.Lens' a b -> GLens r w a b
fromLens = fromOptics

fromIso :: (Monad r, Monad w) => Optics.Iso' a b -> GLens r w a b
fromIso = fromOptics

toLens :: GLens Identity Identity a b -> Optics.Lens' a b
toLens l = Optics.lens (runIdentity . get l) (\a b -> runIdentity $ set l b a)

{-------------------------------------------------------------------------------
  Generic computation of all lenses for a record type
-------------------------------------------------------------------------------}

glenses :: forall r w a xs.
     (Generic a, Code a ~ '[xs], Monad r, Monad w)
  => NP (GLens r w a) xs
glenses =
    case sList :: SList (Code a) of
      SCons -> hliftA (\l -> l . sop . rep) np

{-------------------------------------------------------------------------------
  Generalized lenses for representation types
-------------------------------------------------------------------------------}

np :: forall r w xs.
     (Monad r, Monad w, SListI xs)
  => NP (GLens r w (NP I xs)) xs
np = case sList :: SList xs of
      SNil  -> Nil
      SCons -> i . head :* hliftA (. tail) np

rep :: (Monad r, Monad w, Generic a) => GLens r w a (Rep a)
rep = fromIso $ Optics.iso from to

sop :: (Monad r, Monad w) => GLens r w (SOP f '[xs]) (NP f xs)
sop = fromIso $ Optics.iso (unZ . unSOP) (SOP . Z)

head :: (Monad r, Monad w) => GLens r w (NP f (x ': xs)) (f x)
head = fromLens $ Optics.lens (\(x :* _) -> x) (\(_ :* xs) x -> x :* xs)

tail :: (Monad r, Monad w) => GLens r w (NP f (x ': xs)) (NP f xs)
tail = fromLens $ Optics.lens (\(_ :* xs) -> xs) (\(x :* _) xs -> (x :* xs))

i :: (Monad r, Monad w) => GLens r w (I a) a
i = fromIso $ Optics.iso unI I