{-# OPTIONS_GHC -Wno-orphans #-} module RecSpec where import Cleff.Internal.Rec (Elem, Rec (Rec), type (++)) import qualified Cleff.Internal.Rec as Rec import Data.Functor.Identity (Identity (Identity)) import Data.Primitive.SmallArray (indexSmallArray, sizeofSmallArray) import Data.Typeable (cast) import Test.Hspec type I = Identity i :: a -> Identity a i = Identity -- | Test the size invariant of 'Rec'. sizeInvariant :: Rec f es -> Rec f es sizeInvariant xs@(Rec off len arr) | tracked == actual = xs | otherwise = error $ "Cleff.Internal.Rec.sizeInvariant: tracked size " <> show tracked <> ", actual size " <> show actual where tracked = len + off actual = sizeofSmallArray arr -- | Test whether all fields of 'Rec' are really set. allAccessible :: Rec f es -> Rec f es allAccessible xs@(Rec off len arr) = go 0 where go n | n == len = xs | otherwise = indexSmallArray arr (off + n) `seq` go (n + 1) -- | Test all invariants. invariant :: Rec f es -> Rec f es invariant = allAccessible . sizeInvariant singleton :: f e -> Rec f '[e] singleton x = x <:> Rec.empty (<:>) :: f e -> Rec f es -> Rec f (e : es) (<:>) = Rec.cons infixr 5 <:> (<++>) :: Rec f es -> Rec f es' -> Rec f (es ++ es') (<++>) = Rec.concat infixr 5 <++> () :: Elem e es => f e -> Rec f es -> Rec f es () = Rec.update infixr 9 instance Eq (Rec f '[]) where _ == _ = True instance (Eq (Rec f xs), Eq (f x)) => Eq (Rec f (x : xs)) where xs == ys = Rec.head xs == Rec.head ys && Rec.tail xs == Rec.tail ys instance Show (Rec f '[]) where show _ = "empty" instance (Show (f x), Show (Rec f xs)) => Show (Rec f (x : xs)) where showsPrec p xs = showParen (p > consPrec) $ showsPrec (consPrec + 1) (Rec.head xs) . showString " <:> " . showsPrec consPrec (Rec.tail xs) where consPrec = 5 spec :: Spec spec = describe "Rec (SmallArray)" $ parallel do it "is Typeable" do let x = i (5 :: Int) <:> i False <:> Rec.empty y = cast x :: Maybe (Rec I '[Int, String]) z = cast x :: Maybe (Rec I '[Int, Bool]) y `shouldBe` Nothing z `shouldBe` Just x it "can be constructed with 'empty', 'cons', 'concat'" do let x = invariant $ i (5 :: Int) <:> i False <:> i 'X' <:> i (Just 'O') <:> Rec.empty y = invariant $ singleton (i (5 :: Int)) <++> singleton (i False) <++> singleton (i 'X') <++> singleton (i (Just 'O')) a = invariant $ i (5 :: Int) <:> singleton (i False) b = invariant $ singleton (i 'X') <++> singleton (i (Just 'O')) x `shouldBe` y invariant (a <++> b) `shouldBe` x it "can contain multiple fields of the same type" do let x = i (5 :: Int) <:> i False <:> i 'X' <:> i (Just 'O') <:> Rec.empty y = i (5 :: Int) <:> i False <:> i 'X' <:> i (Just 'O') <:> i (6 :: Int) <:> i (Just 'A') <:> Rec.empty invariant (x <++> 6 <:> i (Just 'A') <:> Rec.empty) `shouldBe` y it "can be destructed via 'head', 'tail', 'take', 'drop'" do let a = (x <:> y) <++> singleton z x = i (5 :: Int) y = i (singleton $ i False) <:> i 'X' <:> Rec.empty z = i (Just 'O') Rec.head a `shouldBe` x invariant (Rec.drop @'[Int, Rec I '[Bool], Char] a) `shouldBe` singleton z invariant (Rec.tail a) `shouldBe` invariant (y <++> singleton z) invariant (Rec.take @'[Int, Rec I '[Bool], Char] a) `shouldBe` (x <:> y) it "can get elements via 'index'" do let x = i (5 :: Int) <:> i False <:> i 'X' <:> i (Just 'O') <:> Rec.empty Rec.index @Int x `shouldBe` 5 Rec.index @Bool x `shouldBe` i False Rec.index @Char x `shouldBe` i 'X' Rec.index @(Maybe Char) x `shouldBe` i (Just 'O') it "can get the topmost element among the duplicate ones" do let y = i (5 :: Int) <:> i False <:> i 'X' <:> i (Just 'O') <:> i (6 :: Int) <:> i (Just 'A') <:> Rec.empty Rec.index @Int y `shouldBe` 5 Rec.index @Bool y `shouldBe` i False Rec.index @Char y `shouldBe` i 'X' Rec.index @(Maybe Char) y `shouldBe` i (Just 'O') it "can set elements via 'update'" do let x = i (5 :: Int) <:> i False <:> i 'X' <:> i (Just 'O') <:> Rec.empty invariant (Rec.update @Int 6 x) `shouldBe` 6 <:> i False <:> i 'X' <:> i (Just 'O') <:> Rec.empty invariant (i True x) `shouldBe` 5 <:> i True <:> i 'X' <:> i (Just 'O') <:> Rec.empty invariant (i 'O' x) `shouldBe` 5 <:> i False <:> i 'O' <:> i (Just 'O') <:> Rec.empty invariant (i (Just 'P') x) `shouldBe` 5 <:> i False <:> i 'X' <:> i (Just 'P') <:> Rec.empty it "can get multiple elements via 'pick'" do let x = i (5 :: Int) <:> i False <:> i 'X' <:> i (Just 'O') <:> Rec.empty invariant (Rec.pick @'[Int, Maybe Char] x) `shouldBe` 5 <:> i (Just 'O') <:> Rec.empty it "can reorder elements via 'pick'" do let x = i (5 :: Int) <:> i False <:> i 'X' <:> i (Just 'O') <:> Rec.empty invariant (Rec.pick @'[Bool, Int, Maybe Char] x) `shouldBe` i False <:> 5 <:> i (Just 'O') <:> Rec.empty