{-# LANGUAGE EmptyCase #-}
{-# LANGUAGE LambdaCase #-}
{-# LANGUAGE MagicHash #-}
{-# LANGUAGE Rank2Types #-}
{-# LANGUAGE TypeOperators #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE PolyKinds #-}
-----------------------------------------------------------------------------
-- |
-- Module      :  GHC.Generics.Lens
-- Copyright   :  (C) 2012-16 Edward Kmett
-- License     :  BSD-style (see the file LICENSE)
-- Maintainer  :  Edward Kmett <ekmett@gmail.com>
-- Stability   :  experimental
-- Portability :  GHC
--
-- Note: @GHC.Generics@ exports a number of names that collide with @Control.Lens@.
--
-- You can use hiding or imports to mitigate this to an extent, and the following imports,
-- represent a fair compromise for user code:
--
-- > import Control.Lens hiding (Rep)
-- > import GHC.Generics hiding (from, to)
--
-- You can use 'generic' to replace 'GHC.Generics.from' and 'GHC.Generics.to' from @GHC.Generics@,
-- and probably won't be explicitly referencing 'Control.Lens.Representable.Rep' from @Control.Lens@
-- in code that uses generics.
--
-- This module provides compatibility with older GHC versions by using the
-- <http://hackage.haskell.org/package/generic-deriving generic-deriving>
-- package.
----------------------------------------------------------------------------
module GHC.Generics.Lens
  (
    generic
  , generic1
  , _V1
  , _U1
  , _Par1
  , _Rec1
  , _K1
  , _M1
  , _L1
  , _R1
  , _UAddr
  , _UChar
  , _UDouble
  , _UFloat
  , _UInt
  , _UWord
  ) where

import           Control.Lens
import           GHC.Exts (Char(..), Double(..), Float(..),
                           Int(..), Ptr(..), Word(..))
import qualified GHC.Generics as Generic
import           GHC.Generics hiding (from, to)

-- $setup
-- >>> :set -XNoOverloadedStrings
-- >>> import Control.Lens

-- | Convert from the data type to its representation (or back)
--
-- >>> "hello"^.generic.from generic :: String
-- "hello"
generic :: (Generic a, Generic b) => Iso a b (Rep a g) (Rep b h)
generic = iso Generic.from Generic.to
{-# INLINE generic #-}

-- | Convert from the data type to its representation (or back)
generic1 :: (Generic1 f, Generic1 g) => Iso (f a) (g b) (Rep1 f a) (Rep1 g b)
generic1 = iso from1 to1
{-# INLINE generic1 #-}

_V1 :: Over p f (V1 s) (V1 t) a b
_V1 _ = \case
{-# INLINE _V1 #-}

_U1 :: Iso (U1 p) (U1 q) () ()
_U1 = iso (const ()) (const U1)
{-# INLINE _U1 #-}

_Par1 :: Iso (Par1 p) (Par1 q) p q
_Par1 = coerced
{-# INLINE _Par1 #-}

_Rec1 :: Iso (Rec1 f p) (Rec1 g q) (f p) (g q)
_Rec1 = coerced
{-# INLINE _Rec1 #-}

_K1 :: Iso (K1 i c p) (K1 j d q) c d
_K1 = coerced
{-# INLINE _K1 #-}

_M1 :: Iso (M1 i c f p) (M1 j d g q) (f p) (g q)
_M1 = coerced
{-# INLINE _M1 #-}

_L1 :: Prism' ((f :+: g) a) (f a)
_L1 = prism remitter reviewer
  where
  remitter = L1
  reviewer (L1 l) = Right l
  reviewer x = Left x
{-# INLINE _L1 #-}

-- | You can access fields of `data (f :*: g) p` by using its `Field1` and
-- `Field2` instances.

_R1 :: Prism' ((f :+: g) a) (g a)
_R1 = prism remitter reviewer
  where
  remitter = R1
  reviewer (R1 l) = Right l
  reviewer x = Left x
{-# INLINE _R1 #-}

_UAddr :: Iso (UAddr p) (UAddr q) (Ptr c) (Ptr d)
_UAddr = iso remitter reviewer
  where
  remitter (UAddr a) = Ptr a
  reviewer (Ptr a) = UAddr a
{-# INLINE _UAddr #-}

_UChar :: Iso (UChar p) (UChar q) Char Char
_UChar = iso remitter reviewer
  where
  remitter (UChar c) = C# c
  reviewer (C# c) = UChar c
{-# INLINE _UChar #-}

_UDouble :: Iso (UDouble p) (UDouble q) Double Double
_UDouble = iso remitter reviewer
  where
  remitter (UDouble d) = D# d
  reviewer (D# d) = UDouble d
{-# INLINE _UDouble #-}

_UFloat :: Iso (UFloat p) (UFloat q) Float Float
_UFloat = iso remitter reviewer
  where
  remitter (UFloat f) = F# f
  reviewer (F# f) = UFloat f
{-# INLINE _UFloat #-}

_UInt :: Iso (UInt p) (UInt q) Int Int
_UInt = iso remitter reviewer
  where
  remitter (UInt i) = I# i
  reviewer (I# i) = UInt i
{-# INLINE _UInt #-}

_UWord :: Iso (UWord p) (UWord q) Word Word
_UWord = iso remitter reviewer
  where
  remitter (UWord w) = W# w
  reviewer (W# w) = UWord w
{-# INLINE _UWord #-}
