either-n-0.1.0.0: src/Data/Lens/Injection/Generic.hs
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE RankNTypes #-}
{-# OPTIONS_GHC -Wall #-}
{- | A prism to the @n@th constructor of any type with a 'Generic' instance.
This is the default implementation of each of the @InjectionN@ classes.
The class 'GInjection' is exported so that it can be named in constraints;
the machinery behind it is in "Data.Lens.Injection.Generic.Internal".
-}
module Data.Lens.Injection.Generic (
-- * Prism
injection,
-- * Generic machinery
GInjection,
) where
import Control.Lens (Prism, iso)
import Data.Lens.Injection.Generic.Internal (GInjection (..))
import GHC.Generics (Generic (..))
{- $setup
>>> :set -XDataKinds -XDeriveGeneric -XTypeApplications
>>> import Control.Lens((^?), (#), over)
>>> import Data.Proxy(Proxy(..))
>>> import GHC.Generics(Generic)
>>> data T a = T1 a | T2 | T3 Int Bool deriving (Show, Generic)
-}
{- | A prism to the @n@th constructor (counting from 0) of a type with a 'Generic' instance,
where @n@ is given by a proxy, such as @Proxy \@0@.
The focus of the prism depends on the fields of the constructor:
* No fields: the focus is @()@.
* One field: the focus is that field.
* More than one field: the focus is any type with a single constructor
that has the same fields, such as a tuple.
>>> T1 'x' ^? injection (Proxy @0)
Just 'x'
>>> T2 ^? injection (Proxy @0)
Nothing
Unlike the @InjectionN@ classes, 'injection' has no functional dependencies,
so a type-changing use may need a type annotation.
>>> over (injection (Proxy @0)) show (T1 1) :: T String
T1 "1"
>>> (T2 :: T Char) ^? injection (Proxy @1)
Just ()
>>> (T3 1 True :: T Char) ^? injection (Proxy @2) :: Maybe (Int, Bool)
Just (1,True)
>>> injection (Proxy @2) # (1, True) :: T Char
T3 1 True
-}
injection :: (Generic s, Generic t, GInjection n (Rep s) (Rep t) a b) => proxy n -> Prism s t a b
injection n =
iso from to . gInjection n
{-# INLINE injection #-}