{-# LANGUAGE FlexibleInstances   #-}
{-# LANGUAGE RankNTypes          #-}
{-# LANGUAGE RoleAnnotations     #-}
{-# LANGUAGE TypeOperators       #-}

-- | Definition of 'Rep' and functions that do not depend on ".Generic"
--
-- Defined as a separate module to avoid circular module dependencies.
module Data.Record.Generic.Rep.Internal (
    Rep(..)
    -- * Basic functions
  , map'
  , sequenceA
    -- * Conversion
  , unsafeFromList
  , unsafeFromListAny
  , collapse
  , toListAny
    -- * Auxiliary
  , noInlineUnsafeCo
  ) where

import Prelude hiding (sequenceA)
import qualified Prelude

import Data.Coerce (coerce)
import Data.Foldable (toList)
import Data.Primitive.SmallArray
import Data.SOP.BasicFunctors
import GHC.Exts (Any)
import Unsafe.Coerce (unsafeCoerce)

{-------------------------------------------------------------------------------
  Representation
-------------------------------------------------------------------------------}

-- | Representation of some record @a@
--
-- The @f@ parameter describes which functor has been applied to all fields of
-- the record; in other words @Rep I@ is isomorphic to the record itself.
newtype Rep f a = Rep (SmallArray (f Any))

type role Rep representational nominal

{-------------------------------------------------------------------------------
  Basic functions
-------------------------------------------------------------------------------}

-- | Strict map
--
-- @map' f x@ is strict in @x@: if @x@ is undefined, @map f x@ will also be
-- undefined, even if @f@ never needs any values from @x@.
map' :: (forall x. f x -> g x) -> Rep f a -> Rep g a
map' :: forall (f :: * -> *) (g :: * -> *) a.
(forall x. f x -> g x) -> Rep f a -> Rep g a
map' forall x. f x -> g x
f (Rep SmallArray (f Any)
v) = forall (f :: * -> *) a. SmallArray (f Any) -> Rep f a
Rep forall a b. (a -> b) -> a -> b
$ forall x. f x -> g x
f forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> SmallArray (f Any)
v

sequenceA :: Applicative m => Rep (m :.: f) a -> m (Rep f a)
sequenceA :: forall (m :: * -> *) (f :: * -> *) a.
Applicative m =>
Rep (m :.: f) a -> m (Rep f a)
sequenceA (Rep SmallArray ((:.:) m f Any)
v) = forall (f :: * -> *) a. SmallArray (f Any) -> Rep f a
Rep forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> forall (t :: * -> *) (f :: * -> *) a.
(Traversable t, Applicative f) =>
t (f a) -> f (t a)
Prelude.sequenceA (forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap forall {l} {k} (f :: l -> *) (g :: k -> l) (p :: k).
(:.:) f g p -> f (g p)
unComp SmallArray ((:.:) m f Any)
v)

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

collapse :: Rep (K a) b -> [a]
collapse :: forall a b. Rep (K a) b -> [a]
collapse (Rep SmallArray (K a Any)
v) = coerce :: forall a b. Coercible a b => a -> b
coerce (forall (t :: * -> *) a. Foldable t => t a -> [a]
toList SmallArray (K a Any)
v)

-- | Convert 'Rep' to list
toListAny :: Rep f a -> [f Any]
toListAny :: forall (f :: * -> *) a. Rep f a -> [f Any]
toListAny (Rep SmallArray (f Any)
v) = forall (t :: * -> *) a. Foldable t => t a -> [a]
toList SmallArray (f Any)
v

-- | Convert list to 'Rep'
--
-- Does not check that the list has the right number of elements.
unsafeFromList :: [b] -> Rep (K b) a
unsafeFromList :: forall b a. [b] -> Rep (K b) a
unsafeFromList = forall (f :: * -> *) a. SmallArray (f Any) -> Rep f a
Rep forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall a. [a] -> SmallArray a
smallArrayFromList forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall a b. (a -> b) -> [a] -> [b]
Prelude.map forall k a (b :: k). a -> K a b
K

-- | Convert list to 'Rep'
--
-- Does not check that the list has the right number of elements, nor the
-- types of those elements.
unsafeFromListAny :: [f Any] -> Rep f a
unsafeFromListAny :: forall (f :: * -> *) a. [f Any] -> Rep f a
unsafeFromListAny = forall (f :: * -> *) a. SmallArray (f Any) -> Rep f a
Rep forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall a. [a] -> SmallArray a
smallArrayFromList

{-------------------------------------------------------------------------------
  Some specialised instances for 'Rep
-------------------------------------------------------------------------------}

instance Show x => Show (Rep (K x) a) where
  show :: Rep (K x) a -> String
show (Rep SmallArray (K x Any)
v) =
      forall a. Show a => a -> String
show forall a b. (a -> b) -> a -> b
$ forall a b. (a -> b) -> [a] -> [b]
Prelude.map forall {k} a (b :: k). K a b -> a
unK (forall (t :: * -> *) a. Foldable t => t a -> [a]
toList SmallArray (K x Any)
v)

instance Eq x => Eq (Rep (K x) a) where
  Rep SmallArray (K x Any)
v == :: Rep (K x) a -> Rep (K x) a -> Bool
== Rep SmallArray (K x Any)
v' =
         forall a b. (a -> b) -> [a] -> [b]
Prelude.map forall {k} a (b :: k). K a b -> a
unK (forall (t :: * -> *) a. Foldable t => t a -> [a]
toList SmallArray (K x Any)
v)
      forall a. Eq a => a -> a -> Bool
== forall a b. (a -> b) -> [a] -> [b]
Prelude.map forall {k} a (b :: k). K a b -> a
unK (forall (t :: * -> *) a. Foldable t => t a -> [a]
toList SmallArray (K x Any)
v')

{-------------------------------------------------------------------------------
  Auxiliary
-------------------------------------------------------------------------------}

-- | Avoid potential segfault with ghc < 9.0
--
-- See <https://gitlab.haskell.org/ghc/ghc/-/issues/16893>.
-- I haven't actually seen this fail in large-records, but we saw it fail in
-- the compact representation branch of sop-core, and what we do here is not
-- so different, so better to play it safe.
noInlineUnsafeCo :: forall a b. a -> b
{-# NOINLINE noInlineUnsafeCo #-}
noInlineUnsafeCo :: forall a b. a -> b
noInlineUnsafeCo = forall a b. a -> b
unsafeCoerce