{-# 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 x. f x -> g x) -> Rep f a -> Rep g a
map' forall x. f x -> g x
f (Rep SmallArray (f Any)
v) = SmallArray (g Any) -> Rep g a
forall (f :: * -> *) a. SmallArray (f Any) -> Rep f a
Rep (SmallArray (g Any) -> Rep g a) -> SmallArray (g Any) -> Rep g a
forall a b. (a -> b) -> a -> b
$ f Any -> g Any
forall x. f x -> g x
f (f Any -> g Any) -> SmallArray (f Any) -> SmallArray (g Any)
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 :: Rep (m :.: f) a -> m (Rep f a)
sequenceA (Rep SmallArray ((:.:) m f Any)
v) = SmallArray (f Any) -> Rep f a
forall (f :: * -> *) a. SmallArray (f Any) -> Rep f a
Rep (SmallArray (f Any) -> Rep f a)
-> m (SmallArray (f Any)) -> m (Rep f a)
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> SmallArray (m (f Any)) -> m (SmallArray (f Any))
forall (t :: * -> *) (f :: * -> *) a.
(Traversable t, Applicative f) =>
t (f a) -> f (t a)
Prelude.sequenceA (((:.:) m f Any -> m (f Any))
-> SmallArray ((:.:) m f Any) -> SmallArray (m (f Any))
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap (:.:) m f Any -> m (f Any)
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 :: Rep (K a) b -> [a]
collapse (Rep SmallArray (K a Any)
v) = [K a Any] -> [a]
coerce (SmallArray (K a Any) -> [K a Any]
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 :: Rep f a -> [f Any]
toListAny (Rep SmallArray (f Any)
v) = SmallArray (f Any) -> [f Any]
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 :: [b] -> Rep (K b) a
unsafeFromList = SmallArray (K b Any) -> Rep (K b) a
forall (f :: * -> *) a. SmallArray (f Any) -> Rep f a
Rep (SmallArray (K b Any) -> Rep (K b) a)
-> ([b] -> SmallArray (K b Any)) -> [b] -> Rep (K b) a
forall b c a. (b -> c) -> (a -> b) -> a -> c
. [K b Any] -> SmallArray (K b Any)
forall a. [a] -> SmallArray a
smallArrayFromList ([K b Any] -> SmallArray (K b Any))
-> ([b] -> [K b Any]) -> [b] -> SmallArray (K b Any)
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (b -> K b Any) -> [b] -> [K b Any]
forall a b. (a -> b) -> [a] -> [b]
Prelude.map b -> K b Any
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 :: [f Any] -> Rep f a
unsafeFromListAny = SmallArray (f Any) -> Rep f a
forall (f :: * -> *) a. SmallArray (f Any) -> Rep f a
Rep (SmallArray (f Any) -> Rep f a)
-> ([f Any] -> SmallArray (f Any)) -> [f Any] -> Rep f a
forall b c a. (b -> c) -> (a -> b) -> a -> c
. [f Any] -> SmallArray (f Any)
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) =
      [x] -> String
forall a. Show a => a -> String
show ([x] -> String) -> [x] -> String
forall a b. (a -> b) -> a -> b
$ (K x Any -> x) -> [K x Any] -> [x]
forall a b. (a -> b) -> [a] -> [b]
Prelude.map K x Any -> x
forall k a (b :: k). K a b -> a
unK (SmallArray (K x Any) -> [K x Any]
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' =
         (K x Any -> x) -> [K x Any] -> [x]
forall a b. (a -> b) -> [a] -> [b]
Prelude.map K x Any -> x
forall k a (b :: k). K a b -> a
unK (SmallArray (K x Any) -> [K x Any]
forall (t :: * -> *) a. Foldable t => t a -> [a]
toList SmallArray (K x Any)
v)
      [x] -> [x] -> Bool
forall a. Eq a => a -> a -> Bool
== (K x Any -> x) -> [K x Any] -> [x]
forall a b. (a -> b) -> [a] -> [b]
Prelude.map K x Any -> x
forall k a (b :: k). K a b -> a
unK (SmallArray (K x Any) -> [K x Any]
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 :: a -> b
noInlineUnsafeCo = a -> b
forall a b. a -> b
unsafeCoerce