{-# LANGUAGE PackageImports #-} {-# LANGUAGE UndecidableInstances #-} {-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE FlexibleInstances #-} {-# LANGUAGE MultiParamTypeClasses #-} {-# LANGUAGE ScopedTypeVariables #-} {-# LANGUAGE TypeApplications #-} ----------------------------------------------------------------------------- -- | -- Module : Data.Generics.Product.Typed -- Copyright : (C) 2020 Csongor Kiss -- License : BSD3 -- Maintainer : Csongor Kiss <kiss.csongor.kiss@gmail.com> -- Stability : experimental -- Portability : non-portable -- -- Derive lenses of a given type in a product. -- ----------------------------------------------------------------------------- module Data.Generics.Product.Typed ( -- *Lenses -- -- $setup HasType (..) ) where import "this" Data.Generics.Internal.VL.Lens as VL import qualified "generic-lens-core" Data.Generics.Product.Internal.Typed as Core import "generic-lens-core" Data.Generics.Internal.Void -- $setup -- == /Running example:/ -- -- >>> :set -XTypeApplications -- >>> :set -XDataKinds -- >>> :set -XDeriveGeneric -- >>> import GHC.Generics -- >>> :m +Data.Generics.Internal.VL.Lens -- >>> :{ -- data Human -- = Human -- { name :: String -- , age :: Int -- , address :: String -- , tall :: Bool -- } -- | HumanNoTall -- { name :: String -- , age :: Int -- , address :: String -- } -- deriving (Generic, Show) -- human :: Human -- human = Human "Tunyasz" 50 "London" False -- :} -- |Records that have a field with a unique type. class HasType a s where -- |A lens that focuses on a field with a unique type in its parent type. -- Compatible with the lens package's 'Control.Lens.Lens' type. -- -- >>> human ^. typed @Int -- 50 -- -- === /Type errors/ -- -- >>> human ^. typed @String -- ... -- ... -- ... The type Human contains multiple values of type [Char]. -- ... The choice of value is thus ambiguous. The offending constructors are: -- ... Human -- ... HumanNoTall -- ... -- -- >>> human ^. typed @Bool -- ... -- ... -- ... Not all constructors of the type Human contain a field of type Bool. -- ... The offending constructors are: -- ... HumanNoTall -- ... typed :: VL.Lens s s a a typed = forall s a b t. (s -> a) -> (s -> b -> t) -> Lens s t a b VL.lens (forall a s. HasType a s => s -> a getTyped @a) (forall a b c. (a -> b -> c) -> b -> a -> c flip (forall a s. HasType a s => a -> s -> s setTyped @a)) {-# INLINE typed #-} -- |Get field at type. getTyped :: s -> a getTyped s s = s s forall s a. s -> ((a -> Const a a) -> s -> Const a s) -> a ^. forall a s. HasType a s => Lens s s a a typed @a -- |Set field at type. setTyped :: a -> s -> s setTyped = forall s t a b. Lens s t a b -> b -> s -> t VL.set (forall a s. HasType a s => Lens s s a a typed @a) {-# MINIMAL typed | setTyped, getTyped #-} instance Core.Context a s => HasType a s where typed :: Lens s s a a typed = forall a b i s t. (ALens a b i a b -> ALens a b i s t) -> Lens s t a b VL.ravel forall a s. Context a s => Lens s s a a Core.derived {-# INLINE typed #-} instance {-# OVERLAPPING #-} HasType a a where getTyped :: a -> a getTyped = forall a. a -> a id {-# INLINE getTyped #-} setTyped :: a -> a -> a setTyped a a a _ = a a {-# INLINE setTyped #-} -- | Uncluttering type signatures (see 'Void') -- -- >>> :t +d typed -- typed :: (HasType a s, Functor f) => (a -> f a) -> s -> f s -- -- Note that this might not longer be needed given the 'HasType a a' instance. instance {-# OVERLAPPING #-} HasType a Void where typed :: Lens Void Void a a typed = forall a. HasCallStack => a undefined