    {-# LANGUAGE PackageImports #-}
    {-# LANGUAGE TypeInType #-}
    {-# LANGUAGE PartialTypeSignatures #-}
    {-# LANGUAGE AllowAmbiguousTypes    #-}
    {-# LANGUAGE ConstraintKinds        #-}
    {-# LANGUAGE DataKinds              #-}
    {-# LANGUAGE FlexibleContexts       #-}
    {-# LANGUAGE FlexibleInstances      #-}
    {-# LANGUAGE FunctionalDependencies #-}
    {-# LANGUAGE KindSignatures         #-}
    {-# LANGUAGE MultiParamTypeClasses  #-}
    {-# LANGUAGE PolyKinds              #-}
    {-# LANGUAGE ScopedTypeVariables    #-}
    {-# LANGUAGE TypeApplications       #-}
    {-# LANGUAGE TypeFamilies           #-}
    {-# LANGUAGE TypeOperators          #-}
    {-# LANGUAGE UndecidableInstances   #-}

    -----------------------------------------------------------------------------
    -- |
    -- Module      :  Data.Generics.Product.Positions
    -- Copyright   :  (C) 2020 Csongor Kiss
    -- License     :  BSD3
    -- Maintainer  :  Csongor Kiss <kiss.csongor.kiss@gmail.com>
    -- Stability   :  experimental
    -- Portability :  non-portable
    --
    -- Derive positional product type getters and setters generically.
    --
    -----------------------------------------------------------------------------

    module Data.Generics.Product.Positions
      ( -- *Lenses

        -- $setup
        HasPosition (..)
      , HasPosition' (..)
      , HasPosition_ (..)
      , HasPosition0 (..)

      , getPosition
      , setPosition
      ) where

    import "this" Data.Generics.Internal.VL.Lens as VL

    import "generic-lens-core" Data.Generics.Internal.Void
    import qualified "generic-lens-core" Data.Generics.Product.Internal.Positions as Core

    import GHC.TypeLits   (Nat)

    -- $setup
    -- == /Running example:/
    --
    -- >>> :set -XTypeApplications
    -- >>> :set -XDataKinds
    -- >>> :set -XDeriveGeneric
    -- >>> :set -XGADTs
    -- >>> :set -XFlexibleContexts
    -- >>> import GHC.Generics
    -- >>> :m +Data.Generics.Internal.VL.Lens
    -- >>> :m +Data.Function
    -- >>> :{
    -- data Human = Human
    --   { name    :: String
    --   , age     :: Int
    --   , address :: String
    --   }
    --   deriving (Generic, Show)
    -- human :: Human
    -- human = Human "Tunyasz" 50 "London"
    -- :}

    -- |Records that have a field at a given position.
    class HasPosition (i :: Nat) s t a b | s i -> a, t i -> b, s i b -> t, t i a -> s where
      -- |A lens that focuses on a field at a given position. Compatible with the
      --  lens package's 'Control.Lens.Lens' type.
      --
      --  >>> human ^. position @1
      --  "Tunyasz"
      --  >>> human & position @3 .~ "Berlin"
      --  Human {name = "Tunyasz", age = 50, address = "Berlin"}
      --
      --  === /Type errors/
      --
      --  >>> human & position @4 .~ "Berlin"
      --  ...
      --  ... The type Human does not contain a field at position 4
      --  ...
      position :: VL.Lens s t a b

    class HasPosition_ (i :: Nat) s t a b where
      position_ :: VL.Lens s t a b

    -- |Records that have a field at a given position.
    --
    -- The difference between 'HasPosition' and 'HasPosition_' is similar to the
    -- one between 'Data.Generics.Product.Fields.HasField' and
    -- 'Data.Generics.Product.Fields.HasField_'.
    -- See 'Data.Generics.Product.Fields.HasField_'.
    class HasPosition' (i :: Nat) s a | s i -> a where
      position' :: VL.Lens s s a a

    -- |Records that have a field at a given position.
    --
    -- This class gives the minimal constraints needed to define this lens.
    -- For common uses, see 'HasPosition'.
    class HasPosition0 (i :: Nat) s t a b where
      position0 :: VL.Lens s t a b

    -- |
    -- >>> getPosition @2 human
    -- 50
    getPosition :: forall i s a. HasPosition' i s a => s -> a
    getPosition :: forall (i :: Nat) s a. HasPosition' i s a => s -> a
    getPosition s
    s = s
    s s -> ((a -> Const a a) -> s -> Const a s) -> a
    forall s a. s -> ((a -> Const a a) -> s -> Const a s) -> a
    ^. forall (i :: Nat) s a. HasPosition' i s a => Lens s s a a
    position' @i

    -- |
    -- >>> setPosition @2 60 human
    -- Human {name = "Tunyasz", age = 60, address = "London"}
    setPosition :: forall i s a. HasPosition' i s a => a -> s -> s
    setPosition :: forall (i :: Nat) s a. HasPosition' i s a => a -> s -> s
    setPosition = Lens s s a a -> a -> s -> s
    forall s t a b. Lens s t a b -> b -> s -> t
    VL.set (forall (i :: Nat) s a. HasPosition' i s a => Lens s s a a
    position' @i)

    instance Core.Context' i s a => HasPosition' i s a where
      position' :: Lens s s a a
    position' a -> f a
    f s
    s = (ALens a a (ZonkAny 1) a a -> ALens a a (ZonkAny 1) s s)
    -> Lens s s a a
    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 (i :: Nat) s a. Context' i s a => Lens s s a a
    Core.derived' @i) a -> f a
    f s
    s
      {-# INLINE position' #-}

    instance (Core.Context i s t a b , HasPosition0 i s t a b) => HasPosition i s t a b where
      position :: Lens s t a b
    position = forall (i :: Nat) s t a b. HasPosition0 i s t a b => Lens s t a b
    position0 @i
      {-# INLINE position #-}

    -- | Uncluttering type signatures (see 'Void')
    --
    -- >>> :t +d position
    -- position
    --   :: (HasPosition i s t a b, Functor f) => (a -> f b) -> s -> f t
    instance {-# OVERLAPPING #-} HasPosition f (Void1 a) (Void1 b) a b where
      position :: Lens (Void1 a) (Void1 b) a b
    position = (a -> f b) -> Void1 a -> f (Void1 b)
    forall a. HasCallStack => a
    undefined

    instance (Core.Context_ i s t a b, HasPosition0 i s t a b) => HasPosition_ i s t a b where
      position_ :: Lens s t a b
    position_ = forall (i :: Nat) s t a b. HasPosition0 i s t a b => Lens s t a b
    position0 @i
      {-# INLINE position_ #-}

    instance {-# OVERLAPPING #-} HasPosition_ f (Void1 a) (Void1 b) a b where
      position_ :: Lens (Void1 a) (Void1 b) a b
    position_ = (a -> f b) -> Void1 a -> f (Void1 b)
    forall a. HasCallStack => a
    undefined

    instance Core.Context0 i s t a b => HasPosition0 i s t a b where
      position0 :: Lens s t a b
    position0 a -> f b
    f s
    s = (ALens a b (ZonkAny 0) a b -> ALens a b (ZonkAny 0) s t)
    -> Lens s t a b
    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 (i :: Nat) s t a b. Context0 i s t a b => Lens s t a b
    Core.derived0 @i) a -> f b
    f s
    s
      {-# INLINE position0 #-}