packages feed

recover-rtti 0.3.0.0 → 0.4.0.0

raw patch · 27 files changed

+2698/−1904 lines, 27 filesdep +primitivedep ~aesondep ~unordered-containersdep ~vectorPVP ok

version bump matches the API change (PVP)

Dependencies added: primitive

Dependency ranges changed: aeson, unordered-containers, vector

API changes (from Hackage documentation)

- Debug.RecoverRTTI: Classifiers :: NP Classified xs -> Classifiers xs
- Debug.RecoverRTTI: [C_Custom] :: Classifier UserDefined
- Debug.RecoverRTTI: [FJustPair] :: f a -> f b -> MaybePairF f a b
- Debug.RecoverRTTI: [FJust] :: f a -> MaybeF f a
- Debug.RecoverRTTI: [FLeft] :: f a -> EitherF f a Void
- Debug.RecoverRTTI: [FNothingPair] :: MaybePairF f Void Void
- Debug.RecoverRTTI: [FNothing] :: MaybeF f Void
- Debug.RecoverRTTI: [FRight] :: f b -> EitherF f Void b
- Debug.RecoverRTTI: [classifiedType] :: Classified a -> Classifier a
- Debug.RecoverRTTI: [classifiedValue] :: Classified a -> a
- Debug.RecoverRTTI: classified :: a -> Either Closure (Classified a)
- Debug.RecoverRTTI: data Classifier (a :: Type) :: Type
- Debug.RecoverRTTI: data EitherF f a b
- Debug.RecoverRTTI: data MaybeF f a
- Debug.RecoverRTTI: data MaybePairF f a b
- Debug.RecoverRTTI: newtype Classifiers xs
+ Debug.RecoverRTTI: Elems :: NP (Elem o) xs -> Elems o xs
+ Debug.RecoverRTTI: SomePrimArrayM :: MutableArray RealWorld Any -> SomePrimArrayM
+ Debug.RecoverRTTI: SomePrimitiveVector :: Any -> SomePrimitiveVector
+ Debug.RecoverRTTI: SomePrimitiveVectorM :: Any -> SomePrimitiveVectorM
+ Debug.RecoverRTTI: SomeStorableVector :: Any -> SomeStorableVector
+ Debug.RecoverRTTI: SomeStorableVectorM :: Any -> SomeStorableVectorM
+ Debug.RecoverRTTI: [Absurd] :: FromUsr Void a
+ Debug.RecoverRTTI: [C_Other] :: o a -> Classifier_ o a
+ Debug.RecoverRTTI: [C_Prim] :: PrimClassifier a -> Classifier_ o a
+ Debug.RecoverRTTI: [C_Prim_ArrayM] :: PrimClassifier SomePrimArrayM
+ Debug.RecoverRTTI: [C_Prim_Array] :: Elems o '[a] -> Classifier_ o (Array a)
+ Debug.RecoverRTTI: [C_Vector_PrimitiveM] :: PrimClassifier SomePrimitiveVectorM
+ Debug.RecoverRTTI: [C_Vector_Primitive] :: PrimClassifier SomePrimitiveVector
+ Debug.RecoverRTTI: [C_Vector_StorableM] :: PrimClassifier SomeStorableVectorM
+ Debug.RecoverRTTI: [C_Vector_Storable] :: PrimClassifier SomeStorableVector
+ Debug.RecoverRTTI: [Compose] :: FromUsr b c -> FromUsr a b -> FromUsr a c
+ Debug.RecoverRTTI: [Elem] :: Classifier_ o a -> Elem o a
+ Debug.RecoverRTTI: [F1] :: FromUsr a1 b1 -> FromUsr (f a1) (f b1)
+ Debug.RecoverRTTI: [F2] :: FromUsr a1 b1 -> FromUsr a2 b2 -> FromUsr (f a1 a2) (f b1 b2)
+ Debug.RecoverRTTI: [FN] :: PairWise FromUsr as bs -> FromUsr (f as) (f bs)
+ Debug.RecoverRTTI: [FromUsr] :: FromUsr UserDefined a
+ Debug.RecoverRTTI: [Id] :: FromUsr a a
+ Debug.RecoverRTTI: [IsUserDefined] :: UserDefined -> IsUserDefined UserDefined
+ Debug.RecoverRTTI: [NoElem] :: Elem o Void
+ Debug.RecoverRTTI: [Reclassified] :: o b -> FromUsr a b -> Reclassified o a
+ Debug.RecoverRTTI: canShowClassified :: Classifier a -> Dict Show a
+ Debug.RecoverRTTI: canShowClassified_ :: forall o. (forall a. o a -> Dict Show a) -> forall a. Classifier_ o a -> Dict Show a
+ Debug.RecoverRTTI: canShowPrim :: PrimClassifier a -> Dict Show a
+ Debug.RecoverRTTI: class (PrimSatisfies c, forall a. (c a) => c (Maybe a), forall a b. (c a, c b) => c (Either a b), forall a. (c a) => c [a], forall a. (c a) => c (Ratio a), forall a. (c a) => c (Set a), forall a b. (c a, c b) => c (Map a b), forall a. (c a) => c (IntMap a), forall a. (c a) => c (Seq a), forall a. (c a) => c (Tree a), forall a. (c a) => c (HashSet a), forall a b. (c a, c b) => c (HashMap a b), forall a. (c a) => c (Array a), forall a. (c a) => c (Array a), forall a. (c a) => c (Vector a), forall xs. (All c xs, IsValidSize (Length xs)) => c (WrappedTuple xs)) => ClassifiedSatisfies (c :: Type -> Constraint)
+ Debug.RecoverRTTI: classifiedSatisfies :: forall c o. (ClassifiedSatisfies c, c Void) => (forall a. o a -> Dict c a) -> forall a. Classifier_ o a -> Dict c a
+ Debug.RecoverRTTI: coerceFromUsr :: FromUsr a b -> a -> b
+ Debug.RecoverRTTI: data Classifier_ (o :: Type -> Type) (a :: Type) :: Type
+ Debug.RecoverRTTI: data Elem o a
+ Debug.RecoverRTTI: data FromUsr :: Type -> Type -> Type
+ Debug.RecoverRTTI: data IsUserDefined a
+ Debug.RecoverRTTI: data PrimClassifier (a :: Type)
+ Debug.RecoverRTTI: data Reclassified o a
+ Debug.RecoverRTTI: distribReclassified :: forall o. forall a. Classifier_ (Reclassified o) a -> Reclassified (Classifier_ o) a
+ Debug.RecoverRTTI: mapClassifier :: forall m o o'. Applicative m => (forall a. o a -> m (o' a)) -> forall a. Classifier_ o a -> m (Classifier_ o' a)
+ Debug.RecoverRTTI: mapSome :: (forall x. f x -> g x) -> Some f -> Some g
+ Debug.RecoverRTTI: newtype Elems o xs
+ Debug.RecoverRTTI: newtype SomePrimArrayM
+ Debug.RecoverRTTI: newtype SomePrimitiveVector
+ Debug.RecoverRTTI: newtype SomePrimitiveVectorM
+ Debug.RecoverRTTI: newtype SomeStorableVector
+ Debug.RecoverRTTI: newtype SomeStorableVectorM
+ Debug.RecoverRTTI: primSatisfies :: forall c. PrimSatisfies c => forall a. PrimClassifier a -> Dict c a
+ Debug.RecoverRTTI: reclassify_ :: forall m o o'. Applicative m => (forall a. o a -> m (Reclassified o' a)) -> forall a. Classifier_ o a -> m (Classifier_ (Reclassified o') a)
+ Debug.RecoverRTTI: sameClassifier_ :: forall o. (forall a b. o a -> o b -> Maybe (a :~: b)) -> forall a b. Classifier_ o a -> Classifier_ o b -> Maybe (a :~: b)
+ Debug.RecoverRTTI: sameElem :: forall o. (forall a b. o a -> o b -> Maybe (a :~: b)) -> forall a b. Elem o a -> Elem o b -> Maybe (a :~: b)
+ Debug.RecoverRTTI: sameElems :: forall o r. (forall a b. o a -> o b -> Maybe (a :~: b)) -> forall as bs. Elems o as -> Elems o bs -> (as ~ bs => r) -> Maybe r
+ Debug.RecoverRTTI: samePrim :: PrimClassifier a -> PrimClassifier b -> Maybe (a :~: b)
+ Debug.RecoverRTTI: type Classifier = Classifier_ IsUserDefined
+ Debug.RecoverRTTI: type PrimSatisfies (c :: Type -> Constraint) = (c Bool, c Char, c Double, c Float, c Int, c Int16, c Int8, c Int32, c Int64, c Integer, c Ordering, c (), c Word, c Word8, c Word16, c Word32, c Word64, c String, c ByteString, c ByteString, c ShortByteString, c Text, c Text, c Value, c SomeSTRef, c SomeTVar, c SomeMVar, c SomeFun, c IntSet, c SomePrimArrayM, c SomeStorableVector, c SomeStorableVectorM, c SomePrimitiveVector, c SomePrimitiveVectorM)
+ Debug.RecoverRTTI: unwrapTuple :: WrappedTuple xs -> Tuple xs
+ Debug.RecoverRTTI.Classify: Classified :: Classifier a -> a -> Classified a
+ Debug.RecoverRTTI.Classify: anythingToString :: forall a. a -> String
+ Debug.RecoverRTTI.Classify: canShowClassified :: Classifier a -> Dict Show a
+ Debug.RecoverRTTI.Classify: canShowClassified_ :: forall o. (forall a. o a -> Dict Show a) -> forall a. Classifier_ o a -> Dict Show a
+ Debug.RecoverRTTI.Classify: canShowPrim :: PrimClassifier a -> Dict Show a
+ Debug.RecoverRTTI.Classify: classify :: a -> Either Closure (Classifier a)
+ Debug.RecoverRTTI.Classify: data Classified a
+ Debug.RecoverRTTI.Classify: fromUserDefined :: UserDefined -> (String, [Some Classified])
+ Debug.RecoverRTTI.Classify: instance GHC.Show.Show (Debug.RecoverRTTI.Classify.Classified a)
+ Debug.RecoverRTTI.Classify: instance GHC.Show.Show (Debug.RecoverRTTI.Util.Some Debug.RecoverRTTI.Classify.Classified)
+ Debug.RecoverRTTI.Classify: instance GHC.Show.Show Debug.RecoverRTTI.Wrappers.UserDefined
+ Debug.RecoverRTTI.Classify: pattern ElemK :: Classifier_ o a -> Elems o '[a]
+ Debug.RecoverRTTI.Classify: pattern ElemKK :: Classifier_ o a -> Classifier_ o b -> Elems o '[a, b]
+ Debug.RecoverRTTI.Classify: pattern ElemKU :: Classifier_ o a -> Elems o '[a, Void]
+ Debug.RecoverRTTI.Classify: pattern ElemU :: Elems o '[Void]
+ Debug.RecoverRTTI.Classify: pattern ElemUK :: Classifier_ o b -> Elems o '[Void, b]
+ Debug.RecoverRTTI.Classify: pattern ElemUU :: Elems o '[Void, Void]
+ Debug.RecoverRTTI.ClosureTree: showClosureTree :: Int -> a -> IO String
- Debug.RecoverRTTI: [C_BS_Lazy] :: Classifier ByteString
+ Debug.RecoverRTTI: [C_BS_Lazy] :: PrimClassifier ByteString
- Debug.RecoverRTTI: [C_BS_Short] :: Classifier ShortByteString
+ Debug.RecoverRTTI: [C_BS_Short] :: PrimClassifier ShortByteString
- Debug.RecoverRTTI: [C_BS_Strict] :: Classifier ByteString
+ Debug.RecoverRTTI: [C_BS_Strict] :: PrimClassifier ByteString
- Debug.RecoverRTTI: [C_Bool] :: Classifier Bool
+ Debug.RecoverRTTI: [C_Bool] :: PrimClassifier Bool
- Debug.RecoverRTTI: [C_Char] :: Classifier Char
+ Debug.RecoverRTTI: [C_Char] :: PrimClassifier Char
- Debug.RecoverRTTI: [C_Double] :: Classifier Double
+ Debug.RecoverRTTI: [C_Double] :: PrimClassifier Double
- Debug.RecoverRTTI: [C_Either] :: EitherF Classified a b -> Classifier (Either a b)
+ Debug.RecoverRTTI: [C_Either] :: Elems o '[a, b] -> Classifier_ o (Either a b)
- Debug.RecoverRTTI: [C_Float] :: Classifier Float
+ Debug.RecoverRTTI: [C_Float] :: PrimClassifier Float
- Debug.RecoverRTTI: [C_Fun] :: Classifier SomeFun
+ Debug.RecoverRTTI: [C_Fun] :: PrimClassifier SomeFun
- Debug.RecoverRTTI: [C_HM_Array] :: MaybeF Classified a -> Classifier (Array a)
+ Debug.RecoverRTTI: [C_HM_Array] :: Elems o '[a] -> Classifier_ o (Array a)
- Debug.RecoverRTTI: [C_HashMap] :: MaybePairF Classified a b -> Classifier (HashMap a b)
+ Debug.RecoverRTTI: [C_HashMap] :: Elems o '[a, b] -> Classifier_ o (HashMap a b)
- Debug.RecoverRTTI: [C_HashSet] :: Classified a -> Classifier (HashSet a)
+ Debug.RecoverRTTI: [C_HashSet] :: Elems o '[a] -> Classifier_ o (HashSet a)
- Debug.RecoverRTTI: [C_Int16] :: Classifier Int16
+ Debug.RecoverRTTI: [C_Int16] :: PrimClassifier Int16
- Debug.RecoverRTTI: [C_Int32] :: Classifier Int32
+ Debug.RecoverRTTI: [C_Int32] :: PrimClassifier Int32
- Debug.RecoverRTTI: [C_Int64] :: Classifier Int64
+ Debug.RecoverRTTI: [C_Int64] :: PrimClassifier Int64
- Debug.RecoverRTTI: [C_Int8] :: Classifier Int8
+ Debug.RecoverRTTI: [C_Int8] :: PrimClassifier Int8
- Debug.RecoverRTTI: [C_IntMap] :: MaybeF Classified a -> Classifier (IntMap a)
+ Debug.RecoverRTTI: [C_IntMap] :: Elems o '[a] -> Classifier_ o (IntMap a)
- Debug.RecoverRTTI: [C_IntSet] :: Classifier IntSet
+ Debug.RecoverRTTI: [C_IntSet] :: PrimClassifier IntSet
- Debug.RecoverRTTI: [C_Int] :: Classifier Int
+ Debug.RecoverRTTI: [C_Int] :: PrimClassifier Int
- Debug.RecoverRTTI: [C_Integer] :: Classifier Integer
+ Debug.RecoverRTTI: [C_Integer] :: PrimClassifier Integer
- Debug.RecoverRTTI: [C_List] :: MaybeF Classified a -> Classifier [a]
+ Debug.RecoverRTTI: [C_List] :: Elems o '[a] -> Classifier_ o [a]
- Debug.RecoverRTTI: [C_MVar] :: Classifier SomeMVar
+ Debug.RecoverRTTI: [C_MVar] :: PrimClassifier SomeMVar
- Debug.RecoverRTTI: [C_Map] :: MaybePairF Classified a b -> Classifier (Map a b)
+ Debug.RecoverRTTI: [C_Map] :: Elems o '[a, b] -> Classifier_ o (Map a b)
- Debug.RecoverRTTI: [C_Maybe] :: MaybeF Classified a -> Classifier (Maybe a)
+ Debug.RecoverRTTI: [C_Maybe] :: Elems o '[a] -> Classifier_ o (Maybe a)
- Debug.RecoverRTTI: [C_Ordering] :: Classifier Ordering
+ Debug.RecoverRTTI: [C_Ordering] :: PrimClassifier Ordering
- Debug.RecoverRTTI: [C_Ratio] :: Classified a -> Classifier (Ratio a)
+ Debug.RecoverRTTI: [C_Ratio] :: Elems o '[a] -> Classifier_ o (Ratio a)
- Debug.RecoverRTTI: [C_STRef] :: Classifier SomeSTRef
+ Debug.RecoverRTTI: [C_STRef] :: PrimClassifier SomeSTRef
- Debug.RecoverRTTI: [C_Sequence] :: MaybeF Classified a -> Classifier (Seq a)
+ Debug.RecoverRTTI: [C_Sequence] :: Elems o '[a] -> Classifier_ o (Seq a)
- Debug.RecoverRTTI: [C_Set] :: MaybeF Classified a -> Classifier (Set a)
+ Debug.RecoverRTTI: [C_Set] :: Elems o '[a] -> Classifier_ o (Set a)
- Debug.RecoverRTTI: [C_String] :: Classifier String
+ Debug.RecoverRTTI: [C_String] :: PrimClassifier String
- Debug.RecoverRTTI: [C_TVar] :: Classifier SomeTVar
+ Debug.RecoverRTTI: [C_TVar] :: PrimClassifier SomeTVar
- Debug.RecoverRTTI: [C_Text_Lazy] :: Classifier Text
+ Debug.RecoverRTTI: [C_Text_Lazy] :: PrimClassifier Text
- Debug.RecoverRTTI: [C_Text_Strict] :: Classifier Text
+ Debug.RecoverRTTI: [C_Text_Strict] :: PrimClassifier Text
- Debug.RecoverRTTI: [C_Tree] :: Classified a -> Classifier (Tree a)
+ Debug.RecoverRTTI: [C_Tree] :: Elems o '[a] -> Classifier_ o (Tree a)
- Debug.RecoverRTTI: [C_Tuple] :: (SListI xs, IsValidSize (Length xs)) => Classifiers xs -> Classifier (WrappedTuple xs)
+ Debug.RecoverRTTI: [C_Tuple] :: (SListI xs, IsValidSize (Length xs)) => Elems o xs -> Classifier_ o (WrappedTuple xs)
- Debug.RecoverRTTI: [C_Unit] :: Classifier ()
+ Debug.RecoverRTTI: [C_Unit] :: PrimClassifier ()
- Debug.RecoverRTTI: [C_Value] :: Classifier Value
+ Debug.RecoverRTTI: [C_Value] :: PrimClassifier Value
- Debug.RecoverRTTI: [C_Vector_Boxed] :: MaybeF Classified a -> Classifier (Vector a)
+ Debug.RecoverRTTI: [C_Vector_Boxed] :: Elems o '[a] -> Classifier_ o (Vector a)
- Debug.RecoverRTTI: [C_Word16] :: Classifier Word16
+ Debug.RecoverRTTI: [C_Word16] :: PrimClassifier Word16
- Debug.RecoverRTTI: [C_Word32] :: Classifier Word32
+ Debug.RecoverRTTI: [C_Word32] :: PrimClassifier Word32
- Debug.RecoverRTTI: [C_Word64] :: Classifier Word64
+ Debug.RecoverRTTI: [C_Word64] :: PrimClassifier Word64
- Debug.RecoverRTTI: [C_Word8] :: Classifier Word8
+ Debug.RecoverRTTI: [C_Word8] :: PrimClassifier Word8
- Debug.RecoverRTTI: [C_Word] :: Classifier Word
+ Debug.RecoverRTTI: [C_Word] :: PrimClassifier Word

Files

CHANGELOG.md view
@@ -1,5 +1,17 @@ # Revision history for recover-rtti +## 0.4 -- 2021-06-30++* Correctly set some required lower bounds.+* Add support for reclassification+* Add classification equality check+* Add support for primitive arrays and vectors+* Fix classification on OSX+* General internal cleanup of the library++This release is backwards incompatible with 0.3, but users that simply use+`anythingToString` should be unaffected.+ ## 0.3.0.0 -- 2021-03-17  * Fix bug that could cause `anythingToString` to fail on lists with an
recover-rtti.cabal view
@@ -1,6 +1,6 @@ cabal-version:      2.4 name:               recover-rtti-version:            0.3.0.0+version:            0.4.0.0 synopsis:           Recover run-time type information from the GHC heap description:        The main function in this package is 'classify', which looks                     at the GHC heap to recover type information about arbitrary@@ -24,31 +24,40 @@  library     exposed-modules:  Debug.RecoverRTTI---    other-modules:    Debug.RecoverRTTI.Classifier                       Debug.RecoverRTTI.Classify                       Debug.RecoverRTTI.ClosureTree++    other-modules:    Debug.RecoverRTTI.CheckSame+                      Debug.RecoverRTTI.Classifier+                      Debug.RecoverRTTI.Constraint                       Debug.RecoverRTTI.FlatClosure                       Debug.RecoverRTTI.Modules                       Debug.RecoverRTTI.Nat+                      Debug.RecoverRTTI.Reclassify                       Debug.RecoverRTTI.Tuple                       Debug.RecoverRTTI.Tuple.Recursive                       Debug.RecoverRTTI.Tuple.Size                       Debug.RecoverRTTI.Util                       Debug.RecoverRTTI.Wrappers -    build-depends:    base                 >= 4.13  && < 4.16-                    , aeson                >= 1.5   && < 1.6-                    , bytestring           >= 0.10  && < 0.11-                    , containers           >= 0.6   && < 0.7-                    , ghc-heap             >= 8.8   && < 9.1-                    , mtl                  >= 2.2   && < 2.3-                    , sop-core             >= 0.5   && < 0.6-                    , stm                  >= 2.5   && < 2.6-                    , text                 >= 1.2   && < 1.3-                    , unordered-containers-                    , vector+    build-depends:    base                 >= 4.13     && < 4.16+                    , aeson                >= 1.4      && < 1.6+                    , bytestring           >= 0.10     && < 0.11+                    , containers           >= 0.6      && < 0.7+                    , ghc-heap             >= 8.8      && < 9.1+                    , mtl                  >= 2.2      && < 2.3+                    , sop-core             >= 0.5      && < 0.6+                    , stm                  >= 2.5      && < 2.6+                    , text                 >= 1.2      && < 1.3+                      -- 0.2.12 introduces Data.HashMap.Internal.Array+                    , unordered-containers >= 0.2.12   && < 0.3++                      -- THe oldest ghc we support is 8.8.+		      -- The dependencies below are the oldest versions of+		      -- these packages that compile with this ghc version.+                    , vector               >= 0.12.1.2 && < 0.13+                    , primitive            >= 0.7      && < 0.8+     hs-source-dirs:   src     default-language: Haskell2010     ghc-options:      -Wall@@ -59,12 +68,17 @@     type:             exitcode-stdio-1.0     hs-source-dirs:   tests     main-is:          RecoverRttiTests.hs-    other-modules:    Test.RecoverRTTI.Arbitrary+    other-modules:    Test.RecoverRTTI.Classifier.Arbitrary+                      Test.RecoverRTTI.Classifier.Equality+                      Test.RecoverRTTI.Classifier.Size                       Test.RecoverRTTI.Classify                       Test.RecoverRTTI.ConcreteClassifier-                      Test.RecoverRTTI.Orphans-                      Test.RecoverRTTI.Show+                      Test.RecoverRTTI.Globals+                      Test.RecoverRTTI.Prim+                      Test.RecoverRTTI.QuickCheck.DepGen+                      Test.RecoverRTTI.QuickCheck.Sized                       Test.RecoverRTTI.Sanity+                      Test.RecoverRTTI.Show                       Test.RecoverRTTI.Staged                       Test.RecoverRTTI.UserDefined     build-depends:    base >= 4.13@@ -76,6 +90,7 @@                     , ghc-heap                     , ghc-prim                     , mtl+                    , primitive                     , QuickCheck                     , sop-core                     , stm
src/Debug/RecoverRTTI.hs view
@@ -4,27 +4,57 @@     anythingToString     -- * Recover type information   , classify-  , Classifier(..)-  , Classifiers(..)-    -- ** Pair value with its classifier-  , Classified(..)-  , classified+  , Classifier+  , PrimClassifier(..)+  , IsUserDefined(..)+    -- ** Generalizations+  , Classifier_(..)     -- ** Unknown or partially known type arguments-  , MaybeF(..)-  , EitherF(..)-  , MaybePairF(..)+  , Elem(..)+  , Elems(..)     -- ** Newtype wrappers for unshowable types   , SomeSTRef(..)   , SomeTVar(..)   , SomeMVar(..)   , SomeFun(..)+    -- ** Mutable arrays+  , SomePrimArrayM(..)+  , SomeStorableVector(..)+  , SomeStorableVectorM(..)+  , SomePrimitiveVector(..)+  , SomePrimitiveVectorM(..)+    -- * Working with classifiers+    -- ** Mapping+  , mapClassifier+    -- ** Equality+  , samePrim+  , sameClassifier_+  , sameElem+  , sameElems     -- * User-defined types   , UserDefined -- opaque     -- ** Classify constructor arguments+  , Classified(..)   , fromUserDefined-  , Some(..)+    -- * Recovering type class instances+    -- ** Show+  , canShowClassified+  , canShowPrim+  , canShowClassified_+    -- ** Generic+  , PrimSatisfies+  , primSatisfies+  , ClassifiedSatisfies+  , classifiedSatisfies+    -- * Reclassification+  , Reclassified(..)+  , reclassify_+  , distribReclassified+  , FromUsr(..)+  , coerceFromUsr     -- * Inductive tuples-  , WrappedTuple(..)+  , WrappedTuple(WrappedTuple, TNil, TCons)+  , unwrapTuple   , Tuple     -- ** Translation to/from NP   , tupleFromNP@@ -36,16 +66,23 @@   , smallerIsValid   , toValidSize   , liftValidSize-    -- * Type-level naturals+    -- * Util+    -- ** Type-level naturals   , Nat(..)   , SNat(..)   , KnownNat(..)   , Length+    -- ** Existentials+  , Some(..)+  , mapSome   ) where +import Debug.RecoverRTTI.CheckSame import Debug.RecoverRTTI.Classifier import Debug.RecoverRTTI.Classify+import Debug.RecoverRTTI.Constraint import Debug.RecoverRTTI.Nat+import Debug.RecoverRTTI.Reclassify import Debug.RecoverRTTI.Tuple import Debug.RecoverRTTI.Util import Debug.RecoverRTTI.Wrappers
+ src/Debug/RecoverRTTI/CheckSame.hs view
@@ -0,0 +1,225 @@+{-# LANGUAGE DataKinds           #-}+{-# LANGUAGE FlexibleContexts    #-}+{-# LANGUAGE GADTs               #-}+{-# LANGUAGE LambdaCase          #-}+{-# LANGUAGE RankNTypes          #-}+{-# LANGUAGE ScopedTypeVariables #-}+{-# LANGUAGE TypeOperators       #-}++module Debug.RecoverRTTI.CheckSame (+    -- * Check if two classifiers are the same+    samePrim+  , sameClassifier_+  , sameElem+  , sameElems+  ) where++import Data.SOP+import Data.Type.Equality++import Debug.RecoverRTTI.Classifier++{-------------------------------------------------------------------------------+  Equality check+-------------------------------------------------------------------------------}++samePrim :: PrimClassifier a -> PrimClassifier b -> Maybe (a :~: b)+samePrim = go+  where+    go :: PrimClassifier a -> PrimClassifier b -> Maybe (a :~: b)++    -- Primitive types+    go C_Bool     C_Bool     = Just Refl+    go C_Char     C_Char     = Just Refl+    go C_Double   C_Double   = Just Refl+    go C_Float    C_Float    = Just Refl+    go C_Int      C_Int      = Just Refl+    go C_Int8     C_Int8     = Just Refl+    go C_Int16    C_Int16    = Just Refl+    go C_Int32    C_Int32    = Just Refl+    go C_Int64    C_Int64    = Just Refl+    go C_Integer  C_Integer  = Just Refl+    go C_Ordering C_Ordering = Just Refl+    go C_Unit     C_Unit     = Just Refl+    go C_Word     C_Word     = Just Refl+    go C_Word8    C_Word8    = Just Refl+    go C_Word16   C_Word16   = Just Refl+    go C_Word32   C_Word32   = Just Refl+    go C_Word64   C_Word64   = Just Refl++    -- String types++    go C_String      C_String      = Just Refl+    go C_BS_Strict   C_BS_Strict   = Just Refl+    go C_BS_Lazy     C_BS_Lazy     = Just Refl+    go C_BS_Short    C_BS_Short    = Just Refl+    go C_Text_Strict C_Text_Strict = Just Refl+    go C_Text_Lazy   C_Text_Lazy   = Just Refl++    -- Aeson++    go C_Value C_Value = Just Refl++    -- Reference cells++    go C_STRef C_STRef = Just Refl+    go C_TVar  C_TVar  = Just Refl+    go C_MVar  C_MVar  = Just Refl++    -- Containers without type arguments++    go C_IntSet            C_IntSet            = Just Refl+    go C_Prim_ArrayM       C_Prim_ArrayM       = Just Refl+    go C_Vector_Storable   C_Vector_Storable   = Just Refl+    go C_Vector_StorableM  C_Vector_StorableM  = Just Refl+    go C_Vector_Primitive  C_Vector_Primitive  = Just Refl+    go C_Vector_PrimitiveM C_Vector_PrimitiveM = Just Refl++    -- Functions++    go C_Fun C_Fun = Just Refl++    -- Not equal+    go _ _ = Nothing++    _checkAllCases :: PrimClassifier a -> ()+    _checkAllCases = \case+        -- Primitive types++        C_Bool     -> ()+        C_Char     -> ()+        C_Double   -> ()+        C_Float    -> ()+        C_Int      -> ()+        C_Int8     -> ()+        C_Int16    -> ()+        C_Int32    -> ()+        C_Int64    -> ()+        C_Integer  -> ()+        C_Ordering -> ()+        C_Unit     -> ()+        C_Word     -> ()+        C_Word8    -> ()+        C_Word16   -> ()+        C_Word32   -> ()+        C_Word64   -> ()++        -- String types++        C_String      -> ()+        C_BS_Strict   -> ()+        C_BS_Lazy     -> ()+        C_BS_Short    -> ()+        C_Text_Strict -> ()+        C_Text_Lazy   -> ()++        -- Aeson++        C_Value -> ()++        -- Reference cells++        C_STRef -> ()+        C_TVar  -> ()+        C_MVar  -> ()++        -- Containers without type arguments++        C_IntSet            -> ()+        C_Prim_ArrayM       -> ()+        C_Vector_Storable   -> ()+        C_Vector_StorableM  -> ()+        C_Vector_Primitive  -> ()+        C_Vector_PrimitiveM -> ()++        -- Functions++        C_Fun -> ()++-- | Check that two classifiers are the same+--+-- If they are the same, additionally return a proof that that means the+-- /types/ they classify must be equal (note that equality on the classifiers+-- is strictly stronger than equality on the types: for example, non-empty+-- and empty lists have different classifiers, but classify the same type).+--+-- This is defined on the general type 'Classifier_' rather than on 'Classifier'+-- because different user-defined types may both be classified as @UserDefined@+-- yet not be equal to each other+sameClassifier_ :: forall o.+     (forall a b. o a -> o b -> Maybe (a :~: b))+  -> (forall a b. Classifier_ o a -> Classifier_ o b -> Maybe (a :~: b))+sameClassifier_ sameOther = go+  where+    go :: Classifier_ o a -> Classifier_ o b -> Maybe (a :~: b)++    -- User-defined and primitive types+    go (C_Prim  c) (C_Prim  c') = samePrim  c c'+    go (C_Other c) (C_Other c') = sameOther c c'++    -- Compound+    go (C_Maybe        c) (C_Maybe        c') = sameElems sameOther c c' $ Refl+    go (C_Either       c) (C_Either       c') = sameElems sameOther c c' $ Refl+    go (C_List         c) (C_List         c') = sameElems sameOther c c' $ Refl+    go (C_Ratio        c) (C_Ratio        c') = sameElems sameOther c c' $ Refl+    go (C_Set          c) (C_Set          c') = sameElems sameOther c c' $ Refl+    go (C_Map          c) (C_Map          c') = sameElems sameOther c c' $ Refl+    go (C_IntMap       c) (C_IntMap       c') = sameElems sameOther c c' $ Refl+    go (C_Sequence     c) (C_Sequence     c') = sameElems sameOther c c' $ Refl+    go (C_Tree         c) (C_Tree         c') = sameElems sameOther c c' $ Refl+    go (C_HashSet      c) (C_HashSet      c') = sameElems sameOther c c' $ Refl+    go (C_HashMap      c) (C_HashMap      c') = sameElems sameOther c c' $ Refl+    go (C_HM_Array     c) (C_HM_Array     c') = sameElems sameOther c c' $ Refl+    go (C_Prim_Array   c) (C_Prim_Array   c') = sameElems sameOther c c' $ Refl+    go (C_Vector_Boxed c) (C_Vector_Boxed c') = sameElems sameOther c c' $ Refl+    go (C_Tuple        c) (C_Tuple        c') = sameElems sameOther c c' $ Refl++    -- No match+    go _ _ = Nothing+      where+        _checkAllCases :: Classifier_ o a -> ()+        _checkAllCases = \case+           -- Primitive and user-defined+           C_Prim{}  -> ()+           C_Other{} -> ()++           -- Compound+           C_Maybe{}        -> ()+           C_Either{}       -> ()+           C_List{}         -> ()+           C_Ratio{}        -> ()+           C_Set{}          -> ()+           C_Map{}          -> ()+           C_IntMap{}       -> ()+           C_Sequence{}     -> ()+           C_Tree{}         -> ()+           C_HashSet{}      -> ()+           C_HashMap{}      -> ()+           C_HM_Array{}     -> ()+           C_Prim_Array{}   -> ()+           C_Vector_Boxed{} -> ()+           C_Tuple{}        -> ()++sameElem :: forall o.+     (forall a b. o a -> o b -> Maybe (a :~: b))+  -> (forall a b. Elem o a -> Elem o b -> Maybe (a :~: b))+sameElem sameOther = go+  where+    go :: Elem o a -> Elem o b -> Maybe (a :~: b)+    go NoElem     NoElem   = Just Refl+    go NoElem    (Elem _)  = Nothing+    go (Elem _)   NoElem   = Nothing+    go (Elem ca) (Elem cb) = sameClassifier_ sameOther ca cb++sameElems :: forall o r.+     (forall a b. o a -> o b -> Maybe (a :~: b))+  -> (forall as bs. Elems o as -> Elems o bs -> (as ~ bs => r) -> Maybe r)+sameElems sameOther = go+  where+    go :: Elems o as -> Elems o bs -> (as ~ bs => r) -> Maybe r+    go (Elems Nil)       (Elems Nil)         k = Just k+    go (Elems Nil)       (Elems (_  :* _))   _ = Nothing+    go (Elems (_ :* _))  (Elems Nil)         _ = Nothing+    go (Elems (c :* cs)) (Elems (c' :* cs')) k = do+        Refl <- sameElem sameOther c c'+        go (Elems cs) (Elems cs') k
src/Debug/RecoverRTTI/Classifier.hs view
@@ -1,16 +1,25 @@-{-# LANGUAGE FlexibleContexts   #-}-{-# LANGUAGE GADTs              #-}-{-# LANGUAGE KindSignatures     #-}-{-# LANGUAGE StandaloneDeriving #-}+{-# LANGUAGE DataKinds             #-}+{-# LANGUAGE FlexibleContexts      #-}+{-# LANGUAGE GADTs                 #-}+{-# LANGUAGE KindSignatures        #-}+{-# LANGUAGE LambdaCase            #-}+{-# LANGUAGE QuantifiedConstraints #-}+{-# LANGUAGE RankNTypes            #-}+{-# LANGUAGE ScopedTypeVariables   #-}+{-# LANGUAGE StandaloneDeriving    #-}+{-# LANGUAGE UndecidableInstances  #-}  module Debug.RecoverRTTI.Classifier (-    Classifier(..)-  , Classifiers(..)-  , Classified(..)-    -- * Partial information-  , MaybeF(..)-  , EitherF(..)-  , MaybePairF(..)+    Classifier+  , PrimClassifier(..)+  , IsUserDefined(..)+    -- * Generalizations+  , Classifier_(..)+    -- * Nested classification+  , Elem(..)+  , Elems(..)+    -- * Mapping+  , mapClassifier   ) where  import Data.Aeson (Value)@@ -25,6 +34,7 @@ import Data.Sequence (Seq) import Data.Set (Set) import Data.SOP+import Data.SOP.Dict import Data.Tree (Tree) import Data.Void import Data.Word@@ -33,6 +43,7 @@ import qualified Data.ByteString.Lazy        as BS.Lazy import qualified Data.ByteString.Short       as BS.Short import qualified Data.HashMap.Internal.Array as HashMap (Array)+import qualified Data.Primitive.Array        as Prim (Array) import qualified Data.Text                   as Text.Strict import qualified Data.Text.Lazy              as Text.Lazy import qualified Data.Vector                 as Vector.Boxed@@ -45,55 +56,44 @@   Classifier -------------------------------------------------------------------------------} --- | A value along with its classifier-data Classified a = Classified {-      classifiedType  :: Classifier a-    , classifiedValue :: a-    }- -- | Classifier -- -- Given a value of some unknown type @a@, a @Classifier a@ will tell you what -- the type of @a@ is. This is similar to a @TypeRep@, but since we recover -- this information from the heap, we have less accurate type information than -- @TypeRep@ does.-data Classifier (a :: Type) :: Type where-  -- Primitive types--  C_Bool     :: Classifier Bool-  C_Char     :: Classifier Char-  C_Double   :: Classifier Double-  C_Float    :: Classifier Float-  C_Int      :: Classifier Int-  C_Int16    :: Classifier Int16-  C_Int8     :: Classifier Int8-  C_Int32    :: Classifier Int32-  C_Int64    :: Classifier Int64-  C_Integer  :: Classifier Integer-  C_Ordering :: Classifier Ordering-  C_Unit     :: Classifier ()-  C_Word     :: Classifier Word-  C_Word8    :: Classifier Word8-  C_Word16   :: Classifier Word16-  C_Word32   :: Classifier Word32-  C_Word64   :: Classifier Word64+type Classifier = Classifier_ IsUserDefined -  -- String types-  ---  -- We list @String@ separately, so that we show them properly (rather than-  -- as a list of characters). Of course, empty strings will be inferred as-  -- empty lists instead.+-- | User-defined types+--+-- If we classify a type as user-defined, we pair the classifier with the+-- original value. This means that a @Classifier@ is sufficient information+-- for staged inference by client code that may wish to further classify these+-- types given additional domain knowledge (see also 'reclassify_').+data IsUserDefined a where+  IsUserDefined :: UserDefined -> IsUserDefined UserDefined -  C_String      :: Classifier String-  C_BS_Strict   :: Classifier BS.Strict.ByteString-  C_BS_Lazy     :: Classifier BS.Lazy.ByteString-  C_BS_Short    :: Classifier BS.Short.ShortByteString-  C_Text_Strict :: Classifier Text.Strict.Text-  C_Text_Lazy   :: Classifier Text.Lazy.Text+instance Show (IsUserDefined a) where+  show (IsUserDefined _) = "IsUserDefined" -  -- Aeson+{-------------------------------------------------------------------------------+  Generalizations+-------------------------------------------------------------------------------} -  C_Value :: Classifier Value+-- | Generalization of 'Classifier'+--+-- Type arguments:+--+-- * @o@: Classification of " other " types (not explicitly known to the lib)+--+--   Normally we instantiate this to 'IsUserDefined', classifying all unknown+--   types as 'UserDefined'.+--+-- * @a@: The type we're actually classifying+data Classifier_ (o :: Type -> Type) (a :: Type) :: Type where+  -- Primitive and user-defined types+  C_Prim  :: PrimClassifier a -> Classifier_ o a+  C_Other :: o              a -> Classifier_ o a    -- Compound   --@@ -103,53 +103,150 @@   -- as a 'HashSet'; however, we can only do this of course if we have at   -- least one element. -  C_Maybe        :: MaybeF     Classified a   -> Classifier (Maybe a)-  C_Either       :: EitherF    Classified a b -> Classifier (Either a b)-  C_List         :: MaybeF     Classified a   -> Classifier [a]-  C_Ratio        ::            Classified a   -> Classifier (Ratio a)-  C_Set          :: MaybeF     Classified a   -> Classifier (Set a)-  C_Map          :: MaybePairF Classified a b -> Classifier (Map a b)-  C_IntSet       ::                              Classifier IntSet-  C_IntMap       :: MaybeF     Classified a   -> Classifier (IntMap a)-  C_Sequence     :: MaybeF     Classified a   -> Classifier (Seq a)-  C_Tree         ::            Classified a   -> Classifier (Tree a)-  C_HashSet      ::            Classified a   -> Classifier (HashSet a)-  C_HashMap      :: MaybePairF Classified a b -> Classifier (HashMap a b)-  C_HM_Array     :: MaybeF     Classified a   -> Classifier (HashMap.Array a)-  C_Vector_Boxed :: MaybeF     Classified a   -> Classifier (Vector.Boxed.Vector a)+  C_Maybe        :: Elems o '[a]    -> Classifier_ o (Maybe a)+  C_Either       :: Elems o '[a, b] -> Classifier_ o (Either a b)+  C_List         :: Elems o '[a]    -> Classifier_ o [a]+  C_Ratio        :: Elems o '[a]    -> Classifier_ o (Ratio a)+  C_Set          :: Elems o '[a]    -> Classifier_ o (Set a)+  C_Map          :: Elems o '[a, b] -> Classifier_ o (Map a b)+  C_IntMap       :: Elems o '[a]    -> Classifier_ o (IntMap a)+  C_Sequence     :: Elems o '[a]    -> Classifier_ o (Seq a)+  C_Tree         :: Elems o '[a]    -> Classifier_ o (Tree a)+  C_HashSet      :: Elems o '[a]    -> Classifier_ o (HashSet a)+  C_HashMap      :: Elems o '[a, b] -> Classifier_ o (HashMap a b)+  C_HM_Array     :: Elems o '[a]    -> Classifier_ o (HashMap.Array a)+  C_Prim_Array   :: Elems o '[a]    -> Classifier_ o (Prim.Array a)+  C_Vector_Boxed :: Elems o '[a]    -> Classifier_ o (Vector.Boxed.Vector a)    C_Tuple ::        (SListI xs, IsValidSize (Length xs))-    => Classifiers xs -> Classifier (WrappedTuple xs)+    => Elems o xs -> Classifier_ o (WrappedTuple xs) +-- | Classifier for primitive types+data PrimClassifier (a :: Type) where+  -- Primitive types++  C_Bool     :: PrimClassifier Bool+  C_Char     :: PrimClassifier Char+  C_Double   :: PrimClassifier Double+  C_Float    :: PrimClassifier Float+  C_Int      :: PrimClassifier Int+  C_Int16    :: PrimClassifier Int16+  C_Int8     :: PrimClassifier Int8+  C_Int32    :: PrimClassifier Int32+  C_Int64    :: PrimClassifier Int64+  C_Integer  :: PrimClassifier Integer+  C_Ordering :: PrimClassifier Ordering+  C_Unit     :: PrimClassifier ()+  C_Word     :: PrimClassifier Word+  C_Word8    :: PrimClassifier Word8+  C_Word16   :: PrimClassifier Word16+  C_Word32   :: PrimClassifier Word32+  C_Word64   :: PrimClassifier Word64++  -- String types+  --+  -- We list @String@ separately, so that we show them properly (rather than+  -- as a list of characters). Of course, empty strings will be inferred as+  -- empty lists instead.++  C_String      :: PrimClassifier String+  C_BS_Strict   :: PrimClassifier BS.Strict.ByteString+  C_BS_Lazy     :: PrimClassifier BS.Lazy.ByteString+  C_BS_Short    :: PrimClassifier BS.Short.ShortByteString+  C_Text_Strict :: PrimClassifier Text.Strict.Text+  C_Text_Lazy   :: PrimClassifier Text.Lazy.Text++  -- Aeson++  C_Value :: PrimClassifier Value+   -- Reference cells -  C_STRef :: Classifier SomeSTRef-  C_TVar  :: Classifier SomeTVar-  C_MVar  :: Classifier SomeMVar+  C_STRef :: PrimClassifier SomeSTRef+  C_TVar  :: PrimClassifier SomeTVar+  C_MVar  :: PrimClassifier SomeMVar    -- Functions -  C_Fun :: Classifier SomeFun+  C_Fun :: PrimClassifier SomeFun -  -- User-defined+  -- Containers with no type arguments+  --+  -- We include mutable containers here, because we currently do not attempt+  -- to peek inside them and hence cannot infer any types for their elements. -  C_Custom :: Classifier UserDefined+  C_IntSet            :: PrimClassifier IntSet+  C_Prim_ArrayM       :: PrimClassifier SomePrimArrayM+  C_Vector_Storable   :: PrimClassifier SomeStorableVector+  C_Vector_StorableM  :: PrimClassifier SomeStorableVectorM+  C_Vector_Primitive  :: PrimClassifier SomePrimitiveVector+  C_Vector_PrimitiveM :: PrimClassifier SomePrimitiveVectorM -newtype Classifiers xs = Classifiers (NP Classified xs)+{-------------------------------------------------------------------------------+  Nested classification+-------------------------------------------------------------------------------} +data Elem o a where+  Elem   :: Classifier_ o a -> Elem o a+  NoElem :: Elem o Void++newtype Elems o xs = Elems (NP (Elem o) xs)+ {--------------------------------------------------------------------------------  Partial information+  Show -------------------------------------------------------------------------------} -data MaybeF f a where-  FNothing :: MaybeF f Void-  FJust    :: f a -> MaybeF f a+deriving instance Show (PrimClassifier a) -data EitherF f a b where-  FLeft  :: f a -> EitherF f a Void-  FRight :: f b -> EitherF f Void b+deriving instance (forall x. Show (o x)) => Show (Classifier_ o a)+deriving instance (forall x. Show (o x)) => Show (Elem o a) -data MaybePairF f a b where-  FNothingPair :: MaybePairF f Void Void-  FJustPair    :: f a -> f b -> MaybePairF f a b+instance (forall a. Show (o a), SListI xs) => Show (Elems o xs) where+  showsPrec p (Elems xs) =+      case all_NP allShow of+        Dict -> showsPrec p xs+    where+      allShow :: NP (Dict (Compose Show (Elem o))) xs+      allShow = hpure Dict++{-------------------------------------------------------------------------------+  Map over classifiers+-------------------------------------------------------------------------------}++mapClassifier :: forall m o o'.+     Applicative m+  => (forall a. o a -> m (o' a))+  -> (forall a. Classifier_ o a -> m (Classifier_ o' a))+mapClassifier other = go+  where+    go :: forall a. Classifier_ o a -> m (Classifier_ o' a)+    -- Primitive and user-defined types++    go (C_Prim  c) = pure (C_Prim c)+    go (C_Other c) = C_Other <$> other c++    -- Compound++    go (C_Maybe        c) = C_Maybe        <$> goElems c+    go (C_Either       c) = C_Either       <$> goElems c+    go (C_List         c) = C_List         <$> goElems c+    go (C_Ratio        c) = C_Ratio        <$> goElems c+    go (C_Set          c) = C_Set          <$> goElems c+    go (C_Map          c) = C_Map          <$> goElems c+    go (C_IntMap       c) = C_IntMap       <$> goElems c+    go (C_Sequence     c) = C_Sequence     <$> goElems c+    go (C_Tree         c) = C_Tree         <$> goElems c+    go (C_HashSet      c) = C_HashSet      <$> goElems c+    go (C_HashMap      c) = C_HashMap      <$> goElems c+    go (C_HM_Array     c) = C_HM_Array     <$> goElems c+    go (C_Prim_Array   c) = C_Prim_Array   <$> goElems c+    go (C_Vector_Boxed c) = C_Vector_Boxed <$> goElems c+    go (C_Tuple        c) = C_Tuple        <$> goElems c++    goElems :: SListI xs => Elems o xs -> m (Elems o' xs)+    goElems (Elems cs) = Elems <$> htraverse' goElem cs++    goElem :: Elem o a -> m (Elem o' a)+    goElem (Elem c) = Elem <$> go c+    goElem NoElem   = pure NoElem
src/Debug/RecoverRTTI/Classify.hs view
@@ -1,8 +1,11 @@+{-# LANGUAGE DataKinds             #-} {-# LANGUAGE FlexibleContexts      #-} {-# LANGUAGE FlexibleInstances     #-} {-# LANGUAGE GADTs                 #-} {-# LANGUAGE NamedFieldPuns        #-}+{-# LANGUAGE PatternSynonyms       #-} {-# LANGUAGE QuantifiedConstraints #-}+{-# LANGUAGE RankNTypes            #-} {-# LANGUAGE ScopedTypeVariables   #-} {-# LANGUAGE StandaloneDeriving    #-} {-# LANGUAGE TupleSections         #-}@@ -16,11 +19,21 @@ module Debug.RecoverRTTI.Classify (     -- * Classification     classify-  , classified+    -- * User-defined types+  , Classified(..)   , fromUserDefined     -- * Showing values   , anythingToString+  , canShowPrim   , canShowClassified+  , canShowClassified_+    -- * Patterns for common shapes of 'Elems' (exported for the tests)+  , pattern ElemK+  , pattern ElemU+  , pattern ElemKK+  , pattern ElemUU+  , pattern ElemKU+  , pattern ElemUK   ) where  import Control.Monad.Except@@ -32,22 +45,24 @@ import Data.SOP import Data.SOP.Dict import Data.Tree (Tree)+import Data.Void import GHC.Exts.Heap (Closure) import GHC.Real import System.IO.Unsafe (unsafePerformIO) import Unsafe.Coerce (unsafeCoerce) +import qualified Data.Foldable               as Foldable import qualified Data.HashMap.Internal.Array as HashMap (Array) import qualified Data.HashMap.Internal.Array as HashMap.Array import qualified Data.HashMap.Lazy           as HashMap-import qualified Data.IntMap                 as IntMap import qualified Data.Map                    as Map-import qualified Data.Sequence               as Seq-import qualified Data.Set                    as Set+import qualified Data.Primitive.Array        as Prim.Array+import qualified Data.Primitive.Array        as Prim (Array) import qualified Data.Tree                   as Tree import qualified Data.Vector                 as Vector.Boxed  import Debug.RecoverRTTI.Classifier+import Debug.RecoverRTTI.Constraint import Debug.RecoverRTTI.FlatClosure import Debug.RecoverRTTI.Modules import Debug.RecoverRTTI.Nat@@ -68,65 +83,65 @@       --        -- GHC.Types-      (inKnownModule GhcTypes -> Just "True")  -> return $ mustBe C_Bool-      (inKnownModule GhcTypes -> Just "False") -> return $ mustBe C_Bool-      (inKnownModule GhcTypes -> Just "C#")    -> return $ mustBe C_Char-      (inKnownModule GhcTypes -> Just "D#")    -> return $ mustBe C_Double-      (inKnownModule GhcTypes -> Just "F#")    -> return $ mustBe C_Float-      (inKnownModule GhcTypes -> Just "I#")    -> return $ mustBe C_Int-      (inKnownModule GhcTypes -> Just "LT")    -> return $ mustBe C_Ordering-      (inKnownModule GhcTypes -> Just "GT")    -> return $ mustBe C_Ordering-      (inKnownModule GhcTypes -> Just "EQ")    -> return $ mustBe C_Ordering-      (inKnownModule GhcTypes -> Just "W#")    -> return $ mustBe C_Word+      (inKnownModule GhcTypes -> Just "True")  -> return $ mustBe $ C_Prim C_Bool+      (inKnownModule GhcTypes -> Just "False") -> return $ mustBe $ C_Prim C_Bool+      (inKnownModule GhcTypes -> Just "C#")    -> return $ mustBe $ C_Prim C_Char+      (inKnownModule GhcTypes -> Just "D#")    -> return $ mustBe $ C_Prim C_Double+      (inKnownModule GhcTypes -> Just "F#")    -> return $ mustBe $ C_Prim C_Float+      (inKnownModule GhcTypes -> Just "I#")    -> return $ mustBe $ C_Prim C_Int+      (inKnownModule GhcTypes -> Just "LT")    -> return $ mustBe $ C_Prim C_Ordering+      (inKnownModule GhcTypes -> Just "GT")    -> return $ mustBe $ C_Prim C_Ordering+      (inKnownModule GhcTypes -> Just "EQ")    -> return $ mustBe $ C_Prim C_Ordering+      (inKnownModule GhcTypes -> Just "W#")    -> return $ mustBe $ C_Prim C_Word        -- GHC.Tuple-      (inKnownModule GhcTuple -> Just "()") -> return $ mustBe C_Unit+      (inKnownModule GhcTuple -> Just "()") -> return $ mustBe $ C_Prim C_Unit        -- GHC.Int-      (inKnownModule GhcInt -> Just "I8#")  -> return $ mustBe C_Int8-      (inKnownModule GhcInt -> Just "I16#") -> return $ mustBe C_Int16-      (inKnownModule GhcInt -> Just "I32#") -> return $ mustBe C_Int32-      (inKnownModule GhcInt -> Just "I64#") -> return $ mustBe C_Int64+      (inKnownModule GhcInt -> Just "I8#")  -> return $ mustBe $ C_Prim C_Int8+      (inKnownModule GhcInt -> Just "I16#") -> return $ mustBe $ C_Prim C_Int16+      (inKnownModule GhcInt -> Just "I32#") -> return $ mustBe $ C_Prim C_Int32+      (inKnownModule GhcInt -> Just "I64#") -> return $ mustBe $ C_Prim C_Int64        -- GHC.Integer-      (inKnownModule GhcIntegerType -> Just "S#")  -> return $ mustBe C_Integer-      (inKnownModule GhcIntegerType -> Just "Jp#") -> return $ mustBe C_Integer-      (inKnownModule GhcIntegerType -> Just "Jn#") -> return $ mustBe C_Integer-      (inKnownModule GhcNumInteger  -> Just "IS")  -> return $ mustBe C_Integer-      (inKnownModule GhcNumInteger  -> Just "IP")  -> return $ mustBe C_Integer-      (inKnownModule GhcNumInteger  -> Just "IN")  -> return $ mustBe C_Integer+      (inKnownModule GhcIntegerType -> Just "S#")  -> return $ mustBe $ C_Prim C_Integer+      (inKnownModule GhcIntegerType -> Just "Jp#") -> return $ mustBe $ C_Prim C_Integer+      (inKnownModule GhcIntegerType -> Just "Jn#") -> return $ mustBe $ C_Prim C_Integer+      (inKnownModule GhcNumInteger  -> Just "IS")  -> return $ mustBe $ C_Prim C_Integer+      (inKnownModule GhcNumInteger  -> Just "IP")  -> return $ mustBe $ C_Prim C_Integer+      (inKnownModule GhcNumInteger  -> Just "IN")  -> return $ mustBe $ C_Prim C_Integer        -- GHC.Word-      (inKnownModule GhcWord -> Just "W8#")  -> return $ mustBe C_Word8-      (inKnownModule GhcWord -> Just "W16#") -> return $ mustBe C_Word16-      (inKnownModule GhcWord -> Just "W32#") -> return $ mustBe C_Word32-      (inKnownModule GhcWord -> Just "W64#") -> return $ mustBe C_Word64+      (inKnownModule GhcWord -> Just "W8#")  -> return $ mustBe $ C_Prim C_Word8+      (inKnownModule GhcWord -> Just "W16#") -> return $ mustBe $ C_Prim C_Word16+      (inKnownModule GhcWord -> Just "W32#") -> return $ mustBe $ C_Prim C_Word32+      (inKnownModule GhcWord -> Just "W64#") -> return $ mustBe $ C_Prim C_Word64        --       -- String types       --        -- bytestring-      (inKnownModule DataByteStringInternal      -> Just "PS")    -> return $ mustBe C_BS_Strict-      (inKnownModule DataByteStringLazyInternal  -> Just "Empty") -> return $ mustBe C_BS_Lazy-      (inKnownModule DataByteStringLazyInternal  -> Just "Chunk") -> return $ mustBe C_BS_Lazy-      (inKnownModule DataByteStringShortInternal -> Just "SBS")   -> return $ mustBe C_BS_Short+      (inKnownModule DataByteStringInternal      -> Just "PS")    -> return $ mustBe $ C_Prim C_BS_Strict+      (inKnownModule DataByteStringLazyInternal  -> Just "Empty") -> return $ mustBe $ C_Prim C_BS_Lazy+      (inKnownModule DataByteStringLazyInternal  -> Just "Chunk") -> return $ mustBe $ C_Prim C_BS_Lazy+      (inKnownModule DataByteStringShortInternal -> Just "SBS")   -> return $ mustBe $ C_Prim C_BS_Short        -- text-      (inKnownModule DataTextInternal     -> Just "Text")  -> return $ mustBe C_Text_Strict-      (inKnownModule DataTextInternalLazy -> Just "Chunk") -> return $ mustBe C_Text_Lazy-      (inKnownModule DataTextInternalLazy -> Just "Empty") -> return $ mustBe C_Text_Lazy+      (inKnownModule DataTextInternal     -> Just "Text")  -> return $ mustBe $ C_Prim C_Text_Strict+      (inKnownModule DataTextInternalLazy -> Just "Chunk") -> return $ mustBe $ C_Prim C_Text_Lazy+      (inKnownModule DataTextInternalLazy -> Just "Empty") -> return $ mustBe $ C_Prim C_Text_Lazy        --       -- Aeson       -- -      (inKnownModule DataAesonTypesInternal -> Just "Object") -> return $ mustBe C_Value-      (inKnownModule DataAesonTypesInternal -> Just "Array")  -> return $ mustBe C_Value-      (inKnownModule DataAesonTypesInternal -> Just "String") -> return $ mustBe C_Value-      (inKnownModule DataAesonTypesInternal -> Just "Number") -> return $ mustBe C_Value-      (inKnownModule DataAesonTypesInternal -> Just "Bool")   -> return $ mustBe C_Value-      (inKnownModule DataAesonTypesInternal -> Just "Null")   -> return $ mustBe C_Value+      (inKnownModule DataAesonTypesInternal -> Just "Object") -> return $ mustBe $ C_Prim C_Value+      (inKnownModule DataAesonTypesInternal -> Just "Array")  -> return $ mustBe $ C_Prim C_Value+      (inKnownModule DataAesonTypesInternal -> Just "String") -> return $ mustBe $ C_Prim C_Value+      (inKnownModule DataAesonTypesInternal -> Just "Number") -> return $ mustBe $ C_Prim C_Value+      (inKnownModule DataAesonTypesInternal -> Just "Bool")   -> return $ mustBe $ C_Prim C_Value+      (inKnownModule DataAesonTypesInternal -> Just "Null")   -> return $ mustBe $ C_Prim C_Value        --       -- Compound (ghc-prim)@@ -168,11 +183,11 @@        -- IntSet       (inKnownModule DataIntSetInternal -> Just "Bin") ->-        return $ mustBe $ C_IntSet+        return $ mustBe $ C_Prim C_IntSet       (inKnownModule DataIntSetInternal -> Just "Tip") ->-        return $ mustBe $ C_IntSet+        return $ mustBe $ C_Prim C_IntSet       (inKnownModule DataIntSetInternal -> Just "Nil") ->-        return $ mustBe $ C_IntSet+        return $ mustBe $ C_Prim C_IntSet        -- IntMap       (inKnownModule DataIntMapInternal -> Just "Nil") ->@@ -221,30 +236,48 @@       (inKnownModule DataHashMapInternalArray -> Just "Array") ->         mustBe <$> classifyHMArray (unsafeCoerce x) +      -- Arrays from @primitive@+      (inKnownModule DataPrimitiveArray -> Just "Array") ->+        mustBe <$> classifyPrimArray (unsafeCoerce x)+      (inKnownModule DataPrimitiveArray -> Just "MutableArray") ->+        return $ mustBe $ C_Prim C_Prim_ArrayM+       -- Boxed vectors       (inKnownModule DataVector -> Just "Vector") ->         mustBe <$> classifyVectorBoxed (unsafeCoerce x) +      -- Storable vectors+      (inKnownModule DataVectorStorable -> Just "Vector") ->+        return $ mustBe $ C_Prim C_Vector_Storable+      (inKnownModule DataVectorStorableMutable -> Just "MVector") ->+        return $ mustBe $ C_Prim C_Vector_StorableM++      -- Primitive vectors+      (inKnownModule DataVectorPrimitive -> Just "Vector") ->+        return $ mustBe $ C_Prim C_Vector_Primitive+      (inKnownModule DataVectorPrimitiveMutable -> Just "MVector") ->+        return $ mustBe $ C_Prim C_Vector_PrimitiveM+       --       -- Reference cells       -- -      (inKnownModule GhcSTRef    -> Just "STRef") -> return $ mustBe C_STRef-      (inKnownModule GhcMVar     -> Just "MVar")  -> return $ mustBe C_MVar-      (inKnownModule GhcConcSync -> Just "TVar")  -> return $ mustBe C_TVar+      (inKnownModule GhcSTRef    -> Just "STRef") -> return $ mustBe $ C_Prim C_STRef+      (inKnownModule GhcMVar     -> Just "MVar")  -> return $ mustBe $ C_Prim C_MVar+      (inKnownModule GhcConcSync -> Just "TVar")  -> return $ mustBe $ C_Prim C_TVar        --       -- Functions       -- -      FunClosure {} -> return $ mustBe C_Fun+      FunClosure {} -> return $ mustBe $ C_Prim C_Fun        --       -- User defined       --        ConstrClosure {} ->-        return $ mustBe C_Custom+        return $ mustBe $ C_Other (IsUserDefined (unsafeCoerce x))        --       -- Classification failed@@ -252,7 +285,7 @@        OtherClosure other -> ExceptT $ return (Left other) -mustBe :: Classifier b -> Classifier a+mustBe :: Classifier_ o b -> Classifier_ o a mustBe = unsafeCoerce  -- | Classify a value@@ -271,131 +304,78 @@   Classification for compound types -------------------------------------------------------------------------------} -classifyMaybe ::-     Maybe a-  -> ExceptT Closure IO (Classifier (Maybe a))-classifyMaybe x =-    case x of-      Nothing -> return $ mustBe $ C_Maybe FNothing-      Just x' -> do-        cx <- classifyIO x'-        return $ mustBe $ C_Maybe (FJust (Classified cx x'))+classifyMaybe :: Maybe a -> ExceptT Closure IO (Classifier (Maybe a))+classifyMaybe = classifyFoldable C_Maybe  classifyEither ::      Either a b   -> ExceptT Closure IO (Classifier (Either a b)) classifyEither x =     case x of-      Left x' -> do-        cx <- classifyIO x'-        return $ mustBe $ C_Either (FLeft (Classified cx x'))-      Right y' -> do-        cy <- classifyIO y'-        return $ mustBe $ C_Either (FRight (Classified cy y'))+      Left  x' -> (mustBe . C_Either . ElemKU)  <$> classifyIO x'+      Right y' -> (mustBe . C_Either . ElemUK) <$> classifyIO y' -classifyList ::-     [a]-  -> ExceptT Closure IO (Classifier [a])-classifyList x =-    case x of-      []   -> return $ mustBe $ C_List FNothing-      x':_ -> do-        cx <- classifyIO x'-        return $ case cx of-          C_Char     -> mustBe $ C_String-          _otherwise -> mustBe $ C_List (FJust (Classified cx x'))+classifyList :: [a] -> ExceptT Closure IO (Classifier [a])+classifyList = classifyFoldable c_list+  where+    -- We special case for @String@, so that @show@ will use the (overlapped)+    -- instance for @String@ instead of the general instance for @[a]@+    c_list :: Elems o '[x] -> Classifier_ o [x]+    c_list (ElemK (C_Prim C_Char)) = C_Prim C_String+    c_list c = C_List c -classifyRatio ::-     Ratio a-  -> ExceptT Closure IO (Classifier (Ratio a))-classifyRatio (x' :% _) = do-    cx <- classifyIO x'-    return $ mustBe $ C_Ratio (Classified cx x')+classifyRatio :: Ratio a -> ExceptT Closure IO (Classifier (Ratio a))+classifyRatio (x' :% _) = mustBe . C_Ratio . ElemK <$> classifyIO x' -classifySet ::-     Set a-  -> ExceptT Closure IO (Classifier (Set a))-classifySet x =-    case Set.lookupMin x of-      Nothing -> return $ mustBe $ C_Set FNothing-      Just x' -> do-        cx <- classifyIO x'-        return $ mustBe $ C_Set (FJust (Classified cx x'))+classifySet :: Set a -> ExceptT Closure IO (Classifier (Set a))+classifySet = classifyFoldable C_Set -classifyMap ::-     Map a b-  -> ExceptT Closure IO (Classifier (Map a b))-classifyMap x =-    case Map.lookupMin x of-      Nothing       -> return $ mustBe $ C_Map FNothingPair-      Just (x', y') -> do-        cx <- classifyIO x'-        cy <- classifyIO y'-        return $ mustBe $ C_Map (FJustPair (Classified cx x') (Classified cy y'))+classifyMap :: Map a b -> ExceptT Closure IO (Classifier (Map a b))+classifyMap = classifyFoldablePair C_Map Map.toList -classifyIntMap ::-     IntMap a-  -> ExceptT Closure IO (Classifier (IntMap a))-classifyIntMap x =-    case IntMap.minView x of-      Nothing      -> return $ mustBe $ C_IntMap FNothing-      Just (x', _) -> do-        cx <- classifyIO x'-        return $ mustBe $ C_IntMap (FJust (Classified cx x'))+classifyIntMap :: IntMap a -> ExceptT Closure IO (Classifier (IntMap a))+classifyIntMap = classifyFoldable C_IntMap -classifySequence ::-     Seq a-  -> ExceptT Closure IO (Classifier (Seq a))-classifySequence x =-    case Seq.viewl x of-      Seq.EmptyL  -> return $ mustBe $ C_Sequence FNothing-      x' Seq.:< _ -> do-        cx <- classifyIO x'-        return $ mustBe $ C_Sequence (FJust (Classified cx x'))+classifySequence :: Seq a -> ExceptT Closure IO (Classifier (Seq a))+classifySequence = classifyFoldable C_Sequence -classifyTree ::-     Tree a-  -> ExceptT Closure IO (Classifier (Tree a))-classifyTree x =-    case x of-      Tree.Node x' _ -> do-        cx <- classifyIO x'-        return $ mustBe $ C_Tree (Classified cx x')+classifyTree :: Tree a -> ExceptT Closure IO (Classifier (Tree a))+classifyTree (Tree.Node x' _) = mustBe . C_Tree . ElemK <$> classifyIO x' -classifyHashMap ::-     HashMap a b-  -> ExceptT Closure IO (Classifier (HashMap a b))-classifyHashMap x =-    case HashMap.toList x of-      []           -> return $ mustBe $ C_HashMap FNothingPair-      ((x', y'):_) -> do-        cx <- classifyIO x'-        cy <- classifyIO y'-        return $ case cy of-          C_Unit     -> mustBe $ C_HashSet (Classified cx x')-          _otherwise -> mustBe $ C_HashMap (FJustPair (Classified cx x') (Classified cy y'))+classifyHashMap :: HashMap a b -> ExceptT Closure IO (Classifier (HashMap a b))+classifyHashMap = classifyFoldablePair c_hashmap HashMap.toList+  where+    -- HashSet is a newtype around HashMap+    c_hashmap :: Elems o '[x, y] -> Classifier_ o (HashMap x y)+    c_hashmap (ElemKK c (C_Prim C_Unit)) = mustBe $ C_HashSet (ElemK c)+    c_hashmap c = C_HashMap c  classifyHMArray ::      HashMap.Array a-  -> ExceptT Closure IO (Classifier (Tree a))-classifyHMArray x =-    if HashMap.Array.length x == 0-      then return $ mustBe $ C_HM_Array FNothing-      else do-        let x' = HashMap.Array.index x 0-        cx <- classifyIO x'-        return $ mustBe $ C_HM_Array (FJust (Classified cx x'))+  -> ExceptT Closure IO (Classifier (HashMap.Array a))+classifyHMArray =+    classifyArrayLike+      C_HM_Array+      HashMap.Array.length+      (`HashMap.Array.index` 0) +classifyPrimArray ::+     Prim.Array a+  -> ExceptT Closure IO (Classifier (Prim.Array a))+classifyPrimArray =+    classifyArrayLike+      C_Prim_Array+      Prim.Array.sizeofArray+      (`Prim.Array.indexArray` 0)+ classifyVectorBoxed ::      Vector.Boxed.Vector a   -> ExceptT Closure IO (Classifier (Vector.Boxed.Vector a))-classifyVectorBoxed x =-    if Vector.Boxed.length x == 0-      then return $ mustBe $ C_Vector_Boxed FNothing-      else do-        let x' = Vector.Boxed.head x-        cx <- classifyIO x'-        return $ mustBe $ C_Vector_Boxed (FJust (Classified cx x'))+classifyVectorBoxed =+    classifyArrayLike+      C_Vector_Boxed+      Vector.Boxed.length+      Vector.Boxed.head  classifyTuple ::      (SListI xs, IsValidSize (Length xs))@@ -403,14 +383,75 @@   -> ExceptT Closure IO (Classifier (WrappedTuple xs)) classifyTuple ptrs = do     cs <- hsequence' (hmap aux ptrs)-    return $ C_Tuple (Classifiers cs)+    return $ C_Tuple (Elems (hmap Elem cs))   where-    aux :: K Box a -> (ExceptT Closure IO :.: Classified) a-    aux (K (Box x)) = Comp $ do-        c <- classifyIO (unsafeCoerce x)-        return $ Classified c (unsafeCoerce x)+    aux :: K Box a -> (ExceptT Closure IO :.: Classifier) a+    aux (K (Box x)) = Comp $ classifyIO (unsafeCoerce x)  {-------------------------------------------------------------------------------+  Helper functions for defining classifiers+-------------------------------------------------------------------------------}++classifyFoldable ::+     Foldable f+  => (forall o x. Elems o '[x] -> Classifier_ o (f x))+  -> f a -> ExceptT Closure IO (Classifier (f a))+classifyFoldable cc x =+    case Foldable.toList x of+      []   -> return $ mustBe $ cc ElemU+      x':_ -> mustBe . cc . ElemK <$> classifyIO x'++classifyFoldablePair ::+     (forall o x y. Elems o '[x, y] -> Classifier_ o (f x y))+  -> (f a b -> [(a, b)])+  -> f a b -> ExceptT Closure IO (Classifier (f a b))+classifyFoldablePair cc toList x =+    case toList x of+      []         -> return $ mustBe $ cc ElemUU+      (x', y'):_ -> (\ca cb -> mustBe $ cc (ElemKK ca cb))+                       <$> classifyIO x'+                       <*> classifyIO y'++classifyArrayLike ::+     (forall o x. Elems o '[x] -> Classifier_ o (f x))+  -> (f a -> Int)  -- ^ Get the length of the array+  -> (f a -> a)    -- ^ Get the first element (provided the array is not empty)+  -> f a -> ExceptT Closure IO (Classifier (f a))+classifyArrayLike cc getLen getFirst x =+    if getLen x == 0+      then return $ mustBe $ cc ElemU+      else do+        let x' = getFirst x+        mustBe . cc . ElemK <$> classifyIO x'++{-------------------------------------------------------------------------------+  Patterns for common shapes of 'Elems'++  This is mostly useful internally; we export these only for the benefit of the+  QuickCheck generator. Most other code can treat the all types uniformly.++  We distinguish between which elements are (K)nown and which (U)nknown+-------------------------------------------------------------------------------}++pattern ElemK :: Classifier_ o a -> Elems o '[a]+pattern ElemK c = Elems (Elem c :* Nil)++pattern ElemU :: Elems o '[Void]+pattern ElemU = Elems (NoElem :* Nil)++pattern ElemKK :: Classifier_ o a -> Classifier_ o b -> Elems o '[a, b]+pattern ElemKK ca cb = Elems (Elem ca :* Elem cb :* Nil)++pattern ElemUU :: Elems o '[Void, Void]+pattern ElemUU = Elems (NoElem :* NoElem :* Nil)++pattern ElemKU :: Classifier_ o a -> Elems o '[a, Void]+pattern ElemKU c = Elems (Elem c :* NoElem :* Nil)++pattern ElemUK :: Classifier_ o b -> Elems o '[Void, b]+pattern ElemUK c = Elems (NoElem :* Elem c :* Nil)++{-------------------------------------------------------------------------------   Recognizing tuples -------------------------------------------------------------------------------} @@ -421,16 +462,12 @@     toValidSize (length xs + 1)  {--------------------------------------------------------------------------------  Classified values--------------------------------------------------------------------------------}--classified :: a -> Either Closure (Classified a)-classified x = (\cx -> Classified cx x) <$> classify x--{-------------------------------------------------------------------------------   Classify constructor arguments -------------------------------------------------------------------------------} +-- | Bundle a value with its classifier+data Classified a = Classified (Classifier a) a+ -- | Classify the arguments to the constructor -- -- Additionally returns the constructor name itself.@@ -478,14 +515,11 @@ -- If classification fails, we show the actual closure. anythingToString :: forall a. a -> String anythingToString x =-    case classified x of-      Right classifier -> showClassifiedValue 0 classifier ""+    case classify x of       Left  closure    -> show closure+      Right classifier -> case canShowClassified classifier of+                            Dict -> show x -deriving instance Show (Classifier a)-deriving instance Show (MaybeF     Classified a)-deriving instance Show (EitherF    Classified a b)-deriving instance Show (MaybePairF Classified a b) deriving instance Show (Some Classified)  instance Show (Classified a) where@@ -497,14 +531,6 @@           . showString " "           . showsPrec 11 x -instance SListI xs => Show (Classifiers xs) where-  show (Classifiers xs) = go (hpure Dict)-    where-      go :: NP (Dict (Compose Show Classified)) xs -> String-      go dicts =-          case all_NP dicts of-            Dict -> "(" ++ show xs ++ ")"- -- | Show the classified value (without the classifier) showClassifiedValue :: Int -> Classified a -> ShowS showClassifiedValue p (Classified c x) =@@ -512,107 +538,18 @@       Dict -> showsPrec p x  canShowClassified :: Classifier a -> Dict Show a-canShowClassified = go+canShowClassified = canShowClassified_ showOther   where-    go :: Classifier a -> Dict Show a--    ---    -- Simple cases-    ----    -- Primitive types-    go C_Bool     = Dict-    go C_Char     = Dict-    go C_Double   = Dict-    go C_Float    = Dict-    go C_Int      = Dict-    go C_Int16    = Dict-    go C_Int8     = Dict-    go C_Int32    = Dict-    go C_Int64    = Dict-    go C_Integer  = Dict-    go C_Ordering = Dict-    go C_Unit     = Dict-    go C_Word     = Dict-    go C_Word8    = Dict-    go C_Word16   = Dict-    go C_Word32   = Dict-    go C_Word64   = Dict--    -- String types-    go C_String      = Dict-    go C_BS_Strict   = Dict-    go C_BS_Lazy     = Dict-    go C_BS_Short    = Dict-    go C_Text_Strict = Dict-    go C_Text_Lazy   = Dict--    -- Aeson-    go C_Value = Dict--    -- Reference cells-    go C_STRef = Dict-    go C_TVar  = Dict-    go C_MVar  = Dict--    -- Functions-    go C_Fun = Dict--    -- User-defined-    go C_Custom = Dict--    ---    -- Compound-    ----    go (C_Maybe        c) = goMaybeF     c-    go (C_Either       c) = goEitherF    c-    go (C_List         c) = goMaybeF     c-    go (C_Ratio        c) = goF          c-    go (C_Set          c) = goMaybeF     c-    go (C_Map          c) = goMaybePairF c-    go  C_IntSet          = Dict-    go (C_IntMap       c) = goMaybeF     c-    go (C_Sequence     c) = goMaybeF     c-    go (C_Tree         c) = goF          c-    go (C_HashSet      c) = goF          c-    go (C_HashMap      c) = goMaybePairF c-    go (C_HM_Array     c) = goMaybeF     c-    go (C_Vector_Boxed c) = goMaybeF     c--    go (C_Tuple (Classifiers cs)) =-        case all_NP (hmap (canShowClassified . classifiedType) cs) of-          Dict -> Dict--    goMaybeF :: forall f a.-         (forall x. Show x => Show (f x))-      => MaybeF Classified a -> Dict Show (f a)-    goMaybeF FNothing  = Dict-    goMaybeF (FJust c) = case go (classifiedType c) of-                           Dict -> Dict--    goEitherF :: forall f a b.-         (forall x y. (Show x, Show y) => Show (f x y))-      => EitherF Classified a b -> Dict Show (f a b)-    goEitherF (FLeft  c) = case go (classifiedType c) of-                             Dict -> Dict-    goEitherF (FRight c) = case go (classifiedType c) of-                             Dict -> Dict+    showOther :: IsUserDefined a -> Dict Show a+    showOther (IsUserDefined _) = Dict -    goF :: forall f a.-         (forall x. Show x => Show (f x))-      => Classified a -> Dict Show (f a )-    goF c = case go (classifiedType c) of-              Dict -> Dict+canShowPrim :: PrimClassifier a -> Dict Show a+canShowPrim = primSatisfies -    goMaybePairF :: forall f a b.-         (forall x y. (Show x, Show y) => Show (f x y))-      => MaybePairF Classified a b -> Dict Show (f a b)-    goMaybePairF FNothingPair     = Dict-    goMaybePairF (FJustPair c c') = case ( go (classifiedType c)-                                         , go (classifiedType c')-                                         ) of-                                      (Dict, Dict) -> Dict+canShowClassified_ :: forall o.+     (forall a. o a -> Dict Show a)+  -> (forall a. Classifier_ o a -> Dict Show a)+canShowClassified_ = classifiedSatisfies  instance Show UserDefined where   showsPrec p x =
+ src/Debug/RecoverRTTI/Constraint.hs view
@@ -0,0 +1,248 @@+{-# LANGUAGE ConstraintKinds         #-}+{-# LANGUAGE DataKinds               #-}+{-# LANGUAGE FlexibleContexts        #-}+{-# LANGUAGE FlexibleInstances       #-}+{-# LANGUAGE GADTs                   #-}+{-# LANGUAGE KindSignatures          #-}+{-# LANGUAGE QuantifiedConstraints   #-}+{-# LANGUAGE RankNTypes              #-}+{-# LANGUAGE ScopedTypeVariables     #-}+{-# LANGUAGE UndecidableInstances    #-}+{-# LANGUAGE UndecidableSuperClasses #-}++-- | Establish that a constraint holds for all classified types+module Debug.RecoverRTTI.Constraint (+    PrimSatisfies+  , primSatisfies+  , ClassifiedSatisfies+  , classifiedSatisfies+  ) where++import Data.Aeson (Value)+import Data.HashMap.Lazy (HashMap)+import Data.HashSet (HashSet)+import Data.Int+import Data.IntMap (IntMap)+import Data.IntSet (IntSet)+import Data.Kind+import Data.Map (Map)+import Data.Ratio+import Data.Sequence (Seq)+import Data.Set (Set)+import Data.SOP+import Data.SOP.Dict+import Data.Tree (Tree)+import Data.Void+import Data.Word++import qualified Data.ByteString             as BS.Strict+import qualified Data.ByteString.Lazy        as BS.Lazy+import qualified Data.ByteString.Short       as BS.Short+import qualified Data.HashMap.Internal.Array as HashMap (Array)+import qualified Data.Primitive.Array        as Prim (Array)+import qualified Data.Text                   as Text.Strict+import qualified Data.Text.Lazy              as Text.Lazy+import qualified Data.Vector                 as Vector.Boxed++import Debug.RecoverRTTI.Classifier+import Debug.RecoverRTTI.Nat+import Debug.RecoverRTTI.Tuple+import Debug.RecoverRTTI.Wrappers++{-------------------------------------------------------------------------------+  Primitives+-------------------------------------------------------------------------------}++type PrimSatisfies (c :: Type -> Constraint) = (+  -- Primitive types++    c Bool+  , c Char+  , c Double+  , c Float+  , c Int+  , c Int16+  , c Int8+  , c Int32+  , c Int64+  , c Integer+  , c Ordering+  , c ()+  , c Word+  , c Word8+  , c Word16+  , c Word32+  , c Word64++  -- String types++  , c String+  , c BS.Strict.ByteString+  , c BS.Lazy.ByteString+  , c BS.Short.ShortByteString+  , c Text.Strict.Text+  , c Text.Lazy.Text++  -- Aeson++  , c Value++  -- Reference cells++  , c SomeSTRef+  , c SomeTVar+  , c SomeMVar++  -- Functions++  , c SomeFun++  -- Containers with no type arguments++  , c IntSet+  , c SomePrimArrayM+  , c SomeStorableVector+  , c SomeStorableVectorM+  , c SomePrimitiveVector+  , c SomePrimitiveVectorM+  )++primSatisfies :: forall c.+     PrimSatisfies c+  => (forall a. PrimClassifier a -> Dict c a)+primSatisfies = go+  where+    go :: PrimClassifier a -> Dict c a++    -- Primitive types++    go C_Bool     = Dict+    go C_Char     = Dict+    go C_Double   = Dict+    go C_Float    = Dict+    go C_Int      = Dict+    go C_Int16    = Dict+    go C_Int8     = Dict+    go C_Int32    = Dict+    go C_Int64    = Dict+    go C_Integer  = Dict+    go C_Ordering = Dict+    go C_Unit     = Dict+    go C_Word     = Dict+    go C_Word8    = Dict+    go C_Word16   = Dict+    go C_Word32   = Dict+    go C_Word64   = Dict++    -- String types++    go C_String      = Dict+    go C_BS_Strict   = Dict+    go C_BS_Lazy     = Dict+    go C_BS_Short    = Dict+    go C_Text_Strict = Dict+    go C_Text_Lazy   = Dict++    -- Aeson++    go C_Value = Dict++    -- Reference cells++    go C_STRef = Dict+    go C_TVar  = Dict+    go C_MVar  = Dict++    -- Functions++    go C_Fun = Dict++    -- Containers with no type arguments++    go C_IntSet            = Dict+    go C_Prim_ArrayM       = Dict+    go C_Vector_Storable   = Dict+    go C_Vector_StorableM  = Dict+    go C_Vector_Primitive  = Dict+    go C_Vector_PrimitiveM = Dict++{-------------------------------------------------------------------------------+  Compound++  We can't use a type alias for the constraint here as ghc doesn't like+  quantified constraints in constraint type aliases.+-------------------------------------------------------------------------------}++class (+    PrimSatisfies c+    -- Compound+  , forall a.   (c a)      => c (Maybe a)+  , forall a b. (c a, c b) => c (Either a b)+  , forall a.   (c a)      => c [a]+  , forall a.   (c a)      => c (Ratio a)+  , forall a.   (c a)      => c (Set a)+  , forall a b. (c a, c b) => c (Map a b)+  , forall a.   (c a)      => c (IntMap a)+  , forall a.   (c a)      => c (Seq a)+  , forall a.   (c a)      => c (Tree a)+  , forall a.   (c a)      => c (HashSet a)+  , forall a b. (c a, c b) => c (HashMap a b)+  , forall a.   (c a)      => c (HashMap.Array a)+  , forall a.   (c a)      => c (Prim.Array a)+  , forall a.   (c a)      => c (Vector.Boxed.Vector a)+  , forall xs. (All c xs,  IsValidSize (Length xs)) => c (WrappedTuple xs)+  ) => ClassifiedSatisfies (c :: Type -> Constraint)++instance (+    PrimSatisfies c+    -- Compound+  , forall a.   (c a)      => c (Maybe a)+  , forall a b. (c a, c b) => c (Either a b)+  , forall a.   (c a)      => c [a]+  , forall a.   (c a)      => c (Ratio a)+  , forall a.   (c a)      => c (Set a)+  , forall a b. (c a, c b) => c (Map a b)+  , forall a.   (c a)      => c (IntMap a)+  , forall a.   (c a)      => c (Seq a)+  , forall a.   (c a)      => c (Tree a)+  , forall a.   (c a)      => c (HashSet a)+  , forall a b. (c a, c b) => c (HashMap a b)+  , forall a.   (c a)      => c (HashMap.Array a)+  , forall a.   (c a)      => c (Prim.Array a)+  , forall a.   (c a)      => c (Vector.Boxed.Vector a)+  , forall xs. (All c xs,  IsValidSize (Length xs)) => c (WrappedTuple xs)+  ) => ClassifiedSatisfies (c :: Type -> Constraint)++classifiedSatisfies :: forall c o.+     (ClassifiedSatisfies c, c Void)+  => (forall a. o a -> Dict c a)+  -> (forall a. Classifier_ o a -> Dict c a)+classifiedSatisfies otherSatisfies = go+  where+    go :: Classifier_ o a -> Dict c a+    go (C_Prim  c) = primSatisfies  c+    go (C_Other c) = otherSatisfies c++    -- Compound+    go (C_Maybe        c) = goElems c $ Dict+    go (C_Either       c) = goElems c $ Dict+    go (C_List         c) = goElems c $ Dict+    go (C_Ratio        c) = goElems c $ Dict+    go (C_Set          c) = goElems c $ Dict+    go (C_Map          c) = goElems c $ Dict+    go (C_IntMap       c) = goElems c $ Dict+    go (C_Sequence     c) = goElems c $ Dict+    go (C_Tree         c) = goElems c $ Dict+    go (C_HashSet      c) = goElems c $ Dict+    go (C_HashMap      c) = goElems c $ Dict+    go (C_HM_Array     c) = goElems c $ Dict+    go (C_Prim_Array   c) = goElems c $ Dict+    go (C_Vector_Boxed c) = goElems c $ Dict+    go (C_Tuple        c) = goElems c $ Dict++    goElems :: SListI as => Elems o as -> (All c as => r) -> r+    goElems (Elems cs) k = case all_NP (hmap goElem cs) of Dict -> k++    goElem :: Elem o a -> Dict c a+    goElem (Elem c) = go c+    goElem NoElem   = Dict
src/Debug/RecoverRTTI/Modules.hs view
@@ -1,6 +1,7 @@ {-# LANGUAGE DataKinds      #-} {-# LANGUAGE GADTs          #-} {-# LANGUAGE KindSignatures #-}+{-# LANGUAGE LambdaCase     #-} {-# LANGUAGE NamedFieldPuns #-} {-# LANGUAGE TypeFamilies   #-} @@ -34,6 +35,7 @@   | PkgAeson   | PkgUnorderedContainers   | PkgVector+  | PkgPrimitive  data family KnownModule (pkg :: KnownPkg) @@ -52,6 +54,7 @@   SAeson               :: SPkg 'PkgAeson   SUnorderedContainers :: SPkg 'PkgUnorderedContainers   SVector              :: SPkg 'PkgVector+  SPrimitive           :: SPkg 'PkgPrimitive  class IsKnownPkg pkg where   singPkg :: SPkg pkg@@ -66,6 +69,7 @@ instance IsKnownPkg 'PkgAeson               where singPkg = SAeson instance IsKnownPkg 'PkgUnorderedContainers where singPkg = SUnorderedContainers instance IsKnownPkg 'PkgVector              where singPkg = SVector+instance IsKnownPkg 'PkgPrimitive           where singPkg = SPrimitive  {-------------------------------------------------------------------------------   Modules in @ghc-pri@@@ -152,9 +156,20 @@ -------------------------------------------------------------------------------}  data instance KnownModule 'PkgVector =-   DataVector+    DataVector+  | DataVectorStorable+  | DataVectorStorableMutable+  | DataVectorPrimitive+  | DataVectorPrimitiveMutable  {-------------------------------------------------------------------------------+  Modules in @primitive@+-------------------------------------------------------------------------------}++data instance KnownModule 'PkgPrimitive =+    DataPrimitiveArray++{-------------------------------------------------------------------------------   Matching -------------------------------------------------------------------------------} @@ -172,7 +187,8 @@   where     go :: SPkg pkg -> KnownModule pkg -> FlatClosure -> Maybe (String, [Box])     go knownPkg knownModl ConstrClosure{pkg, modl, name, ptrArgs} = do-        guard (namePkg knownPkg `isPrefixOf` pkg) -- ignore the version number+        -- We ignore the package version for now+        guard (stripVowels (namePkg knownPkg) `isPrefixOf` stripVowels pkg)         guard (modl == nameModl knownPkg knownModl)         return (name, ptrArgs)     go _ _ _otherClosure = Nothing@@ -188,32 +204,66 @@     namePkg SAeson               = "aeson"     namePkg SUnorderedContainers = "unordered-containers"     namePkg SVector              = "vector"+    namePkg SPrimitive           = "primitive"      nameModl :: SPkg pkg -> KnownModule pkg -> String-    nameModl SGhcPrim             GhcTypes                    = "GHC.Types"-    nameModl SGhcPrim             GhcTuple                    = "GHC.Tuple"-    nameModl SBase                GhcInt                      = "GHC.Int"-    nameModl SBase                GhcWord                     = "GHC.Word"-    nameModl SBase                GhcSTRef                    = "GHC.STRef"-    nameModl SBase                GhcMVar                     = "GHC.MVar"-    nameModl SBase                GhcConcSync                 = "GHC.Conc.Sync"-    nameModl SBase                GhcMaybe                    = "GHC.Maybe"-    nameModl SBase                GhcReal                     = "GHC.Real"-    nameModl SBase                DataEither                  = "Data.Either"-    nameModl SByteString          DataByteStringInternal      = "Data.ByteString.Internal"-    nameModl SByteString          DataByteStringLazyInternal  = "Data.ByteString.Lazy.Internal"-    nameModl SByteString          DataByteStringShortInternal = "Data.ByteString.Short.Internal"-    nameModl SText                DataTextInternal            = "Data.Text.Internal"-    nameModl SText                DataTextInternalLazy        = "Data.Text.Internal.Lazy"-    nameModl SIntegerWiredIn      GhcIntegerType              = "GHC.Integer.Type"-    nameModl SGhcBignum           GhcNumInteger               = "GHC.Num.Integer"-    nameModl SContainers          DataSetInternal             = "Data.Set.Internal"-    nameModl SContainers          DataMapInternal             = "Data.Map.Internal"-    nameModl SContainers          DataIntSetInternal          = "Data.IntSet.Internal"-    nameModl SContainers          DataIntMapInternal          = "Data.IntMap.Internal"-    nameModl SContainers          DataSequenceInternal        = "Data.Sequence.Internal"-    nameModl SContainers          DataTree                    = "Data.Tree"-    nameModl SAeson               DataAesonTypesInternal      = "Data.Aeson.Types.Internal"-    nameModl SUnorderedContainers DataHashMapInternal         = "Data.HashMap.Internal"-    nameModl SUnorderedContainers DataHashMapInternalArray    = "Data.HashMap.Internal.Array"-    nameModl SVector              DataVector                  = "Data.Vector"+    nameModl = \case+        SGhcPrim -> \case+          GhcTypes -> "GHC.Types"+          GhcTuple -> "GHC.Tuple"++        SBase -> \case+          GhcInt      -> "GHC.Int"+          GhcWord     -> "GHC.Word"+          GhcSTRef    -> "GHC.STRef"+          GhcMVar     -> "GHC.MVar"+          GhcConcSync -> "GHC.Conc.Sync"+          GhcMaybe    -> "GHC.Maybe"+          GhcReal     -> "GHC.Real"+          DataEither  -> "Data.Either"++        SByteString -> \case+          DataByteStringInternal      -> "Data.ByteString.Internal"+          DataByteStringLazyInternal  -> "Data.ByteString.Lazy.Internal"+          DataByteStringShortInternal -> "Data.ByteString.Short.Internal"++        SText -> \case+          DataTextInternal     -> "Data.Text.Internal"+          DataTextInternalLazy -> "Data.Text.Internal.Lazy"++        SIntegerWiredIn -> \case+          GhcIntegerType -> "GHC.Integer.Type"++        SGhcBignum -> \case+          GhcNumInteger -> "GHC.Num.Integer"++        SContainers -> \case+          DataSetInternal      -> "Data.Set.Internal"+          DataMapInternal      -> "Data.Map.Internal"+          DataIntSetInternal   -> "Data.IntSet.Internal"+          DataIntMapInternal   -> "Data.IntMap.Internal"+          DataSequenceInternal -> "Data.Sequence.Internal"+          DataTree             -> "Data.Tree"++        SAeson -> \case+          DataAesonTypesInternal -> "Data.Aeson.Types.Internal"++        SUnorderedContainers -> \case+          DataHashMapInternal      -> "Data.HashMap.Internal"+          DataHashMapInternalArray -> "Data.HashMap.Internal.Array"++        SVector -> \case+          DataVector                 -> "Data.Vector"+          DataVectorStorable         -> "Data.Vector.Storable"+          DataVectorStorableMutable  -> "Data.Vector.Storable.Mutable"+          DataVectorPrimitive        -> "Data.Vector.Primitive"+          DataVectorPrimitiveMutable -> "Data.Vector.Primitive.Mutable"++        SPrimitive -> \case+          DataPrimitiveArray -> "Data.Primitive.Array"++    -- On OSX, cabal strips vowels from package IDs in order to work around+    -- limitations around path lengths+    -- <https://github.com/haskell/cabal/blob/3f397c0c661facd0be9c5c67ad26f66a87725472/cabal-install/src/Distribution/Client/PackageHash.hs#L125-L157>+    stripVowels :: String -> String+    stripVowels = filter (`notElem` "aeoiu")
+ src/Debug/RecoverRTTI/Reclassify.hs view
@@ -0,0 +1,148 @@+{-# LANGUAGE DataKinds           #-}+{-# LANGUAGE FlexibleContexts    #-}+{-# LANGUAGE GADTs               #-}+{-# LANGUAGE KindSignatures      #-}+{-# LANGUAGE LambdaCase          #-}+{-# LANGUAGE RankNTypes          #-}+{-# LANGUAGE ScopedTypeVariables #-}++-- | Support for reclassification+module Debug.RecoverRTTI.Reclassify (+    Reclassified(..)+  , reclassify_+  , distribReclassified+  , FromUsr(..)+  , coerceFromUsr+  ) where++import Data.Kind+import Data.SOP hiding (NS(..))+import Data.Void+import Unsafe.Coerce (unsafeCoerce)++import Debug.RecoverRTTI.Classifier+import Debug.RecoverRTTI.Tuple+import Debug.RecoverRTTI.Nat+import Debug.RecoverRTTI.Wrappers++-- | Reclassified values+--+-- Reclassification can be done by user code which want to take advantage of+-- the classification infrastructure for @recover-rtti@ but add some additional+-- classification for domain-specific types known only to that client code.+--+-- When we reclassify a value, a value that might previously be classified as+-- @UserDefined@ may now be classified as some concrete type; therefore we+-- compute a classifier for a potentially /different/ type along with+-- evidence that we can coerce between the two.+data Reclassified o a where+  Reclassified :: o b -> FromUsr a b -> Reclassified o a++-- | Extension of 'Reclassified' to multiple elems+--+-- This is used internally only.+data ReclassifiedElems o as where+  RElems ::+       (SListI bs, Length bs ~ Length as)+    => Elems o bs -> PairWise FromUsr as bs -> ReclassifiedElems o as++reclassify_ :: forall m o o'. Applicative m+  => (forall a. o a -> m (Reclassified o' a))+  -> (forall a. Classifier_ o a -> m (Classifier_ (Reclassified o') a))+reclassify_ = mapClassifier++-- | Lift 'Reclassified' to the top-level+--+-- Given a classifier with user-defined classifiers at the levels, along with+-- coercion functions, leave the user-defined classifiers in place but lift the+-- coercion function to the top-level.+distribReclassified :: forall o.+     (forall a. Classifier_ (Reclassified o) a -> Reclassified (Classifier_ o) a)+distribReclassified = go+  where+    go :: forall a. Classifier_ (Reclassified o) a -> Reclassified (Classifier_ o) a+    -- Primitive and user-defined+    go (C_Prim  c) = Reclassified (C_Prim c) Id+    go (C_Other c) = case c of Reclassified c' f -> Reclassified (C_Other c') f++    -- Compound+    go (C_Maybe        c) = go1 C_Maybe        c+    go (C_Either       c) = go2 C_Either       c+    go (C_List         c) = go1 C_List         c+    go (C_Ratio        c) = go1 C_Ratio        c+    go (C_Set          c) = go1 C_Set          c+    go (C_Map          c) = go2 C_Map          c+    go (C_IntMap       c) = go1 C_IntMap       c+    go (C_Sequence     c) = go1 C_Sequence     c+    go (C_Tree         c) = go1 C_Tree         c+    go (C_HashSet      c) = go1 C_HashSet      c+    go (C_HashMap      c) = go2 C_HashMap      c+    go (C_HM_Array     c) = go1 C_HM_Array     c+    go (C_Prim_Array   c) = go1 C_Prim_Array   c+    go (C_Vector_Boxed c) = go1 C_Vector_Boxed c+    go (C_Tuple        c) = goN C_Tuple        c++    go1 :: forall f a.+         (forall a'. Elems o '[a'] -> Classifier_ o (f a'))+      -> Elems (Reclassified o) '[a]+      -> Reclassified (Classifier_ o) (f a)+    go1 cf c =+        case distribElems c of+          RElems c' (PCons f PNil) -> Reclassified (cf c') (F1 f)++    go2 :: forall f a b.+         (forall a' b'. Elems o '[a', b'] -> Classifier_ o (f a' b'))+      -> Elems (Reclassified o) '[a, b]+      -> Reclassified (Classifier_ o) (f a b)+    go2 cf c =+        case distribElems c of+          RElems c' (PCons f (PCons f' PNil)) -> Reclassified (cf c') (F2 f f')++    goN :: forall f as.+         SListI as+      => (forall as'.+               (SListI as', Length as' ~ Length as)+            => Elems o as' -> Classifier_ o (f as'))+      -> Elems (Reclassified o) as+      -> Reclassified (Classifier_ o) (f as)+    goN cf c =+        case distribElems c of+          RElems c' fs -> Reclassified (cf c') (FN fs)++distribElem :: Elem (Reclassified o) a -> Reclassified (Elem o) a+distribElem = \case+    NoElem -> Reclassified NoElem Absurd+    Elem c -> case distribReclassified c of+                Reclassified c' f -> Reclassified (Elem c') f++distribElems ::+     SListI xs+  => Elems (Reclassified o) xs -> ReclassifiedElems o xs+distribElems = \(Elems cs) -> go $ hmap distribElem cs+  where+    go :: NP (Reclassified (Elem o)) xs -> ReclassifiedElems o xs+    go Nil                      = RElems (Elems Nil) PNil+    go (Reclassified c f :* cs) = case go cs of+                                    RElems (Elems cs') fs' ->+                                      RElems (Elems (c :* cs')) (PCons f fs')++{-------------------------------------------------------------------------------+  Evidence that we are only doing conversions from Any+-------------------------------------------------------------------------------}++-- | Evidence that we can convert between two types+--+-- The only actual conversion we ever do is from 'UserDefined' (aka 'Any') to+-- whatever type the reclassification gives.+data FromUsr :: Type -> Type -> Type where+  Id      :: FromUsr a a+  Absurd  :: FromUsr Void a+  FromUsr :: FromUsr UserDefined a+  F1      :: FromUsr a1 b1 -> FromUsr (f a1) (f b1)+  F2      :: FromUsr a1 b1 -> FromUsr a2 b2 -> FromUsr (f a1 a2) (f b1 b2)+  FN      :: PairWise FromUsr as bs -> FromUsr (f as) (f bs)+  Compose :: FromUsr b c -> FromUsr a b -> FromUsr a c++-- | Coerce, given some evidence that the coercion is sound.+coerceFromUsr :: FromUsr a b -> a -> b+coerceFromUsr = unsafeCoerce
src/Debug/RecoverRTTI/Tuple.hs view
@@ -11,9 +11,13 @@ module Debug.RecoverRTTI.Tuple (     -- * Wrapped tuple     WrappedTuple(WrappedTuple, TNil, TCons)+  , unwrapTuple     -- * Conversion between tuples and NP   , tupleFromNP   , tupleToNP+    -- * Mapping+  , PairWise(..)+  , mapTuple     -- * Re-exports   , module Debug.RecoverRTTI.Tuple.Recursive   , module Debug.RecoverRTTI.Tuple.Size@@ -38,7 +42,7 @@ -- Inductive view on tuples that can be constructed with or pattern matched on -- using 'TNil' and 'TCons'. The underlying representation is a /true/ tuple -- however; for example, @Tuple '[Int, Bool, Char] ~ (Int, Bool, Char)@.-newtype WrappedTuple xs = WrappedTuple (Tuple xs)+newtype WrappedTuple xs = WrappedTuple { unwrapTuple :: Tuple xs }  pattern TNil ::      forall xs. (SListI xs, IsValidSize (Length xs))@@ -74,6 +78,31 @@   => WrappedTuple xs -> NP I xs tupleToNP TNil         = Nil tupleToNP (TCons x xs) = I x :* tupleToNP xs++{-------------------------------------------------------------------------------+  Mapping+-------------------------------------------------------------------------------}++data PairWise f xs ys where+  PNil  :: PairWise f '[] '[]+  PCons :: f x y -> PairWise f xs ys -> PairWise f (x:xs) (y:ys)++data SameListShape xs ys where+  SameListShape :: (SListI ys, Length xs ~ Length ys) => SameListShape xs ys++mapTuple' ::+     (SListI xs, IsValidSize (Length xs))+  => PairWise (->) xs ys+  -> WrappedTuple xs -> (SameListShape xs ys, WrappedTuple ys)+mapTuple' PNil          TNil        = (SameListShape, TNil)+mapTuple' (PCons f fs) (TCons x xs) =+    case mapTuple' fs xs of+      (SameListShape, ys) -> (SameListShape, TCons (f x) ys)++mapTuple ::+     (SListI xs, IsValidSize (Length xs))+  => PairWise (->) xs ys -> WrappedTuple xs -> WrappedTuple ys+mapTuple fs = snd . mapTuple' fs  {-------------------------------------------------------------------------------   Internal auxiliary functions for defining the pattern synonym
src/Debug/RecoverRTTI/Util.hs view
@@ -2,20 +2,15 @@ {-# LANGUAGE DataKinds           #-} {-# LANGUAGE FlexibleContexts    #-} {-# LANGUAGE GADTs               #-}-{-# LANGUAGE KindSignatures      #-} {-# LANGUAGE LambdaCase          #-} {-# LANGUAGE PolyKinds           #-} {-# LANGUAGE RankNTypes          #-} {-# LANGUAGE ScopedTypeVariables #-} -{-# OPTIONS_GHC -Wno-redundant-constraints #-}- module Debug.RecoverRTTI.Util (     -- * Existentials     Some(..)-  , elimKnownSymbol-    -- * Constraints-  , keepRedundantConstraint+  , mapSome     -- * Lists   , dropEnds     -- * SOP@@ -24,9 +19,7 @@   ) where  import Data.Kind-import Data.Proxy import Data.SOP-import GHC.TypeLits (KnownSymbol, SomeSymbol(..), someSymbolVal)  import Debug.RecoverRTTI.Nat @@ -37,24 +30,8 @@ data Some (f :: k -> Type) where   Some :: forall f a. f a -> Some f -elimKnownSymbol :: String -> (forall n. KnownSymbol n => Proxy n -> r) -> r-elimKnownSymbol s k =-    case someSymbolVal s of-      SomeSymbol p -> k p--{--------------------------------------------------------------------------------  Constraints--------------------------------------------------------------------------------}---- | Can be used to silence individual "redundant constraint" warnings------ > foo :: ConstraintUsefulForDebugging => ...--- > foo =--- >     ..--- >   where--- >     _ = keepRedundantConstraint (Proxy @ConstraintUsefulForDebugging))-keepRedundantConstraint :: c => proxy c -> ()-keepRedundantConstraint _ = ()+mapSome :: (forall x. f x -> g x) -> Some f -> Some g+mapSome f (Some x) = Some (f x)  {-------------------------------------------------------------------------------   Lists
src/Debug/RecoverRTTI/Wrappers.hs view
@@ -20,6 +20,12 @@   , SomeSTRef(..)   , SomeMVar(..)   , SomeTVar(..)+    -- * Arrays+  , SomePrimArrayM(..)+  , SomeStorableVector(..)+  , SomeStorableVectorM(..)+  , SomePrimitiveVector(..)+  , SomePrimitiveVectorM(..)   ) where  import Control.Concurrent.MVar (MVar)@@ -27,6 +33,8 @@ import Data.STRef (STRef) import GHC.Exts +import qualified Data.Primitive.Array as Prim (MutableArray)+ {-------------------------------------------------------------------------------   User-defined types -------------------------------------------------------------------------------}@@ -64,10 +72,50 @@   deriving (Eq)  {-------------------------------------------------------------------------------+  Arrays++  For mutable arrays, we don't currently peek inside, and so we don't infer+  the type of the elements. We /could/ (in principle this would be sound),+  and we may wish to change this at some point. For the purposes of /show/,+  however, having this type inferred it's particularly useful, unless we define+  a `peekAnythingToString :: a -> IO String` function or something like that+  which could look inside mutable structures (references, arrays, ..).+-------------------------------------------------------------------------------}++newtype SomePrimArrayM = SomePrimArrayM (Prim.MutableArray RealWorld Any)++-- | Storable vector ("Data.Vector.Storable")+--+-- For storable arrays we have no hope of inferring the type of the elements:+-- the elements are not stored as pointers, but rather as " serialized " data+-- through the 'Storable' type class. In order to get at any element, we'd need+-- to have the corresponding 'Storable' instance, but of course we don't have it+-- if we don't have the type.+newtype SomeStorableVector = SomeStorableVector Any++-- | Mutable storage vector ("Data.Vector.Storable")+--+-- See 'SomeStorableVector' for some details on why we don't infer anything here.+newtype SomeStorableVectorM = SomeStorableVectorM Any++-- | Primitive vector ("Data.Vector.Primitive")+--+-- See 'SomeStorableVector' for why we can't classify elements of these vectors.+newtype SomePrimitiveVector = SomePrimitiveVector Any++-- | Mutable primitive vector+newtype SomePrimitiveVectorM = SomePrimitiveVectorM Any++{-------------------------------------------------------------------------------   Show instances    Unfortunately reference cells are moved by GC, so we can't do much here;   showing the address of the variable isn't particularly helpful.++  We /could/ use @unsafePerformIO@ here to display the /contents/ of these+  variables and mutable arrays, but that would not be referentially transparent;+  unlike 'classify', that would not be morally pure. In principle we could offer+  such a "display and peek inside mutable references" as a separate function. -------------------------------------------------------------------------------}  instance Show SomeSTRef where@@ -81,3 +129,18 @@  instance Show SomeFun where   show _ = "<Fun>"++instance Show SomePrimArrayM where+  show _ = "<Data.Primitive.Array.MutableArray>"++instance Show SomeStorableVector where+  show _ = "<Data.Vector.Storable.Vector>"++instance Show SomeStorableVectorM where+  show _ = "<Data.Vector.Storable.MVector>"++instance Show SomePrimitiveVector where+  show _ = "<Data.Vector.Primitive.Vector>"++instance Show SomePrimitiveVectorM where+  show _ = "<Data.Vector.Primitive.MVector>"
− tests/Test/RecoverRTTI/Arbitrary.hs
@@ -1,648 +0,0 @@-{-# LANGUAGE ConstraintKinds       #-}-{-# LANGUAGE DataKinds             #-}-{-# LANGUAGE DeriveFunctor         #-}-{-# LANGUAGE FlexibleContexts      #-}-{-# LANGUAGE FlexibleInstances     #-}-{-# LANGUAGE GADTs                 #-}-{-# LANGUAGE KindSignatures        #-}-{-# LANGUAGE LambdaCase            #-}-{-# LANGUAGE OverloadedStrings     #-}-{-# LANGUAGE QuantifiedConstraints #-}-{-# LANGUAGE RankNTypes            #-}-{-# LANGUAGE ScopedTypeVariables   #-}--module Test.RecoverRTTI.Arbitrary (-    ClassifiedGen(..)-  , arbitraryClassifiedGen-    -- * Example values of reference cells-  , exampleIORef-  , exampleSTRef-  , exampleMVar-  , exampleTVar-  ) where--import Control.Concurrent.MVar (newEmptyMVar)-import Control.Concurrent.STM (newTVarIO)-import Control.Monad-import Control.Monad.ST.Unsafe (unsafeSTToIO)-import Data.Bifunctor-import Data.IORef (newIORef)-import Data.Maybe (catMaybes)-import Data.SOP-import Data.SOP.Dict-import Data.STRef (newSTRef)-import Data.Tree (Tree)-import Data.Void-import GHC.Real-import System.IO.Unsafe (unsafePerformIO)-import Unsafe.Coerce (unsafeCoerce)--import qualified Data.Aeson                  as Aeson-import qualified Data.ByteString             as BS.Strict-import qualified Data.ByteString.Lazy        as BS.Lazy-import qualified Data.ByteString.Short       as BS.Short-import qualified Data.HashMap.Internal.Array as HashMap.Array-import qualified Data.HashMap.Lazy           as HashMap-import qualified Data.HashSet                as HashSet-import qualified Data.IntMap                 as IntMap-import qualified Data.Map                    as Map-import qualified Data.Sequence               as Seq-import qualified Data.Set                    as Set-import qualified Data.Text                   as Text.Strict-import qualified Data.Text.Lazy              as Text.Lazy-import qualified Data.Tree                   as Tree-import qualified Data.Vector                 as Vector.Boxed--import Test.QuickCheck hiding (classify, NonEmpty)--import Debug.RecoverRTTI--import Test.RecoverRTTI.ConcreteClassifier-import Test.RecoverRTTI.Orphans ()-import Test.RecoverRTTI.UserDefined--{--------------------------------------------------------------------------------  Generic auxiliary--------------------------------------------------------------------------------}--newtype SizedGen a = SizedGen (Int -> Gen a)-  deriving (Functor)--runSized :: Int -> SizedGen a -> Gen a-runSized n (SizedGen gen) = gen n--ignoreSize :: Gen a -> SizedGen a-ignoreSize gen = SizedGen $ \_sz -> gen--arbitrarySizedGen :: Arbitrary a => SizedGen a-arbitrarySizedGen = ignoreSize arbitrary--{--------------------------------------------------------------------------------  Arbitrary instance--------------------------------------------------------------------------------}---- | Quickcheck generator along with a classifier-data ClassifiedGen a where-  ClassifiedGen ::-       (Show a, Eq a)-    => { -- | The classifier for the generator-         genClassifier :: ConcreteClassifier a--         -- | The classified generator itself-         ---         -- The size argument determines the maximum size of the /value/-         -- (as opposed to the maximum size of the /type/)-       , classifiedGen :: SizedGen a-       }-    -> ClassifiedGen a--canShowClassifiedGen :: ClassifiedGen a -> Dict Show a-canShowClassifiedGen ClassifiedGen{} = Dict--canEqClassifiedGen :: ClassifiedGen a -> Dict Eq a-canEqClassifiedGen ClassifiedGen{} = Dict--defaultClassifiedGen ::-     (Arbitrary a, Show a, Eq a)-  => ConcreteClassifier a-  -> ClassifiedGen a-defaultClassifiedGen cc = ClassifiedGen cc $ ignoreSize arbitrary---- | Generated arbitrary classifier along with a generator for that value------ NOTE: The @sz@ parameter limits the size of the /type tree/ (i.e., the number--- of recursive calls to arbitraryClassifiedGen), /not/ the size of the--- generated /values/.-arbitraryClassifiedGen :: Int -> Gen (Some ClassifiedGen)-arbitraryClassifiedGen typSz-  | typSz <  0 = error "arbitraryClassifiedGen: uhoh.. bug"-  | typSz == 0 = elements leaves-  | otherwise  = oneof (elements leaves : catMaybes compound)-  where-    -- Leaves of the tree (values with no recursion)-    ---    -- Since there are the leaves, we don't need to check the size-    leaves :: [Some ClassifiedGen]-    leaves = concat [-          -- Primitive types-          [ Some $ defaultClassifiedGen CC_Bool-          , Some $ defaultClassifiedGen CC_Char-          , Some $ defaultClassifiedGen CC_Double-          , Some $ defaultClassifiedGen CC_Float-          , Some $ defaultClassifiedGen CC_Int-          , Some $ defaultClassifiedGen CC_Int16-          , Some $ defaultClassifiedGen CC_Int8-          , Some $ defaultClassifiedGen CC_Int32-          , Some $ defaultClassifiedGen CC_Int64-          , Some $ defaultClassifiedGen CC_Integer-          , Some $ defaultClassifiedGen CC_Ordering-          , Some $ defaultClassifiedGen CC_Unit-          , Some $ defaultClassifiedGen CC_Word-          , Some $ defaultClassifiedGen CC_Word8-          , Some $ defaultClassifiedGen CC_Word16-          , Some $ defaultClassifiedGen CC_Word32-          , Some $ defaultClassifiedGen CC_Word64-         ]--          -- Strings-          ---          -- Avoid generating the empty string (recognized as @[Void]@)-        , let mapList :: Arbitrary a => Int -> ([a] -> b) -> SizedGen b-              mapList minSize f = SizedGen $ \valSz -> do-                  n <- choose (minSize, max minSize valSz) -- maybe valSz == 0-                  f <$> vector n-          in [-             Some $ ClassifiedGen CC_String      (mapList 1 id)-           , Some $ ClassifiedGen CC_BS_Strict   (mapList 0 BS.Strict.pack)-           , Some $ ClassifiedGen CC_BS_Lazy     (mapList 0 BS.Lazy.pack)-           , Some $ ClassifiedGen CC_BS_Short    (mapList 0 BS.Short.pack)-           , Some $ ClassifiedGen CC_Text_Strict (mapList 0 Text.Strict.pack)-           , Some $ ClassifiedGen CC_Text_Lazy   (mapList 0 Text.Lazy.pack)-          ]--          -- Aeson-        , [ Some $ ClassifiedGen CC_Value arbitraryAesonValue ]--          -- Reference cells-        , [ Some $ ClassifiedGen CC_STRef (ignoreSize $ pure exampleSTRef)-          , Some $ ClassifiedGen CC_STRef (ignoreSize $ pure exampleIORef)-          , Some $ ClassifiedGen CC_MVar  (ignoreSize $ pure exampleMVar)-          , Some $ ClassifiedGen CC_TVar  (ignoreSize $ pure exampleTVar)-          ]--          -- Functions-          ---          -- For functions we don't currently try to be clever and /generate/-          -- functions. Instead, we just try a few different categories.-        , map (\f -> Some $ ClassifiedGen CC_Fun (ignoreSize $ pure f)) [-              -- Parametrically polymorphic function-              unsafeCoerce (id    :: Int -> Int)-            , unsafeCoerce (const :: Int -> Bool -> Int)-              -- Ad-hoc polymorphic function-            , unsafeCoerce (negate :: Int -> Int)-            , unsafeCoerce ((+)    :: Int -> Int -> Int)-              -- Partial application-            , unsafeCoerce (const 1 :: Bool -> Int)-            , unsafeCoerce ((+)   1 :: Int -> Int)-            ]-        ]--    -- Compound-    ---    -- These are only used if @sz > 0@.-    compound :: [Maybe (Gen (Some ClassifiedGen))]-    compound = [-          -- Lists-          ---          -- We have to be careful not to generate @[Char]@, because this is-          -- inferred as @String@-          guard (typSz >= 1) >> (return $ do-              Some a <- arbitraryClassifiedGen (typSz - 1)-              genMaybeF-                (\case FJust CC_Char -> CC_String-                       c             -> CC_List c)-                (return [])-                (genListLike id)-                a-            )--          -- Maybe-        , guard (typSz >= 1) >> (return $ do-              Some a <- arbitraryClassifiedGen (typSz - 1)-              genMaybeF CC_Maybe (return Nothing) (fmap Just) a-            )--          -- Either-        , guard (typSz >= 2) >> (return $ do-              Some a <- arbitraryClassifiedGen (typSz `div` 2)-              Some b <- arbitraryClassifiedGen (typSz `div` 2)-              genEitherF CC_Either (fmap Left) (fmap Right) a b-            )--          -- Ratio-        , guard (typSz >= 1) >> (return $ do-              Some a <- arbitraryClassifiedGen (typSz `div` 2)-              genF-                CC_Ratio-                (\(SizedGen gen) -> SizedGen $ \sz ->-                   (:%) <$> gen (sz `div` 2) <*> gen (sz `div` 2)-                )-                a-            )--          -- Set-          -- For set we must pick an ordered type, so we just pick Int-        , return (do-              genMaybeF-                CC_Set-                (return Set.empty)-                (genListLike Set.fromList)-                (defaultClassifiedGen CC_Int)-            )--          -- Map-          -- Pick Int for the keys, but randomly for the values-        , guard (typSz >= 1) >> (return $ do-              Some b <- arbitraryClassifiedGen (typSz - 1)-              genMaybePairF-                CC_Map-                (return Map.empty)-                (genMapLike Map.fromList)-                (defaultClassifiedGen CC_Int)-                b-            )--          -- IntSet-        , return $ return $ Some (defaultClassifiedGen CC_IntSet)--          -- IntMap-        , guard (typSz >= 1) >> (return $ do-              Some b <- arbitraryClassifiedGen (typSz - 1)-              genMaybeF-                CC_IntMap-                (return IntMap.empty)-                (genMapLike IntMap.fromList arbitrarySizedGen)-                b-            )--          -- Sequence-        , guard (typSz >= 1) >> (return $ do-              Some a <- arbitraryClassifiedGen (typSz - 1)-              genMaybeF-                CC_Sequence-                (return Seq.empty)-                (genListLike Seq.fromList)-                a-           )--          -- Tree-        , guard (typSz >= 1) >> (return $ do-              Some a <- arbitraryClassifiedGen (typSz - 1)-              genF CC_Tree (genListLike mkSomeTree) a-            )--          -- HashSet-          -- Like Set, we need an Ord instance on the elements, so we pick Int-          -- genListLike never generates the empty list, which is important:-          -- an empty 'HashSet' would be misclassified as a 'HashMap'.-        , (return $-             genF-               CC_HashSet-               (genListLike HashSet.fromList)-               (defaultClassifiedGen CC_Int)-           )--          -- HashMap-        , guard (typSz >= 1) >> (return $ do-              -- A map with @()@ values is classified as a @HashSet@-              let isUnit :: Some ClassifiedGen -> Bool-                  isUnit (Some (ClassifiedGen CC_Unit _)) = True-                  isUnit _otherwise = False-              Some b <- arbitraryClassifiedGen (typSz - 1) `suchThat` (not . isUnit)-              genMaybePairF-                CC_HashMap-                (return HashMap.empty)-                (genMapLike HashMap.fromList)-                (defaultClassifiedGen CC_Int)-                b-            )--          -- HashMap's internal array type-        , guard (typSz >= 1) >> (return $ do-              let mkArray xs = HashMap.Array.fromList (length xs) xs-              Some a <- arbitraryClassifiedGen (typSz - 1)-              genMaybeF-                CC_HM_Array-                (return $ mkArray [])-                (genListLike mkArray)-                a-            )--          -- Boxed vectors-        , guard (typSz >= 1) >> (return $ do-              Some a <- arbitraryClassifiedGen (typSz - 1)-              genMaybeF-                CC_Vector_Boxed-                (return Vector.Boxed.empty)-                (genListLike Vector.Boxed.fromList)-                a-            )--          ---          -- User-defined-          ----          -- SimpleType-        , return $ return $ Some (defaultClassifiedGen CC_User_Simple)--          -- NonRecursive-        , guard (typSz >= 1) >> (return $ do-              Some a <- arbitraryClassifiedGen (typSz - 1)-              genMaybeF-                CC_User_NonRec-                (NR1 <$> arbitrary)-                (\gen -> SizedGen $ \valSz ->-                    NR2 <$> runSized valSz gen <*> arbitrary-                )-                a-            )--          -- Recursive-        , guard (typSz >= 1) >> (return $ do-              Some a <- arbitraryClassifiedGen (typSz - 1)-              genMaybeF-                CC_User_Rec-                (return RNil)-                (genListLike recursiveFromList)-                a-            )--          -- ContainsUnlifted-        , return $ return $ Some (defaultClassifiedGen CC_User_Unlifted)--          ---          -- Tuples-          ----        , guard (typSz >= 2) >> (return $-              arbitraryTuple typSz $ \np ->-              case ( all_NP (hmap canShowClassifiedGen np)-                   , all_NP (hmap canEqClassifiedGen   np)-                   ) of-                (Dict, Dict) ->-                  return . Some $ ClassifiedGen {-                      genClassifier =-                        CC_Tuple (ConcreteClassifiers (hmap genClassifier np))-                    , classifiedGen = SizedGen $ \valSz -> do-                        let valSz' = valSz `div` lengthSList np-                        tupleFromNP <$>-                          hsequence(hmap (runSized valSz' . classifiedGen) np)-                    }-            )-        ]--    -- We check that we cover all cases of 'Classifier' rather than-    -- 'ConcreteClassifier': it is important that we generate test cases for-    -- everything we classify in the main library.-    _checkAllCases :: Classifier a -> ()-    _checkAllCases = \case-         -- Primitive types--         C_Bool     -> ()-         C_Char     -> ()-         C_Double   -> ()-         C_Float    -> ()-         C_Int      -> ()-         C_Int16    -> ()-         C_Int8     -> ()-         C_Int32    -> ()-         C_Int64    -> ()-         C_Integer  -> ()-         C_Ordering -> ()-         C_Unit     -> ()-         C_Word     -> ()-         C_Word8    -> ()-         C_Word16   -> ()-         C_Word32   -> ()-         C_Word64   -> ()--         -- String types--         C_String      -> ()-         C_BS_Strict   -> ()-         C_BS_Lazy     -> ()-         C_BS_Short    -> ()-         C_Text_Strict -> ()-         C_Text_Lazy   -> ()--         -- Aeson--         C_Value -> ()--         -- Compound--         C_Maybe{}        -> ()-         C_Either{}       -> ()-         C_List{}         -> ()-         C_Ratio{}        -> ()-         C_Set{}          -> ()-         C_Map{}          -> ()-         C_IntSet{}       -> ()-         C_IntMap{}       -> ()-         C_Tuple{}        -> ()-         C_Sequence{}     -> ()-         C_Tree{}         -> ()-         C_HashSet{}      -> ()-         C_HashMap{}      -> ()-         C_HM_Array{}     -> ()-         C_Vector_Boxed{} -> ()--         -- Reference cells--         C_STRef -> ()-         C_TVar  -> ()-         C_MVar  -> ()--         -- Functions--         C_Fun -> ()--         -- User-defined--         C_Custom{} -> ()---- | Generate arbitrary tuple size-arbitraryTuple :: forall r.-     Int -- ^ Maximum type size (should be at least 2)-  -> (forall xs.-           (SListI xs, IsValidSize (Length xs))-        => NP ClassifiedGen xs -> Gen r-     )-  -> Gen r-arbitraryTuple = \typSz k -> do-    tupleSz <- choose (2, min typSz 62)-    let typSz' = typSz `div` tupleSz-    case toValidSize tupleSz of-      Nothing ->-        error "arbitraryTuple: impossible, this is a valid tuple size"-      Just (Some valid@(ValidSize n _)) ->-        go typSz' n $ \(np :: NP ClassifiedGen xs) ->-           case liftValidSize (valid :: ValidSize (Length xs))-             of Dict -> k np-  where-    go :: Int-       -> SNat n-       -> (forall xs.-                (SListI xs, Length xs ~ n)-             => NP ClassifiedGen xs -> Gen r-          )-       -> Gen r-    go _      SZ     k = k Nil-    go typSz' (SS n) k = do-        Some c <- arbitraryClassifiedGen typSz'-        go typSz' n $ \cs -> k (c :* cs)--instance Arbitrary (Some Value) where-  arbitrary = sized $ \sz -> do-      -- @sz@ will range from 0..100, but we don't want to generate types that-      -- large-      Some (ClassifiedGen cc gen) <- arbitraryClassifiedGen (sz `div` 10)--      -- For the values however we want to be able to generate larger trees-      Some . Value cc <$> runSized sz gen--{--------------------------------------------------------------------------------  Helpers--------------------------------------------------------------------------------}--genListLike :: ([a] -> x) -> SizedGen a -> SizedGen x-genListLike f gen = SizedGen $ \valSz -> do-    n <- choose (1, 5)-    f <$> vectorOf n (runSized (valSz `div` n) gen)--genMapLike :: ([(a, b)] -> x) -> SizedGen a -> SizedGen b -> SizedGen x-genMapLike f (SizedGen genX) (SizedGen genY) = SizedGen $ \valSz -> do-    n <- choose (1, 5)-    f <$> vectorOf n (-        (,) <$> genX (valSz `div` n `div` 2)-            <*> genY (valSz `div` n `div` 2)-      )--genMaybeF ::-     ( forall x. Show x => Show (f x)-     , forall x. Eq   x => Eq   (f x)-     )-  => (forall x. MaybeF ConcreteClassifier x -> ConcreteClassifier (f x))-  -> Gen (f Void)-  -> (SizedGen a -> SizedGen (f a))-  -> ClassifiedGen a -> Gen (Some ClassifiedGen)-genMaybeF cc genNothing genJust (ClassifiedGen cA genA) =-    elements [-        Some $ ClassifiedGen (cc FNothing)   (ignoreSize $ genNothing)-      , Some $ ClassifiedGen (cc (FJust cA)) (genJust genA)-      ]--genEitherF ::-     ( forall x y. (Show x, Show y) => Show (f x y)-     , forall x y. (Eq   x, Eq   y) => Eq   (f x y)-     )-  => (forall x y. EitherF ConcreteClassifier x y -> ConcreteClassifier (f x y))-  -> (SizedGen a -> SizedGen (f a Void))-  -> (SizedGen b -> SizedGen (f Void b))-  -> ClassifiedGen a-  -> ClassifiedGen b-  -> Gen (Some ClassifiedGen)-genEitherF cc genLeft genRight (ClassifiedGen cA genA) (ClassifiedGen cB genB) =-    elements [-        Some $ ClassifiedGen (cc (FLeft  cA)) (genLeft  genA)-      , Some $ ClassifiedGen (cc (FRight cB)) (genRight genB)-      ]--genMaybePairF ::-     ( forall x y. (Show x, Show y) => Show (f x y)-     , forall x y. (Eq   x, Eq   y) => Eq   (f x y)-     )-  => (forall x y. MaybePairF ConcreteClassifier x y -> ConcreteClassifier (f x y))-  -> Gen (f Void Void)-  -> (SizedGen a -> SizedGen b -> SizedGen (f a b))-  -> ClassifiedGen a -> ClassifiedGen b -> Gen (Some ClassifiedGen)-genMaybePairF cc genNothing genJust (ClassifiedGen cA genA) (ClassifiedGen cB genB) =-    elements [-        Some $ ClassifiedGen (cc FNothingPair)      (ignoreSize $ genNothing)-      , Some $ ClassifiedGen (cc (FJustPair cA cB)) (genJust genA genB)-      ]--genF ::-     ( forall x. Show x => Show (f x)-     , forall x. Eq   x => Eq   (f x)-     )-  => (forall x. ConcreteClassifier x -> ConcreteClassifier (f x))-  -> (SizedGen a -> SizedGen (f a))-  -> ClassifiedGen a -> Gen (Some ClassifiedGen)-genF cc gen (ClassifiedGen cA genA) = return $-    Some $ ClassifiedGen (cc cA) (gen genA)--{--------------------------------------------------------------------------------  Auxiliary tree functions--------------------------------------------------------------------------------}--mkSomeTree :: [a] -> Tree a-mkSomeTree []       = error "mkSomeTree: empty"-mkSomeTree [x]      = Tree.Node x []-mkSomeTree [x, y]   = Tree.Node x [Tree.Node y []]-mkSomeTree (x : xs) =-    let (left, right) = split xs-    in Tree.Node x [mkSomeTree left, mkSomeTree right]---- | Split list into halves------ If the input has at least two elements, neither list will be empty------ > split "abcde" == ("ace","bd")-split :: [a] -> ([a], [a])-split []     = ([], [])-split (x:xs) = first (x:) $ splot xs---- | Auxiliary to 'split'-splot :: [a] -> ([a], [a])-splot []     = ([], [])-splot (x:xs) = second (x:) $ split xs--{--------------------------------------------------------------------------------  Auxiliary Aeson--------------------------------------------------------------------------------}--arbitraryAesonValue :: SizedGen Aeson.Value-arbitraryAesonValue = SizedGen $ go-  where-    go :: Int -> Gen Aeson.Value-    go 0  = oneof nonRecursive-    go sz = oneof (nonRecursive ++ recursive sz)--    nonRecursive :: [Gen Aeson.Value]-    nonRecursive = [-          Aeson.String . Text.Strict.pack <$> arbitrary-        , Aeson.Number . fromInteger <$> arbitrary-        , Aeson.Bool <$> arbitrary-        , return Aeson.Null-        ]--    recursive :: Int -> [Gen Aeson.Value]-    recursive sz = [-          do n <- choose (0, 5)-             Aeson.Array . Vector.Boxed.fromList <$> replicateM n (go (sz `div` n))-        , do n <- choose (0, 5)-             Aeson.object <$> replicateM n (-                     (Aeson..=)-                 <$> fieldName-                 <*> go (sz `div` n)-               )-        ]--    -- We're not interested in testing crazy values-    fieldName :: Gen Text.Strict.Text-    fieldName = elements ["a", "b", "c"]--{--------------------------------------------------------------------------------  Some global variables, which we use only as input to the tests--------------------------------------------------------------------------------}--exampleIORef :: SomeSTRef-{-# NOINLINE exampleIORef #-}-exampleIORef = unsafePerformIO $-    -- IORef is indistinguishable from STRef on the heap-    unsafeCoerce <$> newIORef (unsafeCoerce ())--exampleSTRef :: SomeSTRef-exampleSTRef = unsafePerformIO $ unsafeSTToIO $-    unsafeCoerce <$> newSTRef (unsafeCoerce ())--exampleMVar :: SomeMVar-{-# NOINLINE exampleMVar #-}-exampleMVar = unsafePerformIO $-    SomeMVar <$> newEmptyMVar--exampleTVar :: SomeTVar-{-# NOINLINE exampleTVar #-}-exampleTVar = unsafePerformIO $-    SomeTVar <$> newTVarIO (unsafeCoerce ())
+ tests/Test/RecoverRTTI/Classifier/Arbitrary.hs view
@@ -0,0 +1,259 @@+{-# LANGUAGE DataKinds             #-}+{-# LANGUAGE FlexibleContexts      #-}+{-# LANGUAGE GADTs                 #-}+{-# LANGUAGE KindSignatures        #-}+{-# LANGUAGE LambdaCase            #-}+{-# LANGUAGE QuantifiedConstraints #-}+{-# LANGUAGE RankNTypes            #-}+{-# LANGUAGE ScopedTypeVariables   #-}++module Test.RecoverRTTI.Classifier.Arbitrary (arbitraryClassifier_) where++import Data.Bifunctor+import Data.Kind+import Data.SOP+import Data.Tree (Tree)+import Data.Void+import GHC.Real (Ratio((:%)))++import qualified Data.HashMap.Internal.Array as HashMap.Array+import qualified Data.HashMap.Lazy           as HashMap+import qualified Data.HashSet                as HashSet+import qualified Data.IntMap                 as IntMap+import qualified Data.Map                    as Map+import qualified Data.Primitive.Array        as Prim.Array+import qualified Data.Sequence               as Seq+import qualified Data.Set                    as Set+import qualified Data.Tree                   as Tree+import qualified Data.Vector                 as Vector.Boxed++import Debug.RecoverRTTI+import Debug.RecoverRTTI.Classify++import Test.QuickCheck++import Test.RecoverRTTI.Classifier.Equality ()+import Test.RecoverRTTI.Prim+import Test.RecoverRTTI.QuickCheck.DepGen+import Test.RecoverRTTI.QuickCheck.Sized (SizedGen)++import qualified Test.RecoverRTTI.QuickCheck.Sized as SG++{-------------------------------------------------------------------------------+  Generate arbitiary classifiers+-------------------------------------------------------------------------------}++-- | Generated arbitrary classifier along with a generator for that value+--+-- NOTE: The " size " here refers to the size of the /classifier/. Along with+-- the classifier we construct a generator for values of the corresponding+-- type; that generator in turn has its own (independent) size parameter.+arbitraryClassifier_ :: forall c o.+     (c ~ Classifier_ o)+  => SizedGen (Some (DepGen o)) -> SizedGen (Some (DepGen c))+arbitraryClassifier_  genOther = go+  where+    go :: SizedGen (Some (DepGen c))+    go = SG.leafOrStep leaf compound++    -- Leaves of the tree (values with no recursion).+    --+    -- We will fail to generate a leaf when the size reaches 0; this ensures+    -- termination.+    leaf :: Gen (Some (DepGen c))+    leaf = do+        Some c <- arbitraryPrimClassifier+        return $ Some $ primDepGen c++    -- Compound+    --+    -- We deduct one from the size for the outer-most constructor+    --+    -- For most types we generate arbitrary subtypes, but for some types we+    -- must pick subtypes satisfying a certain constraint (e.g., @Ord@ for+    -- @Set@); for such types we just pick a single example.+    compound :: [SizedGen (Some (DepGen c))]+    compound = [+          -- We include " other " in the compound list, so that we are sure+          -- to subtract one from the size+          (\(Some (DepGen c gen)) -> Some (DepGen (C_Other c) gen)) <$> genOther++        , go_U_K C_Maybe Nothing+            (mapSome (GenK (fmap Just)) <$> go)++        , go_KU_UK C_Either+            (mapSome (GenKU  (fmap Left))  <$> go)+            (mapSome (GenUK (fmap Right)) <$> go)++          -- @[Char]@ is classified as @String@+        , let notChar (Some (DepGen (C_Prim C_Char) _)) = False+              notChar _otherwise = True in+          go_U_K C_List []+            (mapSome (GenK (SG.genListLike id)) <$> (go `SG.suchThat` notChar))++        , go_K C_Ratio $ pure . Some $ GenK {+              justGen  = \g -> uncurry (:%) <$> SG.divvyPair g g+            , justElem = primDepGen C_Int+            }++        , go_U_K C_Set Set.empty $ pure . Some $ GenK {+              justGen  = SG.genListLike Set.fromList+            , justElem = primDepGen C_Int+            }++        , go_UU_KK C_Map Map.empty+            ((\(Some genElem) -> Some $ GenKK {+                pairGen = SG.genMapLike Map.fromList+              , pairFst = primDepGen C_Int+              , pairSnd = genElem+              }) <$> go)++        , go_U_K C_IntMap IntMap.empty+            ((\(Some genElem) -> Some $ GenK {+                justGen  = SG.genMapLike IntMap.fromList SG.arbitrary+              , justElem = genElem+              }) <$> go)++        , go_U_K C_Sequence Seq.empty+            (mapSome (GenK (SG.genListLike Seq.fromList)) <$> go)++        , go_K C_Tree+            (mapSome (GenK (SG.genListLike mkSomeTree)) <$> go)++        , go_K C_HashSet $ pure . Some $ GenK {+              justGen  = SG.genListLike HashSet.fromList+            , justElem = primDepGen C_Int+            }++          -- @HashMap a ()@ is classified as a @HashSet@ instead+        , let notUnit (Some (DepGen (C_Prim C_Unit) _)) = False+              notUnit _otherwise = True in+          go_UU_KK C_HashMap HashMap.empty+            ((\(Some genElem) -> Some $ GenKK {+                pairGen = SG.genMapLike HashMap.fromList+              , pairFst = primDepGen C_Int+              , pairSnd = genElem+              }) <$> (go `SG.suchThat` notUnit))++        , let mkArray xs = HashMap.Array.fromList (length xs) xs in+          go_U_K C_HM_Array (mkArray [])+            (mapSome (GenK (SG.genListLike mkArray)) <$> go)++        , go_U_K C_Prim_Array (Prim.Array.fromList [])+            (mapSome (GenK (SG.genListLike Prim.Array.fromList)) <$> go)++        , go_U_K C_Vector_Boxed Vector.Boxed.empty+            (mapSome (GenK (SG.genListLike Vector.Boxed.fromList)) <$> go)++        , goTuple+        ]++    go_K :: forall f.+         ( forall x. Show x => Show (f x)+         , forall x. Eq   x => Eq   (f x)+         )+      => (forall x. Elems o '[x] -> c (f x))+      -> SizedGen (Some (GenK c f))+      -> SizedGen (Some (DepGen c))+    go_K cf = fmap (\(Some a) -> Some (genJust (cf . ElemK) a))++    go_U_K :: forall f.+         ( forall x. Show x => Show (f x)+         , forall x. Eq   x => Eq   (f x)+         )+      => (forall x. Elems o '[x] -> c (f x))+      -> f Void+      -> SizedGen (Some (GenK c f))+      -> SizedGen (Some (DepGen c))+    go_U_K cf nothing just =+        SG.leafOrStep+          (pure $ Some $ DepGen (cf ElemU) (pure nothing))+          [(\(Some a) -> Some (genJust (cf . ElemK) a)) <$> just]++    go_KU_UK :: forall f.+         ( forall x y. (Show x, Show y) => Show (f x y)+         , forall x y. (Eq   x, Eq   y) => Eq   (f x y)+         )+      => (forall x y. Elems o '[x, y] -> c (f x y))+      -> SizedGen (Some (GenKU c f))+      -> SizedGen (Some (GenUK c f))+      -> SizedGen (Some (DepGen c))+    go_KU_UK cf left right =+        SG.oneofStepped [+            (\(Some a) -> Some (genLeft  (cf . ElemKU)  a)) <$> left+          , (\(Some b) -> Some (genRight (cf . ElemUK) b)) <$> right+          ]++    go_UU_KK :: forall (f :: Type -> Type -> Type).+         ( forall x y. (Show x, Show y) => Show (f x y)+         , forall x y. (Eq   x, Eq   y) => Eq   (f x y)+         )+      => (forall x y. Elems o '[x, y] -> c (f x y))+      -> f Void Void+      -> SizedGen (Some (GenKK c f))+      -> SizedGen (Some (DepGen c))+    go_UU_KK cf nothing just =+        SG.leafOrStep+          (pure $ Some $ DepGen (cf ElemUU) (pure nothing))+          [(\(Some ab@GenKK{}) -> Some (genPair (cf . uncurry ElemKK) ab)) <$> just]++    goTuple :: SizedGen (Some (DepGen c))+    goTuple =+        (\(Some (SG.ValidTuple t)) -> Some (lift t)) <$> SG.genTuple go+      where+        lift :: (SListI xs, IsValidSize (Length xs))+          => NP (DepGen (Classifier_ o)) xs+          -> DepGen (Classifier_ o) (WrappedTuple xs)+        lift t = genNP (C_Tuple . Elems . hmap Elem) $ GenNP {+              npGen  = fmap tupleFromNP . hsequence+            , npElem = t+            }++    _checkAllCases :: Classifier_ o a -> ()+    _checkAllCases = \case+        -- Primitive and user-defined+        C_Prim{}  -> ()+        C_Other{} -> ()++        -- Compound+        C_Maybe{}        -> ()+        C_Either{}       -> ()+        C_List{}         -> ()+        C_Ratio{}        -> ()+        C_Set{}          -> ()+        C_Map{}          -> ()+        C_IntMap{}       -> ()+        C_Sequence{}     -> ()+        C_Tree{}         -> ()+        C_HashSet{}      -> ()+        C_HashMap{}      -> ()+        C_HM_Array{}     -> ()+        C_Prim_Array{}   -> ()+        C_Vector_Boxed{} -> ()+        C_Tuple{}        -> ()++{-------------------------------------------------------------------------------+  Auxiliary tree functions+-------------------------------------------------------------------------------}++mkSomeTree :: [a] -> Tree a+mkSomeTree []       = error "mkSomeTree: empty"+mkSomeTree [x]      = Tree.Node x []+mkSomeTree [x, y]   = Tree.Node x [Tree.Node y []]+mkSomeTree (x : xs) =+    let (left, right) = split xs+    in Tree.Node x [mkSomeTree left, mkSomeTree right]++-- | Split list into halves+--+-- If the input has at least two elements, neither list will be empty+--+-- > split "abcde" == ("ace","bd")+split :: [a] -> ([a], [a])+split []     = ([], [])+split (x:xs) = first (x:) $ splot xs++-- | Auxiliary to 'split'+splot :: [a] -> ([a], [a])+splot []     = ([], [])+splot (x:xs) = second (x:) $ split xs
+ tests/Test/RecoverRTTI/Classifier/Equality.hs view
@@ -0,0 +1,12 @@+-- | Equality orphan instances+module Test.RecoverRTTI.Classifier.Equality () where++import Data.Function (on)++import qualified Data.HashMap.Internal.Array as HashMap (Array)+import qualified Data.HashMap.Internal.Array as HashMap.Array++import Test.RecoverRTTI.Prim ()++instance Eq a => Eq (HashMap.Array a) where+  (==) = (==) `on` HashMap.Array.toList
+ tests/Test/RecoverRTTI/Classifier/Size.hs view
@@ -0,0 +1,45 @@+{-# LANGUAGE FlexibleContexts    #-}+{-# LANGUAGE GADTs               #-}+{-# LANGUAGE RankNTypes          #-}+{-# LANGUAGE ScopedTypeVariables #-}++module Test.RecoverRTTI.Classifier.Size (classifierSize_) where++import Data.SOP++import Debug.RecoverRTTI++{-------------------------------------------------------------------------------+  Size+-------------------------------------------------------------------------------}++classifierSize_ :: forall o.+      (forall a. o a -> Int)+   -> (forall a. Classifier_ o a -> Int)+classifierSize_ sizeOther = go+  where+    go :: Classifier_ o a -> Int+    go (C_Prim         _) = 1+    go (C_Other        c) = sizeOther c+    go (C_Maybe        c) = 1 + goElems c+    go (C_Either       c) = 1 + goElems c+    go (C_List         c) = 1 + goElems c+    go (C_Ratio        c) = 1 + goElems c+    go (C_Set          c) = 1 + goElems c+    go (C_Map          c) = 1 + goElems c+    go (C_IntMap       c) = 1 + goElems c+    go (C_Sequence     c) = 1 + goElems c+    go (C_Tree         c) = 1 + goElems c+    go (C_HashSet      c) = 1 + goElems c+    go (C_HashMap      c) = 1 + goElems c+    go (C_HM_Array     c) = 1 + goElems c+    go (C_Prim_Array   c) = 1 + goElems c+    go (C_Vector_Boxed c) = 1 + goElems c+    go (C_Tuple        c) = 1 + goElems c++    goElems :: SListI as => Elems o as -> Int+    goElems (Elems cs) = sum . hcollapse $ hmap (K . goElem) cs++    goElem :: Elem o a -> Int+    goElem NoElem   = 0+    goElem (Elem c) = go c
tests/Test/RecoverRTTI/Classify.hs view
@@ -11,6 +11,7 @@ import Data.Ratio import Data.SOP import Data.Type.Equality+import Unsafe.Coerce (unsafeCoerce)  import qualified Data.Aeson                  as Aeson import qualified Data.HashMap.Internal.Array as HashMap.Array@@ -19,19 +20,22 @@ import qualified Data.IntMap                 as IntMap import qualified Data.IntSet                 as IntSet import qualified Data.Map                    as Map+import qualified Data.Primitive.Array        as Prim.Array import qualified Data.Sequence               as Seq import qualified Data.Set                    as Set import qualified Data.Tree                   as Tree import qualified Data.Vector                 as Vector.Boxed+import qualified Data.Vector.Storable        as Vector.Storable+import qualified Data.Vector.Primitive       as Vector.Primitive  import Test.Tasty import Test.Tasty.QuickCheck hiding (classify, NonEmpty)  import Debug.RecoverRTTI+import Debug.RecoverRTTI.Classify -import Test.RecoverRTTI.Arbitrary import Test.RecoverRTTI.ConcreteClassifier-import Test.RecoverRTTI.Orphans ()+import Test.RecoverRTTI.Globals import Test.RecoverRTTI.Staged import Test.RecoverRTTI.UserDefined @@ -50,185 +54,253 @@ prop_constants = withMaxSuccess 1 $ conjoin [       -- Primitive types -      compareClassifier $ Value CC_Bool     True-    , compareClassifier $ Value CC_Bool     False-    , compareClassifier $ Value CC_Char     'a'-    , compareClassifier $ Value CC_Double   1.25-    , compareClassifier $ Value CC_Float    1.25-    , compareClassifier $ Value CC_Int      1234-    , compareClassifier $ Value CC_Int      (-1234)-    , compareClassifier $ Value CC_Int8     123-    , compareClassifier $ Value CC_Int16    1234-    , compareClassifier $ Value CC_Int32    1234-    , compareClassifier $ Value CC_Int64    1234-    , compareClassifier $ Value CC_Integer  1234-    , compareClassifier $ Value CC_Integer  (succ (fromIntegral (maxBound :: Int)))-    , compareClassifier $ Value CC_Integer  (pred (fromIntegral (minBound :: Int)))-    , compareClassifier $ Value CC_Ordering LT-    , compareClassifier $ Value CC_Ordering GT-    , compareClassifier $ Value CC_Ordering EQ-    , compareClassifier $ Value CC_Unit     ()-    , compareClassifier $ Value CC_Word     1234-    , compareClassifier $ Value CC_Word8    123-    , compareClassifier $ Value CC_Word16   134-    , compareClassifier $ Value CC_Word32   1234-    , compareClassifier $ Value CC_Word64   1234+      compareClassifier $ Value (C_Prim C_Bool)     True+    , compareClassifier $ Value (C_Prim C_Bool)     False+    , compareClassifier $ Value (C_Prim C_Char)     'a'+    , compareClassifier $ Value (C_Prim C_Double)   1.25+    , compareClassifier $ Value (C_Prim C_Float)    1.25+    , compareClassifier $ Value (C_Prim C_Int)      1234+    , compareClassifier $ Value (C_Prim C_Int)      (-1234)+    , compareClassifier $ Value (C_Prim C_Int8)     123+    , compareClassifier $ Value (C_Prim C_Int16)    1234+    , compareClassifier $ Value (C_Prim C_Int32)    1234+    , compareClassifier $ Value (C_Prim C_Int64)    1234+    , compareClassifier $ Value (C_Prim C_Integer)  1234+    , compareClassifier $ Value (C_Prim C_Integer)  (succ (fromIntegral (maxBound :: Int)))+    , compareClassifier $ Value (C_Prim C_Integer)  (pred (fromIntegral (minBound :: Int)))+    , compareClassifier $ Value (C_Prim C_Ordering) LT+    , compareClassifier $ Value (C_Prim C_Ordering) GT+    , compareClassifier $ Value (C_Prim C_Ordering) EQ+    , compareClassifier $ Value (C_Prim C_Unit)     ()+    , compareClassifier $ Value (C_Prim C_Word)     1234+    , compareClassifier $ Value (C_Prim C_Word8)    123+    , compareClassifier $ Value (C_Prim C_Word16)   134+    , compareClassifier $ Value (C_Prim C_Word32)   1234+    , compareClassifier $ Value (C_Prim C_Word64)   1234        -- String types       ---      -- We skip the empty string, because we infer that as @CC_List Empty@+      -- We skip the empty string, because we infer that as @C_List Empty@ -    , compareClassifier $ Value CC_String      "abcdefg"-    , compareClassifier $ Value CC_BS_Strict   ""-    , compareClassifier $ Value CC_BS_Strict   "abcdefg"-    , compareClassifier $ Value CC_BS_Lazy     ""-    , compareClassifier $ Value CC_BS_Lazy     "abcdefg"-    , compareClassifier $ Value CC_BS_Short    ""-    , compareClassifier $ Value CC_BS_Short    "abcdefg"-    , compareClassifier $ Value CC_Text_Strict ""-    , compareClassifier $ Value CC_Text_Strict "abcdefg"-    , compareClassifier $ Value CC_Text_Lazy   ""-    , compareClassifier $ Value CC_Text_Lazy   "abcdefg"+    , compareClassifier $ Value (C_Prim C_String)      "abcdefg"+    , compareClassifier $ Value (C_Prim C_BS_Strict)   ""+    , compareClassifier $ Value (C_Prim C_BS_Strict)   "abcdefg"+    , compareClassifier $ Value (C_Prim C_BS_Lazy)     ""+    , compareClassifier $ Value (C_Prim C_BS_Lazy)     "abcdefg"+    , compareClassifier $ Value (C_Prim C_BS_Short)    ""+    , compareClassifier $ Value (C_Prim C_BS_Short)    "abcdefg"+    , compareClassifier $ Value (C_Prim C_Text_Strict) ""+    , compareClassifier $ Value (C_Prim C_Text_Strict) "abcdefg"+    , compareClassifier $ Value (C_Prim C_Text_Lazy)   ""+    , compareClassifier $ Value (C_Prim C_Text_Lazy)   "abcdefg"        -- Aeson -    , compareClassifier $ Value CC_Value (Aeson.object [("x" Aeson..= True)])+    , compareClassifier $ Value (C_Prim C_Value) (Aeson.object [("x" Aeson..= True)]) -      -- Compound+      -- Reference cells -    , compareClassifier $ Value (CC_Maybe FNothing)       Nothing-    , compareClassifier $ Value (CC_Maybe (FJust CC_Int)) (Just 3)+    , compareClassifier $ Value (C_Prim C_STRef) exampleIORef+    , compareClassifier $ Value (C_Prim C_STRef) exampleSTRef+    , compareClassifier $ Value (C_Prim C_MVar)  exampleMVar+    , compareClassifier $ Value (C_Prim C_TVar)  exampleTVar -    , compareClassifier $ Value (CC_Either (FLeft  CC_Int))  (Left 3)-    , compareClassifier $ Value (CC_Either (FRight CC_Bool)) (Right True)+      -- Functions -    , compareClassifier $ Value (CC_List FNothing)       []-    , compareClassifier $ Value (CC_List (FJust CC_Int)) [1, 2, 3]+    , compareClassifier $ Value (C_Prim C_Fun) (SomeFun id) -    , compareClassifier $ Value (CC_Tuple (ConcreteClassifiers (CC_Int :* CC_Char :* Nil)))            (WrappedTuple (4, 'a'))-    , compareClassifier $ Value (CC_Tuple (ConcreteClassifiers (CC_Int :* CC_Char :* CC_Bool :* Nil))) (WrappedTuple (4, 'a', True))+      -- Containers without type arguments -    , compareClassifier $ Value (CC_Ratio CC_Integer) (1 % 2)+    , compareClassifier $ Value (C_Prim C_IntSet) $+        IntSet.empty+    , compareClassifier $ Value (C_Prim C_IntSet) $+        IntSet.fromList [1, 2, 3] -    , compareClassifier $ Value (CC_Set FNothing)        Set.empty-    , compareClassifier $ Value (CC_Set (FJust CC_Int)) (Set.fromList [1, 2, 3] )+    , compareClassifier $ Value (C_Prim C_Prim_ArrayM) $+        examplePrimArrayM -    , compareClassifier $ Value (CC_Map FNothingPair)                Map.empty-    , compareClassifier $ Value (CC_Map (FJustPair CC_Int CC_Char)) (Map.fromList [(1, 'a'), (2, 'b')])+    , compareClassifier $ Value (C_Prim C_Vector_Storable) $+        SomeStorableVector $ unsafeCoerce $+          Vector.Storable.fromList ([1, 2] :: [Double]) -    , compareClassifier $ Value CC_IntSet  IntSet.empty-    , compareClassifier $ Value CC_IntSet (IntSet.fromList [1, 2, 3])+    , compareClassifier $ Value (C_Prim C_Vector_StorableM) $+        exampleStorableVectorM -    , compareClassifier $ Value (CC_IntMap FNothing)         IntMap.empty-    , compareClassifier $ Value (CC_IntMap (FJust CC_Char)) (IntMap.fromList [(1, 'a'), (2, 'b')])+    , compareClassifier $ Value (C_Prim C_Vector_Primitive) $+        SomePrimitiveVector $ unsafeCoerce $+          Vector.Primitive.fromList ([1, 2] :: [Double]) -    , compareClassifier $ Value (CC_Sequence FNothing)        Seq.empty-    , compareClassifier $ Value (CC_Sequence (FJust CC_Int)) (Seq.fromList [1, 2, 3])+    , compareClassifier $ Value (C_Prim C_Vector_PrimitiveM) $+        examplePrimitiveVectorM -    , compareClassifier $ Value (CC_Tree CC_Int) (Tree.Node 1 [])+      -- Compound -    , compareClassifier $ Value (CC_HashSet CC_Int) (HashSet.fromList [1, 2, 3])+    , compareClassifier $ Value (C_Maybe ElemU) $+        Nothing+    , compareClassifier $ Value (C_Maybe (ElemK (C_Prim C_Int))) $+        Just 3 -    , compareClassifier $ Value (CC_HashMap FNothingPair)                HashMap.empty-    , compareClassifier $ Value (CC_HashMap (FJustPair CC_Int CC_Char)) (HashMap.fromList [(1, 'a'), (2, 'b')])+    , compareClassifier $ Value (C_Either (ElemKU (C_Prim C_Int))) $+        Left 3+    , compareClassifier $ Value (C_Either (ElemUK (C_Prim C_Bool))) $+        Right True -    , compareClassifier $ Value (CC_HM_Array FNothing)       (HashMap.Array.fromList 0 [])-    , compareClassifier $ Value (CC_HM_Array (FJust CC_Int)) (HashMap.Array.fromList 2 [1, 2])+    , compareClassifier $ Value (C_List ElemU) $+        []+    , compareClassifier $ Value (C_List (ElemK (C_Prim C_Int))) $+        [1, 2, 3] -    , compareClassifier $ Value (CC_Vector_Boxed FNothing)        Vector.Boxed.empty-    , compareClassifier $ Value (CC_Vector_Boxed (FJust CC_Int)) (Vector.Boxed.fromList [1, 2, 3])+    , compareClassifier $ Value (C_Tuple (Elems (Elem (C_Prim C_Int) :* Elem (C_Prim C_Char) :* Nil))) $+        WrappedTuple (4, 'a')+    , compareClassifier $ Value (C_Tuple (Elems (Elem (C_Prim C_Int) :* Elem (C_Prim C_Char) :* Elem (C_Prim C_Bool) :* Nil))) $+        WrappedTuple (4, 'a', True) -      -- Reference cells+    , compareClassifier $ Value (C_Ratio (ElemK (C_Prim C_Integer))) $+        1 % 2 -    , compareClassifier $ Value CC_STRef exampleIORef-    , compareClassifier $ Value CC_STRef exampleSTRef-    , compareClassifier $ Value CC_MVar  exampleMVar-    , compareClassifier $ Value CC_TVar  exampleTVar+    , compareClassifier $ Value (C_Set ElemU) $+        Set.empty+    , compareClassifier $ Value (C_Set (ElemK (C_Prim C_Int))) $+        Set.fromList [1, 2, 3] -      -- Functions+    , compareClassifier $ Value (C_Map ElemUU) $+        Map.empty+    , compareClassifier $ Value (C_Map (ElemKK (C_Prim C_Int) (C_Prim C_Char))) $+        Map.fromList [(1, 'a'), (2, 'b')] -    , compareClassifier $ Value CC_Fun (SomeFun id)+    , compareClassifier $ Value (C_IntMap ElemU) $+        IntMap.empty+    , compareClassifier $ Value (C_IntMap (ElemK (C_Prim C_Char))) $+        IntMap.fromList [(1, 'a'), (2, 'b')] +    , compareClassifier $ Value (C_Sequence ElemU) $+        Seq.empty+    , compareClassifier $ Value (C_Sequence (ElemK (C_Prim C_Int))) $+        Seq.fromList [1, 2, 3]++    , compareClassifier $ Value (C_Tree (ElemK (C_Prim C_Int))) $+        Tree.Node 1 []++    , compareClassifier $ Value (C_HashSet (ElemK (C_Prim C_Int))) $+        HashSet.fromList [1, 2, 3]++    , compareClassifier $ Value (C_HashMap ElemUU) $+        HashMap.empty+    , compareClassifier $ Value (C_HashMap (ElemKK (C_Prim C_Int) (C_Prim C_Char))) $+        HashMap.fromList [(1, 'a'), (2, 'b')]++    , compareClassifier $ Value (C_HM_Array ElemU) $+        HashMap.Array.fromList 0 []+    , compareClassifier $ Value (C_HM_Array (ElemK (C_Prim C_Int))) $+        HashMap.Array.fromList 2 [1, 2]++    , compareClassifier $ Value (C_Prim_Array ElemU) $+        Prim.Array.fromList []+    , compareClassifier $ Value (C_Prim_Array (ElemK (C_Prim C_Int))) $+        Prim.Array.fromList [1, 2, 3]++    , compareClassifier $ Value (C_Vector_Boxed ElemU) $+        Vector.Boxed.empty+    , compareClassifier $ Value (C_Vector_Boxed (ElemK (C_Prim C_Int))) $+        Vector.Boxed.fromList [1, 2, 3]+       -- User defined -    , compareClassifier $ Value  CC_User_Simple                    SimpleA-    , compareClassifier $ Value  CC_User_Simple                    SimpleB-    , compareClassifier $ Value (CC_User_NonRec    FNothing)       (NR1 1234)-    , compareClassifier $ Value (CC_User_NonRec   (FJust CC_Char)) (NR2 'a' True)-    , compareClassifier $ Value (CC_User_Rec       FNothing)        RNil-    , compareClassifier $ Value (CC_User_Rec      (FJust CC_Char)) (RCons 'a' RNil)-    , compareClassifier $ Value  CC_User_Unlifted                  exampleContainsUnlifted+    , compareClassifier $ Value (C_Other C_Simple) $+        SimpleA+    , compareClassifier $ Value (C_Other C_Simple) $+        SimpleB++    , compareClassifier $ Value (C_Other (C_NonRec ElemU))  $+        (NR1 1234)+    , compareClassifier $ Value (C_Other (C_NonRec (ElemK (C_Prim C_Char)))) $+        (NR2 True 'a')++    , compareClassifier $ Value (C_Other (C_Rec ElemU)) $+        RNil+    , compareClassifier $ Value (C_Other (C_Rec (ElemK (C_Prim C_Char)))) $+        (RCons 'a' RNil)++    , compareClassifier $ Value (C_Other C_Unlifted) $+        exampleContainsUnlifted     ]   where     _checkAllCases :: ConcreteClassifier a -> ()     _checkAllCases = \case-        -- Primitive types--        CC_Bool     -> ()-        CC_Char     -> ()-        CC_Double   -> ()-        CC_Float    -> ()-        CC_Int      -> ()-        CC_Int8     -> ()-        CC_Int16    -> ()-        CC_Int32    -> ()-        CC_Int64    -> ()-        CC_Integer  -> ()-        CC_Ordering -> ()-        CC_Unit     -> ()-        CC_Word     -> ()-        CC_Word8    -> ()-        CC_Word16   -> ()-        CC_Word32   -> ()-        CC_Word64   -> ()+        C_Prim C_Bool     -> ()+        C_Prim C_Char     -> ()+        C_Prim C_Double   -> ()+        C_Prim C_Float    -> ()+        C_Prim C_Int      -> ()+        C_Prim C_Int8     -> ()+        C_Prim C_Int16    -> ()+        C_Prim C_Int32    -> ()+        C_Prim C_Int64    -> ()+        C_Prim C_Integer  -> ()+        C_Prim C_Ordering -> ()+        C_Prim C_Unit     -> ()+        C_Prim C_Word     -> ()+        C_Prim C_Word8    -> ()+        C_Prim C_Word16   -> ()+        C_Prim C_Word32   -> ()+        C_Prim C_Word64   -> ()          -- String types -        CC_String      -> ()-        CC_BS_Strict   -> ()-        CC_BS_Lazy     -> ()-        CC_BS_Short    -> ()-        CC_Text_Strict -> ()-        CC_Text_Lazy   -> ()+        C_Prim C_String      -> ()+        C_Prim C_BS_Strict   -> ()+        C_Prim C_BS_Lazy     -> ()+        C_Prim C_BS_Short    -> ()+        C_Prim C_Text_Strict -> ()+        C_Prim C_Text_Lazy   -> ()          -- Aeson -        CC_Value -> ()+        C_Prim C_Value -> () -        -- Compound+        -- Containers without type arguments -        CC_Maybe{}        -> ()-        CC_Either{}       -> ()-        CC_List{}         -> ()-        CC_Ratio{}        -> ()-        CC_Set{}          -> ()-        CC_Map{}          -> ()-        CC_IntSet{}       -> ()-        CC_IntMap{}       -> ()-        CC_Sequence{}     -> ()-        CC_Tree{}         -> ()-        CC_Tuple{}        -> ()-        CC_HashSet{}      -> ()-        CC_HashMap{}      -> ()-        CC_HM_Array{}     -> ()-        CC_Vector_Boxed{} -> ()+        C_Prim C_IntSet            -> ()+        C_Prim C_Prim_ArrayM       -> ()+        C_Prim C_Vector_Storable   -> ()+        C_Prim C_Vector_StorableM  -> ()+        C_Prim C_Vector_Primitive  -> ()+        C_Prim C_Vector_PrimitiveM -> ()          -- Functions -        CC_Fun{} -> ()+        C_Prim C_Fun -> ()          -- Reference cells -        CC_STRef -> ()-        CC_TVar  -> ()-        CC_MVar  -> ()+        C_Prim C_STRef -> ()+        C_Prim C_TVar  -> ()+        C_Prim C_MVar  -> () +        -- Compound++        C_Maybe{}        -> ()+        C_Either{}       -> ()+        C_List{}         -> ()+        C_Ratio{}        -> ()+        C_Set{}          -> ()+        C_Map{}          -> ()+        C_IntMap{}       -> ()+        C_Sequence{}     -> ()+        C_Tree{}         -> ()+        C_Tuple{}        -> ()+        C_HashSet{}      -> ()+        C_HashMap{}      -> ()+        C_HM_Array{}     -> ()+        C_Prim_Array{}   -> ()+        C_Vector_Boxed{} -> ()+         -- User-defined -        CC_User_Simple{}   -> ()-        CC_User_NonRec{}   -> ()-        CC_User_Rec{}      -> ()-        CC_User_Unlifted{} -> ()+        C_Other (C_Simple{})   -> ()+        C_Other (C_NonRec{})   -> ()+        C_Other (C_Rec{})      -> ()+        C_Other (C_Unlifted{}) -> ()  -- | Test using arbitrary values prop_arbitrary :: Some Value -> Property@@ -240,14 +312,14 @@ compareClassifier :: Value a -> Property compareClassifier = \(Value cc x) ->       counterexample ("Generated classifier: " ++ show cc)-    $ case runExcept $ classifyThenReclassify x of+    $ case runExcept $ classifyConcrete x of         Left err  ->             counterexample ("Failed to reclassify. Error: " ++ err)           $ property False-        Right (Reclassified cc' f) ->-          case sameConcreteClassifier cc cc' of+        Right (Reclassified cc' _pf) ->+          case sameConcrete cc cc' of             Nothing ->                 counterexample ("Inferred different classifier: " ++ show cc')               $ property False             Just Refl ->-              x === f x+              property True
tests/Test/RecoverRTTI/ConcreteClassifier.hs view
@@ -1,52 +1,59 @@-{-# LANGUAGE FlexibleContexts      #-}-{-# LANGUAGE FlexibleInstances     #-}-{-# LANGUAGE GADTs                 #-}-{-# LANGUAGE KindSignatures        #-}-{-# LANGUAGE LambdaCase            #-}-{-# LANGUAGE ScopedTypeVariables   #-}-{-# LANGUAGE StandaloneDeriving    #-}-{-# LANGUAGE TypeOperators         #-}-{-# LANGUAGE UndecidableInstances  #-}+{-# LANGUAGE ConstraintKinds         #-}+{-# LANGUAGE DataKinds               #-}+{-# LANGUAGE FlexibleContexts        #-}+{-# LANGUAGE FlexibleInstances       #-}+{-# LANGUAGE GADTs                   #-}+{-# LANGUAGE KindSignatures          #-}+{-# LANGUAGE LambdaCase              #-}+{-# LANGUAGE QuantifiedConstraints   #-}+{-# LANGUAGE RankNTypes              #-}+{-# LANGUAGE ScopedTypeVariables     #-}+{-# LANGUAGE StandaloneDeriving      #-}+{-# LANGUAGE TypeOperators           #-}+{-# LANGUAGE UndecidableInstances    #-}+{-# LANGUAGE UndecidableSuperClasses #-}  module Test.RecoverRTTI.ConcreteClassifier (     -- * Concrete classifier-    ConcreteClassifier(..)-  , sameConcreteClassifier-  , ConcreteClassifiers(..)-  , classifierSize+    ConcreteClassifier+  , ClassifyUser(..)     -- * Values   , Value(..)+    -- * Constraints+  , canShowConcrete+  , canCompareConcrete+    -- * Size+  , sizeUser+  , sizeConcrete+    -- * Same classifier+  , sameUser+  , sameConcrete+    -- * Equality+    -- * Arbitrary+  , arbitraryUser+  , arbitraryConcrete   ) where -import Data.HashMap.Lazy (HashMap)-import Data.HashSet (HashSet)-import Data.Int-import Data.IntMap (IntMap)-import Data.IntSet (IntSet) import Data.Kind-import Data.Map (Map)-import Data.Ratio-import Data.Sequence (Seq)-import Data.Set (Set) import Data.SOP import Data.SOP.Dict-import Data.Tree (Tree) import Data.Type.Equality-import Data.Word--import qualified Data.Aeson                  as Aeson-import qualified Data.ByteString             as BS.Strict-import qualified Data.ByteString.Lazy        as BS.Lazy-import qualified Data.ByteString.Short       as BS.Short-import qualified Data.HashMap.Internal.Array as HashMap (Array)-import qualified Data.Text                   as Text.Strict-import qualified Data.Text.Lazy              as Text.Lazy-import qualified Data.Vector                 as Vector.Boxed+import Data.Void  import Debug.RecoverRTTI+import Debug.RecoverRTTI.Classify +import Test.QuickCheck++import Test.RecoverRTTI.Classifier.Arbitrary+import Test.RecoverRTTI.Classifier.Equality ()+import Test.RecoverRTTI.Classifier.Size+import Test.RecoverRTTI.QuickCheck.DepGen+import Test.RecoverRTTI.QuickCheck.Sized (SizedGen) import Test.RecoverRTTI.UserDefined +import qualified Test.RecoverRTTI.QuickCheck.Sized as SG+ {-------------------------------------------------------------------------------   Concrete classifier @@ -60,180 +67,15 @@   information about which user-defined types we're interested in). -------------------------------------------------------------------------------} --- | Like 'Classifier', but with no guess-work and concrete types-data ConcreteClassifier (a :: Type) :: Type where-    -- Primitive types--    CC_Bool     :: ConcreteClassifier Bool-    CC_Char     :: ConcreteClassifier Char-    CC_Double   :: ConcreteClassifier Double-    CC_Float    :: ConcreteClassifier Float-    CC_Int      :: ConcreteClassifier Int-    CC_Int8     :: ConcreteClassifier Int8-    CC_Int16    :: ConcreteClassifier Int16-    CC_Int32    :: ConcreteClassifier Int32-    CC_Int64    :: ConcreteClassifier Int64-    CC_Integer  :: ConcreteClassifier Integer-    CC_Ordering :: ConcreteClassifier Ordering-    CC_Unit     :: ConcreteClassifier ()-    CC_Word     :: ConcreteClassifier Word-    CC_Word8    :: ConcreteClassifier Word8-    CC_Word16   :: ConcreteClassifier Word16-    CC_Word32   :: ConcreteClassifier Word32-    CC_Word64   :: ConcreteClassifier Word64--    -- Text types--    CC_String      :: ConcreteClassifier String-    CC_BS_Strict   :: ConcreteClassifier BS.Strict.ByteString-    CC_BS_Lazy     :: ConcreteClassifier BS.Lazy.ByteString-    CC_BS_Short    :: ConcreteClassifier BS.Short.ShortByteString-    CC_Text_Strict :: ConcreteClassifier Text.Strict.Text-    CC_Text_Lazy   :: ConcreteClassifier Text.Lazy.Text--    -- Aeson--    CC_Value :: ConcreteClassifier Aeson.Value--    -- Compound--    CC_Maybe        :: MaybeF     ConcreteClassifier a   -> ConcreteClassifier (Maybe a)-    CC_Either       :: EitherF    ConcreteClassifier a b -> ConcreteClassifier (Either a b)-    CC_List         :: MaybeF     ConcreteClassifier a   -> ConcreteClassifier [a]-    CC_Ratio        ::            ConcreteClassifier a   -> ConcreteClassifier (Ratio a)-    CC_Set          :: MaybeF     ConcreteClassifier a   -> ConcreteClassifier (Set a)-    CC_Map          :: MaybePairF ConcreteClassifier a b -> ConcreteClassifier (Map a b)-    CC_IntSet       ::                                      ConcreteClassifier IntSet-    CC_IntMap       :: MaybeF     ConcreteClassifier a   -> ConcreteClassifier (IntMap a)-    CC_Sequence     :: MaybeF     ConcreteClassifier a   -> ConcreteClassifier (Seq a)-    CC_Tree         ::            ConcreteClassifier a   -> ConcreteClassifier (Tree a)-    CC_HashSet      ::            ConcreteClassifier a   -> ConcreteClassifier (HashSet a)-    CC_HashMap      :: MaybePairF ConcreteClassifier a b -> ConcreteClassifier (HashMap a b)-    CC_HM_Array     :: MaybeF     ConcreteClassifier a   -> ConcreteClassifier (HashMap.Array a)-    CC_Vector_Boxed :: MaybeF     ConcreteClassifier a   -> ConcreteClassifier (Vector.Boxed.Vector a)--    CC_Tuple ::-         (SListI xs, IsValidSize (Length xs))-      => ConcreteClassifiers xs -> ConcreteClassifier (WrappedTuple xs)--    -- Functions--    CC_Fun :: ConcreteClassifier SomeFun--    -- Reference cells--    CC_STRef :: ConcreteClassifier SomeSTRef-    CC_TVar  :: ConcreteClassifier SomeTVar-    CC_MVar  :: ConcreteClassifier SomeMVar--    -- User-defined--    CC_User_Simple   :: ConcreteClassifier SimpleType-    CC_User_NonRec   :: MaybeF ConcreteClassifier a -> ConcreteClassifier (NonRecursive a)-    CC_User_Rec      :: MaybeF ConcreteClassifier a -> ConcreteClassifier (Recursive    a)-    CC_User_Unlifted :: ConcreteClassifier ContainsUnlifted--newtype ConcreteClassifiers xs = ConcreteClassifiers (NP ConcreteClassifier xs)--deriving instance Show (ConcreteClassifier a)-deriving instance Show (MaybeF     ConcreteClassifier a)-deriving instance Show (EitherF    ConcreteClassifier a b)-deriving instance Show (MaybePairF ConcreteClassifier a b)--instance SListI xs => Show (ConcreteClassifiers xs) where-  show (ConcreteClassifiers xs) = go (hpure Dict)-    where-      go :: NP (Dict (Compose Show ConcreteClassifier)) xs -> String-      go dicts =-          case all_NP dicts of-            Dict -> "(" ++ show xs ++ ")"--{--------------------------------------------------------------------------------  Size of the classifier--  Mostly used for sanity checking the generator--------------------------------------------------------------------------------}--classifierSize :: ConcreteClassifier a -> Int-classifierSize = go-  where-    go :: ConcreteClassifier a -> Int--    -- Primitive types-    go CC_Bool     = 1-    go CC_Char     = 1-    go CC_Double   = 1-    go CC_Float    = 1-    go CC_Int      = 1-    go CC_Int8     = 1-    go CC_Int16    = 1-    go CC_Int32    = 1-    go CC_Int64    = 1-    go CC_Integer  = 1-    go CC_Ordering = 1-    go CC_Unit     = 1-    go CC_Word     = 1-    go CC_Word8    = 1-    go CC_Word16   = 1-    go CC_Word32   = 1-    go CC_Word64   = 1--    -- Text types-    go CC_String      = 1-    go CC_BS_Strict   = 1-    go CC_BS_Lazy     = 1-    go CC_BS_Short    = 1-    go CC_Text_Strict = 1-    go CC_Text_Lazy   = 1--    -- Aeson-    go CC_Value = 1--    -- Compound--    go (CC_Maybe        c) = 1 + goMaybeF     c-    go (CC_Either       c) = 1 + goEitherF    c-    go (CC_List         c) = 1 + goMaybeF     c-    go (CC_Ratio        c) = 1 + go           c-    go (CC_Set          c) = 1 + goMaybeF     c-    go (CC_Map          c) = 1 + goMaybePairF c-    go  CC_IntSet          = 1-    go (CC_IntMap       c) = 1 + goMaybeF     c-    go (CC_Sequence     c) = 1 + goMaybeF     c-    go (CC_Tree         c) = 1 + go           c-    go (CC_HashSet      c) = 1 + go           c-    go (CC_HashMap      c) = 1 + goMaybePairF c-    go (CC_HM_Array     c) = 1 + goMaybeF     c-    go (CC_Vector_Boxed c) = 1 + goMaybeF     c--    go (CC_Tuple (ConcreteClassifiers cs)) =-        1 + sum (hcollapse (hmap (K . go) cs))--    -- Functions-    go CC_Fun = 1--    -- Reference cells-    go CC_STRef = 1-    go CC_TVar  = 1-    go CC_MVar  = 1--    -- User-defined-    go  CC_User_Simple      = 1-    go (CC_User_NonRec   c) = 1 + goMaybeF c-    go (CC_User_Rec      c) = 1 + goMaybeF c-    go  CC_User_Unlifted    = 1--    goMaybeF :: MaybeF ConcreteClassifier a -> Int-    goMaybeF FNothing  = 0-    goMaybeF (FJust c) = go c+type ConcreteClassifier = Classifier_ ClassifyUser -    goEitherF :: EitherF ConcreteClassifier a b -> Int-    goEitherF (FLeft  c) = go c-    goEitherF (FRight c) = go c+data ClassifyUser (a :: Type) where+  C_Simple   :: ClassifyUser SimpleType+  C_NonRec   :: Elems ClassifyUser '[a] -> ClassifyUser (NonRecursive a)+  C_Rec      :: Elems ClassifyUser '[a] -> ClassifyUser (Recursive    a)+  C_Unlifted :: ClassifyUser ContainsUnlifted -    goMaybePairF :: MaybePairF ConcreteClassifier a b -> Int-    goMaybePairF FNothingPair     = 0-    goMaybePairF (FJustPair c c') = go c + go c'+deriving instance Show (ClassifyUser a)  {-------------------------------------------------------------------------------   Values@@ -249,202 +91,153 @@ deriving instance Show (Value a) deriving instance Show (Some Value) -{--------------------------------------------------------------------------------  Equality--------------------------------------------------------------------------------}---- | Check that two classifiers are the same------ If they are the same, additionally return a proof that that means the--- /types/ they classify must be equal (note that equality on the classifiers--- is strictly stronger than equality on the types: for example, non-empty--- and empty lists have different classifiers, but classify the same type).-sameConcreteClassifier ::-     ConcreteClassifier a-  -> ConcreteClassifier b-  -> Maybe (a :~: b)-sameConcreteClassifier = go-  where-    go :: ConcreteClassifier a -> ConcreteClassifier b -> Maybe (a :~: b)-    go CC_Bool     CC_Bool     = Just Refl-    go CC_Char     CC_Char     = Just Refl-    go CC_Double   CC_Double   = Just Refl-    go CC_Float    CC_Float    = Just Refl-    go CC_Int      CC_Int      = Just Refl-    go CC_Int8     CC_Int8     = Just Refl-    go CC_Int16    CC_Int16    = Just Refl-    go CC_Int32    CC_Int32    = Just Refl-    go CC_Int64    CC_Int64    = Just Refl-    go CC_Integer  CC_Integer  = Just Refl-    go CC_Ordering CC_Ordering = Just Refl-    go CC_Unit     CC_Unit     = Just Refl-    go CC_Word     CC_Word     = Just Refl-    go CC_Word8    CC_Word8    = Just Refl-    go CC_Word16   CC_Word16   = Just Refl-    go CC_Word32   CC_Word32   = Just Refl-    go CC_Word64   CC_Word64   = Just Refl--    -- String types+instance Arbitrary (Some Value) where+  arbitrary = do+      -- We don't want to generate large classifiers+      Some (DepGen cc gen) <- SG.run 10 arbitraryConcrete -    go CC_String      CC_String      = Just Refl-    go CC_BS_Strict   CC_BS_Strict   = Just Refl-    go CC_BS_Lazy     CC_BS_Lazy     = Just Refl-    go CC_BS_Short    CC_BS_Short    = Just Refl-    go CC_Text_Strict CC_Text_Strict = Just Refl-    go CC_Text_Lazy   CC_Text_Lazy   = Just Refl+      -- For the values however we want to be able to generate larger trees+      Some . Value cc <$> SG.run 1000 gen -    -- Aeson+{-------------------------------------------------------------------------------+  Constraints+-------------------------------------------------------------------------------} -    go CC_Value CC_Value = Just Refl+class (+    c SimpleType+  , forall a. c a => c (NonRecursive a)+  , forall a. c a => c (Recursive    a)+  , c ContainsUnlifted+  ) => UserSatisfies c -    -- Compound+instance (+    c SimpleType+  , forall a. c a => c (NonRecursive a)+  , forall a. c a => c (Recursive    a)+  , c ContainsUnlifted+  ) => UserSatisfies c -    go (CC_Maybe        c) (CC_Maybe        c') = goMaybeF     c c'-    go (CC_Either       c) (CC_Either       c') = goEitherF    c c'-    go (CC_List         c) (CC_List         c') = goMaybeF     c c'-    go (CC_Ratio        c) (CC_Ratio        c') = goF          c c'-    go (CC_Set          c) (CC_Set          c') = goMaybeF     c c'-    go (CC_Map          c) (CC_Map          c') = goMaybePairF c c'-    go  CC_IntSet           CC_IntSet           = Just Refl-    go (CC_IntMap       c) (CC_IntMap       c') = goMaybeF     c c'-    go (CC_Sequence     c) (CC_Sequence     c') = goMaybeF     c c'-    go (CC_Tree         c) (CC_Tree         c') = goF          c c'-    go (CC_HashSet      c) (CC_HashSet      c') = goF          c c'-    go (CC_HashMap      c) (CC_HashMap      c') = goMaybePairF c c'-    go (CC_HM_Array     c) (CC_HM_Array     c') = goMaybeF     c c'-    go (CC_Vector_Boxed c) (CC_Vector_Boxed c') = goMaybeF     c c'+userSatisfies :: forall c.+     (ClassifiedSatisfies c, c Void, UserSatisfies c)+  => (forall a. ClassifyUser a -> Dict c a)+userSatisfies = go+  where+    go :: ClassifyUser a -> Dict c a+    go  C_Simple    = Dict+    go (C_NonRec c) = goElems c $ Dict+    go (C_Rec    c) = goElems c $ Dict+    go  C_Unlifted  = Dict -    go (CC_Tuple (ConcreteClassifiers cs))-       (CC_Tuple (ConcreteClassifiers cs')) = (\Refl -> Refl) <$> goList cs cs'+    goElems :: SListI as => Elems ClassifyUser as -> (All c as => r) -> r+    goElems (Elems cs) k = case all_NP (hmap goElem cs) of Dict -> k -    -- Reference cells+    goElem :: Elem ClassifyUser a -> Dict c a+    goElem (Elem c) = concreteSatisfies c+    goElem NoElem   = Dict -    go CC_STRef CC_STRef = Just Refl-    go CC_TVar  CC_TVar  = Just Refl-    go CC_MVar  CC_MVar  = Just Refl+concreteSatisfies ::+     (ClassifiedSatisfies c, c Void, UserSatisfies c)+  => ConcreteClassifier a -> Dict c a+concreteSatisfies = classifiedSatisfies userSatisfies -    -- Functions+canShowConcrete :: ConcreteClassifier a -> Dict Show a+canShowConcrete = concreteSatisfies -    go CC_Fun CC_Fun = Just Refl+canCompareConcrete :: ConcreteClassifier a -> Dict Eq a+canCompareConcrete = concreteSatisfies -    -- User-defined+{-------------------------------------------------------------------------------+  Size of the classifier -    go  CC_User_Simple       CC_User_Simple       = Just Refl-    go (CC_User_NonRec   c) (CC_User_NonRec   c') = goMaybeF c c'-    go (CC_User_Rec      c) (CC_User_Rec      c') = goMaybeF c c'-    go  CC_User_Unlifted     CC_User_Unlifted     = Just Refl+  Mostly used for sanity checking the generator+-------------------------------------------------------------------------------} -    -- Otherwise, not equal+sizeUser :: ClassifyUser a -> Int+sizeUser = go+  where+    go :: ClassifyUser a -> Int+    go  C_Simple    = 1+    go (C_NonRec c) = 1 + goElems c+    go (C_Rec    c) = 1 + goElems c+    go  C_Unlifted  = 1 -    go _ _ = Nothing+    goElems :: SListI as => Elems ClassifyUser as -> Int+    goElems (Elems cs) = sum . hcollapse $ hmap (K . goElem) cs -    goMaybeF ::-         MaybeF ConcreteClassifier x-      -> MaybeF ConcreteClassifier x'-      -> Maybe (f x :~: f x')-    goMaybeF FNothing  FNothing   = Just Refl-    goMaybeF (FJust x) (FJust x') = (\Refl -> Refl) <$> go x x'-    goMaybeF _          _         = Nothing+    goElem :: Elem ClassifyUser a -> Int+    goElem NoElem   = 0+    goElem (Elem c) = sizeConcrete c -    goEitherF ::-         EitherF ConcreteClassifier x  y-      -> EitherF ConcreteClassifier x' y'-      -> Maybe (f x y :~: f x' y')-    goEitherF (FLeft  x) (FLeft  x') = (\Refl -> Refl) <$> go x x'-    goEitherF (FRight y) (FRight y') = (\Refl -> Refl) <$> go y y'-    goEitherF (FLeft  _) (FRight _ ) = Nothing-    goEitherF (FRight _) (FLeft  _ ) = Nothing+sizeConcrete :: ConcreteClassifier a -> Int+sizeConcrete = classifierSize_ sizeUser -    goF ::-         ConcreteClassifier x-      -> ConcreteClassifier x'-      -> Maybe (f x :~: f x')-    goF x x' = (\Refl -> Refl) <$> go x x'+{-------------------------------------------------------------------------------+  Same classifier+-------------------------------------------------------------------------------} -    goMaybePairF ::-         MaybePairF ConcreteClassifier x  y-      -> MaybePairF ConcreteClassifier x' y'-      -> Maybe (f x y :~: f x' y')-    goMaybePairF FNothingPair    FNothingPair      = Just Refl-    goMaybePairF (FJustPair x y) (FJustPair x' y') = (\Refl Refl -> Refl) <$> go x x' <*> go y y'-    goMaybePairF _               _                 = Nothing+-- | Check that two classifiers are the same+sameConcrete ::+     ConcreteClassifier a+  -> ConcreteClassifier b+  -> Maybe (a :~: b)+sameConcrete = sameClassifier_ sameUser -    goList ::-         NP ConcreteClassifier xs-      -> NP ConcreteClassifier ys-      -> Maybe (xs :~: ys)-    goList Nil       Nil       = Just Refl-    goList (x :* xs) (y :* ys) = (\Refl Refl -> Refl) <$> go x y <*> goList xs ys-    goList Nil       (_ :* _)  = Nothing-    goList (_ :* _)  Nil       = Nothing+sameUser :: ClassifyUser a -> ClassifyUser b -> Maybe (a :~: b)+sameUser = go+  where+    go :: ClassifyUser a -> ClassifyUser b -> Maybe (a :~: b)+    go  C_Simple     C_Simple     = Just Refl+    go (C_NonRec c) (C_NonRec c') = sameElems sameUser c c' $ Refl+    go (C_Rec    c) (C_Rec    c') = sameElems sameUser c c' $ Refl+    go  C_Unlifted   C_Unlifted   = Just Refl+    go  _            _            = Nothing -    -- Make sure we get a warning if we add another constructor-    _checkAllCases :: ConcreteClassifier a -> ()+    _checkAllCases :: ClassifyUser a -> ()     _checkAllCases = \case-        -- Primitive types--        CC_Bool     -> ()-        CC_Char     -> ()-        CC_Double   -> ()-        CC_Float    -> ()-        CC_Int      -> ()-        CC_Int8     -> ()-        CC_Int16    -> ()-        CC_Int32    -> ()-        CC_Int64    -> ()-        CC_Integer  -> ()-        CC_Ordering -> ()-        CC_Unit     -> ()-        CC_Word     -> ()-        CC_Word8    -> ()-        CC_Word16   -> ()-        CC_Word32   -> ()-        CC_Word64   -> ()--        -- String types--        CC_String      -> ()-        CC_BS_Strict   -> ()-        CC_BS_Lazy     -> ()-        CC_BS_Short    -> ()-        CC_Text_Strict -> ()-        CC_Text_Lazy   -> ()--        -- Aeson--        CC_Value -> ()--        -- Compound+        C_Simple{}   -> ()+        C_NonRec{}   -> ()+        C_Rec{}      -> ()+        C_Unlifted{} -> () -        CC_Maybe{}        -> ()-        CC_Either{}       -> ()-        CC_List{}         -> ()-        CC_Ratio{}        -> ()-        CC_Set{}          -> ()-        CC_Map{}          -> ()-        CC_IntSet{}       -> ()-        CC_IntMap{}       -> ()-        CC_Tuple{}        -> ()-        CC_Sequence{}     -> ()-        CC_Tree{}         -> ()-        CC_HashSet{}      -> ()-        CC_HashMap{}      -> ()-        CC_HM_Array{}     -> ()-        CC_Vector_Boxed{} -> ()+{-------------------------------------------------------------------------------+  Arbitrary+-------------------------------------------------------------------------------} -        -- Reference cells+arbitraryUser :: SizedGen (Some (DepGen ClassifyUser))+arbitraryUser = SG.leafOrStep leaf compound+  where+    leaf :: Gen (Some (DepGen ClassifyUser))+    leaf = oneof [+          -- SimpleType+          pure . Some $ arbitraryDepGen C_Simple -        CC_STRef -> ()-        CC_TVar  -> ()-        CC_MVar  -> ()+          -- ContainsUnlifted+        , pure . Some $ arbitraryDepGen C_Unlifted+        ] -        -- Functions+    compound :: [SizedGen (Some (DepGen ClassifyUser))]+    compound = [+          -- NonRecursive+          go_U_K C_NonRec (NR1 1234)+            (mapSome (GenK (fmap (NR2 True))) <$> arbitraryConcrete) -        CC_Fun -> ()+          -- Recursive+        , go_U_K C_Rec RNil+            (mapSome (GenK (SG.genListLike recursiveFromList)) <$> arbitraryConcrete)+        ] -        -- User-defined+    go_U_K ::+         ( forall x. Show x => Show (f x)+         , forall x. Eq   x => Eq   (f x)+         )+      => (forall a. Elems ClassifyUser '[a] -> ClassifyUser (f a))+      -> f Void+      -> SizedGen (Some (GenK ConcreteClassifier f))+      -> SizedGen (Some (DepGen ClassifyUser))+    go_U_K cf nothing just =+        SG.leafOrStep+          (pure $ Some $ DepGen (cf ElemU) (pure nothing))+          [(\(Some a) -> Some (genJust (cf . ElemK) a)) <$> just] -        CC_User_Simple{}   -> ()-        CC_User_NonRec{}   -> ()-        CC_User_Rec{}      -> ()-        CC_User_Unlifted{} -> ()+arbitraryConcrete :: SizedGen (Some (DepGen ConcreteClassifier))+arbitraryConcrete = arbitraryClassifier_ arbitraryUser
+ tests/Test/RecoverRTTI/Globals.hs view
@@ -0,0 +1,62 @@+-- | Global mutable variables+--+-- The tests are entirely pure, but occassionally needs examples of these+-- mutable structures. Having a single global example available is convenient.+module Test.RecoverRTTI.Globals (+    exampleIORef+  , exampleSTRef+  , exampleMVar+  , exampleTVar+  , examplePrimArrayM+  , exampleStorableVectorM+  , examplePrimitiveVectorM+  ) where++import Control.Concurrent.MVar (newEmptyMVar)+import Control.Concurrent.STM (newTVarIO)+import Control.Monad.ST.Unsafe (unsafeSTToIO)+import Data.IORef (newIORef)+import Data.STRef (newSTRef)+import System.IO.Unsafe (unsafePerformIO)+import Unsafe.Coerce (unsafeCoerce)++import qualified Data.Primitive.Array  as Prim.Array+import qualified Data.Vector.Primitive as Vector.Primitive+import qualified Data.Vector.Storable  as Vector.Storable++import Debug.RecoverRTTI++exampleIORef :: SomeSTRef+{-# NOINLINE exampleIORef #-}+exampleIORef = unsafePerformIO $+    -- IORef is indistinguishable from STRef on the heap+    unsafeCoerce <$> newIORef (unsafeCoerce ())++exampleSTRef :: SomeSTRef+exampleSTRef = unsafePerformIO $ unsafeSTToIO $+    unsafeCoerce <$> newSTRef (unsafeCoerce ())++exampleMVar :: SomeMVar+{-# NOINLINE exampleMVar #-}+exampleMVar = unsafePerformIO $+    SomeMVar <$> newEmptyMVar++exampleTVar :: SomeTVar+{-# NOINLINE exampleTVar #-}+exampleTVar = unsafePerformIO $+    SomeTVar <$> newTVarIO (unsafeCoerce ())++examplePrimArrayM :: SomePrimArrayM+{-# NOINLINE examplePrimArrayM #-}+examplePrimArrayM = unsafePerformIO $+    unsafeCoerce <$> Prim.Array.newArray 0 (error "no elements")++exampleStorableVectorM :: SomeStorableVectorM+{-# NOINLINE exampleStorableVectorM #-}+exampleStorableVectorM = unsafePerformIO $+    unsafeCoerce <$> Vector.Storable.thaw (Vector.Storable.fromList "abc")++examplePrimitiveVectorM :: SomePrimitiveVectorM+{-# NOINLINE examplePrimitiveVectorM #-}+examplePrimitiveVectorM = unsafePerformIO $+    unsafeCoerce <$> Vector.Primitive.thaw (Vector.Primitive.fromList "abc")
− tests/Test/RecoverRTTI/Orphans.hs
@@ -1,22 +0,0 @@-module Test.RecoverRTTI.Orphans () where--import Data.Function (on)--import qualified Data.HashMap.Internal.Array as HashMap (Array)-import qualified Data.HashMap.Internal.Array as HashMap.Array--import Debug.RecoverRTTI---- | Degenerate 'Eq' instance for functions that always says 'True'------ When we compare values up to the coercion returned by 'reclassify', we need--- an 'Eq' instance. We can't compare functions in any meaningful way though,--- and so we just return 'True' here no matter what.------ This is an orphan defined in the test suite only, so that users of the--- library don't have acccess to this (misleading) instance.-instance Eq SomeFun where-  _ == _ = True--instance Eq a => Eq (HashMap.Array a) where-  (==) = (==) `on` HashMap.Array.toList
+ tests/Test/RecoverRTTI/Prim.hs view
@@ -0,0 +1,292 @@+{-# LANGUAGE GADTs             #-}+{-# LANGUAGE LambdaCase        #-}+{-# LANGUAGE OverloadedStrings #-}++module Test.RecoverRTTI.Prim (+    -- * Equality+    canComparePrim+    -- * Arbitrary+  , primSatisfiesArbitrary+  , arbitraryPrimClassifier+  ) where++import Control.Monad+import Data.SOP.Dict+import Unsafe.Coerce (unsafeCoerce)++import qualified Data.Aeson            as Aeson+import qualified Data.ByteString       as BS.Strict+import qualified Data.ByteString.Lazy  as BS.Lazy+import qualified Data.ByteString.Short as BS.Short+import qualified Data.Text             as Text.Strict+import qualified Data.Text.Lazy        as Text.Lazy+import qualified Data.Vector           as Vector.Boxed+import qualified Data.Vector.Storable  as Vector.Storable+import qualified Data.Vector.Primitive as Vector.Primitive++import Debug.RecoverRTTI++import Test.QuickCheck++import Test.RecoverRTTI.Globals++{-------------------------------------------------------------------------------+  Equality+-------------------------------------------------------------------------------}++canComparePrim :: PrimClassifier a -> Dict Eq a+canComparePrim = primSatisfies++{-------------------------------------------------------------------------------+  Arbitrary support for the primitive types+-------------------------------------------------------------------------------}++primSatisfiesArbitrary :: PrimClassifier a -> Dict Arbitrary a+primSatisfiesArbitrary = primSatisfies++arbitraryPrimClassifier :: Gen (Some PrimClassifier)+arbitraryPrimClassifier = elements [+    -- Primitive types++      Some C_Bool+    , Some C_Char+    , Some C_Double+    , Some C_Float+    , Some C_Int+    , Some C_Int16+    , Some C_Int8+    , Some C_Int32+    , Some C_Int64+    , Some C_Integer+    , Some C_Ordering+    , Some C_Unit+    , Some C_Word+    , Some C_Word8+    , Some C_Word16+    , Some C_Word32+    , Some C_Word64++    -- String types++    , Some C_String+    , Some C_BS_Strict+    , Some C_BS_Lazy+    , Some C_BS_Short+    , Some C_Text_Strict+    , Some C_Text_Lazy++    -- Aeson++    , Some C_Value++    -- Reference cells++    , Some C_STRef+    , Some C_TVar+    , Some C_MVar++    -- Functions++    , Some C_Fun++    -- Containers with no type arguments++    , Some C_IntSet+    , Some C_Prim_ArrayM+    , Some C_Vector_Storable+    , Some C_Vector_StorableM+    , Some C_Vector_Primitive+    , Some C_Vector_PrimitiveM+    ]+  where+    _checkAllCases :: PrimClassifier a -> ()+    _checkAllCases = \case+        -- Primitive types++        C_Bool     -> ()+        C_Char     -> ()+        C_Double   -> ()+        C_Float    -> ()+        C_Int      -> ()+        C_Int16    -> ()+        C_Int8     -> ()+        C_Int32    -> ()+        C_Int64    -> ()+        C_Integer  -> ()+        C_Ordering -> ()+        C_Unit     -> ()+        C_Word     -> ()+        C_Word8    -> ()+        C_Word16   -> ()+        C_Word32   -> ()+        C_Word64   -> ()++        -- String types++        C_String      -> ()+        C_BS_Strict   -> ()+        C_BS_Lazy     -> ()+        C_BS_Short    -> ()+        C_Text_Strict -> ()+        C_Text_Lazy   -> ()++        -- Aeson++        C_Value -> ()++        -- Reference cells++        C_STRef -> ()+        C_TVar  -> ()+        C_MVar  -> ()++        -- Functions++        C_Fun -> ()++        -- Containers with no type arguments++        C_IntSet            -> ()+        C_Prim_ArrayM       -> ()+        C_Vector_Storable   -> ()+        C_Vector_StorableM  -> ()+        C_Vector_Primitive  -> ()+        C_Vector_PrimitiveM -> ()++{-------------------------------------------------------------------------------+  Orphan instances+-------------------------------------------------------------------------------}++instance Arbitrary BS.Strict.ByteString where+  arbitrary = BS.Strict.pack <$> arbitrary++instance Arbitrary BS.Lazy.ByteString where+  arbitrary = BS.Lazy.pack <$> arbitrary++instance Arbitrary BS.Short.ShortByteString where+  arbitrary = BS.Short.pack <$> arbitrary++instance Arbitrary Text.Strict.Text where+  arbitrary = Text.Strict.pack <$> arbitrary++instance Arbitrary Text.Lazy.Text where+  arbitrary = Text.Lazy.pack <$> arbitrary++instance Arbitrary Aeson.Value where+  arbitrary = choose (0, 10) >>= go+    where+      go :: Int -> Gen Aeson.Value+      go 0  = oneof nonRecursive+      go sz = oneof (nonRecursive ++ recursive sz)++      nonRecursive :: [Gen Aeson.Value]+      nonRecursive = [+            Aeson.String . Text.Strict.pack <$> arbitrary+          , Aeson.Number . fromInteger <$> arbitrary+          , Aeson.Bool <$> arbitrary+          , return Aeson.Null+          ]++      recursive :: Int -> [Gen Aeson.Value]+      recursive sz = [+            do n <- choose (0, 5)+               Aeson.Array . Vector.Boxed.fromList <$> replicateM n (go (sz `div` n))+          , do n <- choose (0, 5)+               Aeson.object <$> replicateM n (+                       (Aeson..=)+                   <$> fieldName+                   <*> go (sz `div` n)+                 )+          ]++      -- We're not interested in testing crazy values+      fieldName :: Gen Text.Strict.Text+      fieldName = elements ["a", "b", "c"]++-- | Rather than trying to be clever here, we just generate a handful of+-- examples in different categories.+instance Arbitrary SomeFun where+  arbitrary = elements [+        -- Parametrically polymorphic function+        fun (id    :: Int -> Int)+      , fun (const :: Int -> Bool -> Int)+        -- Ad-hoc polymorphic function+      , fun (negate :: Int -> Int)+      , fun ((+)    :: Int -> Int -> Int)+        -- Partial application+      , fun (const 1 :: Bool -> Int)+      , fun ((+)   1 :: Int -> Int)+      ]+    where+      fun :: (a -> b) -> SomeFun+      fun = unsafeCoerce++instance Arbitrary SomeStorableVector where+  arbitrary = elements [+        some $ Vector.Storable.fromList ([1, 2, 3] :: [Int])+      , some $ Vector.Storable.fromList ("abc"     :: String)+      ]+    where+      some :: Vector.Storable.Vector a -> SomeStorableVector+      some = unsafeCoerce++instance Arbitrary SomePrimitiveVector where+  arbitrary = elements [+        some $ Vector.Primitive.fromList ([1, 2, 3] :: [Int])+      , some $ Vector.Primitive.fromList ("abc"     :: String)+      ]+    where+      some :: Vector.Primitive.Vector a -> SomePrimitiveVector+      some = unsafeCoerce++{-------------------------------------------------------------------------------+  For the mutable variables, we just use the one global example+-------------------------------------------------------------------------------}++instance Arbitrary SomeSTRef where+  arbitrary = return exampleSTRef++instance Arbitrary SomeTVar where+  arbitrary = return exampleTVar++instance Arbitrary SomeMVar where+  arbitrary = return exampleMVar++instance Arbitrary SomePrimArrayM where+  arbitrary = return examplePrimArrayM++instance Arbitrary SomeStorableVectorM where+  arbitrary = return exampleStorableVectorM++instance Arbitrary SomePrimitiveVectorM where+  arbitrary = return examplePrimitiveVectorM++{-------------------------------------------------------------------------------+  Orphan equality instances+-------------------------------------------------------------------------------}++-- | Degenerate 'Eq' instance for functions that always says 'True'+--+-- When we compare values up to the coercion returned by 'reclassify', we need+-- an 'Eq' instance. We can't compare functions in any meaningful way though,+-- and so we just return 'True' here no matter what.+--+-- This is an orphan defined in the test suite only, so that users of the+-- library don't have acccess to this (misleading) instance.+instance Eq SomeFun where+  _ == _ = True++instance Eq SomePrimArrayM where+  _ == _ = True++instance Eq SomeStorableVector where+  _ == _ = True++instance Eq SomeStorableVectorM where+  _ == _ = True++instance Eq SomePrimitiveVector where+  _ == _ = True++instance Eq SomePrimitiveVectorM where+  _ == _ = True
+ tests/Test/RecoverRTTI/QuickCheck/DepGen.hs view
@@ -0,0 +1,163 @@+{-# LANGUAGE DataKinds             #-}+{-# LANGUAGE FlexibleContexts      #-}+{-# LANGUAGE GADTs                 #-}+{-# LANGUAGE KindSignatures        #-}+{-# LANGUAGE NamedFieldPuns        #-}+{-# LANGUAGE QuantifiedConstraints #-}+{-# LANGUAGE RankNTypes            #-}+{-# LANGUAGE ScopedTypeVariables   #-}+{-# LANGUAGE TypeApplications      #-}+{-# LANGUAGE UndecidableInstances  #-}++module Test.RecoverRTTI.QuickCheck.DepGen (+    -- * Dependent generator+    DepGen(..)+  , depClassifier+  , depGen+    -- * Creation+  , arbitraryDepGen+  , primDepGen+    -- * Bundle a dependent generator with a lifting function+  , GenK(..)+  , GenKU(..)+  , GenUK(..)+  , GenKK(..)+  , GenNP(..)+  , genJust+  , genLeft+  , genRight+  , genPair+  , genNP+  ) where++import Data.Kind+import Data.SOP+import Data.SOP.Dict+import Data.Void++import Debug.RecoverRTTI++import Test.QuickCheck++import Test.RecoverRTTI.Prim+import Test.RecoverRTTI.QuickCheck.Sized (SizedGen)++import qualified Test.RecoverRTTI.QuickCheck.Sized as SG++{-------------------------------------------------------------------------------+  Dependent generator+-------------------------------------------------------------------------------}++-- | Dependent generator+data DepGen c a where+  DepGen :: (Show a, Eq a) => c a -> SizedGen a -> DepGen c a++depClassifier :: DepGen c a -> c a+depClassifier (DepGen c _) = c++depGen :: DepGen c a -> SizedGen a+depGen (DepGen _ gen) = gen++{-------------------------------------------------------------------------------+  Construction+-------------------------------------------------------------------------------}++arbitraryDepGen :: (Arbitrary a, Show a, Eq a) => c a -> DepGen c a+arbitraryDepGen cc = DepGen cc $ SG.arbitrary++primDepGen :: PrimClassifier a -> DepGen (Classifier_ o) a+primDepGen C_String = DepGen (C_Prim C_String) $ SG.lift $+    arbitrary `suchThat` (not . null) -- empty string classified as @[Void]@+primDepGen c =+    case (primSatisfiesArbitrary c, canShowPrim c, canComparePrim c) of+      (Dict, Dict, Dict) -> arbitraryDepGen (C_Prim c)++{-------------------------------------------------------------------------------+  Bundle a dependent generator with a lifting function++  These are designed to work with 'MaybeF' and co.+-------------------------------------------------------------------------------}++data GenK c (f :: Type -> Type) a = GenK {+      justGen  :: SizedGen a -> SizedGen (f a)+    , justElem :: DepGen c a+    }++data GenKU c (f :: Type -> Type -> Type) a = GenKU {+      leftGen  :: SizedGen a -> SizedGen (f a Void)+    , leftElem :: DepGen c a+    }++data GenUK c (f :: Type -> Type -> Type) b = GenUK {+      rightGen  :: SizedGen b -> SizedGen (f Void b)+    , rightElem :: DepGen c b+    }++data GenKK c (f :: Type -> Type -> Type) (ab :: (Type, Type)) where+    GenKK :: forall c f a b. {+           pairGen :: SizedGen a -> SizedGen b -> SizedGen (f a b)+         , pairFst :: DepGen c a+         , pairSnd :: DepGen c b+         }+      -> GenKK c f '(a, b)++data GenNP c f xs = GenNP {+      npGen  :: NP SizedGen xs -> SizedGen (f xs)+    , npElem :: NP (DepGen c) xs+    }++genJust ::+     ( forall x. Show x => Show (f x)+     , forall x. Eq   x => Eq   (f x)+     )+  => (c a -> c' (f a)) -> GenK c f a -> DepGen c' (f a)+genJust cf (GenK gen (DepGen cx gx)) =+    DepGen (cf cx) (gen gx)++genLeft ::+     ( forall x y. (Show x, Show y) => Show (f x y)+     , forall x y. (Eq   x, Eq   y) => Eq   (f x y)+     )+  => (c a -> c' (f a Void)) -> GenKU c f a -> DepGen c' (f a Void)+genLeft cf (GenKU gen (DepGen cx gx)) =+    DepGen (cf cx) (gen gx)++genRight ::+     ( forall x y. (Show x, Show y) => Show (f x y)+     , forall x y. (Eq   x, Eq   y) => Eq   (f x y)+     )+  => (c b -> c' (f Void b)) -> GenUK c f b -> DepGen c' (f Void b)+genRight cf (GenUK gen (DepGen cy gy)) =+    DepGen (cf cy) (gen gy)++genPair ::+     ( forall x y. (Show x, Show y) => Show (f x y)+     , forall x y. (Eq   x, Eq   y) => Eq   (f x y)+     )+  => ((c a, c b) -> c' (f a b)) -> GenKK c f '(a, b) -> DepGen c' (f a b)+genPair cf (GenKK gen (DepGen cx gx) (DepGen cy gy)) =+    DepGen (cf (cx, cy)) $+      gen (SG.withSize (`div` 2) gx)+          (SG.withSize (`div` 2) gy)++genNP :: forall c c' f xs.+     ( SListI xs+     , All Show xs => Show (f xs )+     , All Eq   xs => Eq   (f xs)+     )+  => (NP c xs -> c' (f xs)) -> GenNP c f xs -> DepGen c' (f xs)+genNP cf (GenNP gen elems) =+    case (all_NP allShow, all_NP allEq) of+      (Dict, Dict) ->+        DepGen+          (cf (hmap depClassifier elems))+          (gen (hmap (SG.withSize divSize . depGen) elems))+  where+    divSize :: Int -> Int+    divSize sz = (sz - 1) `div` lengthSList (Proxy @xs)++    allShow :: NP (Dict Show) xs+    allShow = hmap (\DepGen{} -> Dict) elems++    allEq :: NP (Dict Eq) xs+    allEq = hmap (\DepGen{} -> Dict) elems
+ tests/Test/RecoverRTTI/QuickCheck/Sized.hs view
@@ -0,0 +1,153 @@+{-# LANGUAGE DataKinds                  #-}+{-# LANGUAGE DeriveFunctor              #-}+{-# LANGUAGE DerivingStrategies         #-}+{-# LANGUAGE FlexibleContexts           #-}+{-# LANGUAGE GADTs                      #-}+{-# LANGUAGE GeneralizedNewtypeDeriving #-}+{-# LANGUAGE KindSignatures             #-}+{-# LANGUAGE RankNTypes                 #-}+{-# LANGUAGE ScopedTypeVariables        #-}+{-# LANGUAGE TypeApplications           #-}+{-# LANGUAGE TypeOperators              #-}++-- | Sized generators+--+-- Intended for qualified import+--+-- > import Test.RecoverRTTI.QuickCheck.Sized (SizedGen)+-- > import qualified Test.RecoverRTTI.QuickCheck.Sized as SG+module Test.RecoverRTTI.QuickCheck.Sized (+    -- * Sized generators+    SizedGen(..)+  , run+    -- * Lifting @Gen@ into @SizedGen@+  , lift+  , arbitrary+    -- * Combinators+  , suchThat+  , withSize+  , leafOrStep+  , oneofStepped+  , replicate+  , divvy+  , divvyPair+    -- ** Derived+  , genListLike+  , genMapLike+    -- ** Support for tuples+  , ValidTuple(..)+  , genTuple+  ) where++import Prelude hiding (replicate)++import Data.Kind+import Data.SOP+import Data.SOP.Dict++import Debug.RecoverRTTI++import Test.QuickCheck (Arbitrary, Gen)++import qualified Test.QuickCheck as QC++{-------------------------------------------------------------------------------+  Sized generators+-------------------------------------------------------------------------------}++-- | Sized generators+--+-- We thread the size all the way through the generating, to avoid generating+-- very big trees. This is nonetheless a naive approach; we might want to look+-- at papers such as "Feat: Functional Enumeration of Algebraic Types".+newtype SizedGen a = SizedGen { unSizedGen :: Int -> Gen a }+  deriving (Functor)++instance Applicative SizedGen where+  pure x  = SizedGen $ \_sz -> pure x+  f <*> x = SizedGen $ \ sz -> unSizedGen f sz <*> unSizedGen x sz++run :: Int -> SizedGen a -> Gen a+run n (SizedGen gen) = gen n++{-------------------------------------------------------------------------------+  Lifting @Gen@ into @SizedGen@+-------------------------------------------------------------------------------}++lift :: Gen a -> SizedGen a+lift gen = SizedGen $ \_ -> gen++arbitrary :: Arbitrary a => SizedGen a+arbitrary = lift QC.arbitrary++{-------------------------------------------------------------------------------+  Combinators+-------------------------------------------------------------------------------}++suchThat :: SizedGen a -> (a -> Bool) -> SizedGen a+gen `suchThat` p = SizedGen $ \sz -> unSizedGen gen sz `QC.suchThat` p++withSize :: (Int -> Int) -> SizedGen a -> SizedGen a+withSize f gen = SizedGen $ unSizedGen gen . f++leafOrStep :: Gen a -> [SizedGen a] -> SizedGen a+leafOrStep leaf nested = SizedGen $ \sz ->+    if sz > 1+      then QC.oneof (map (run (sz - 1)) nested)+      else leaf++oneofStepped :: [SizedGen a] -> SizedGen a+oneofStepped gens = SizedGen $ \sz -> QC.oneof $ map (run (sz - 1)) gens++replicate :: (Int, Int) -> SizedGen a -> SizedGen [a]+replicate (lo, hi) gen = SizedGen $ \sz -> do+    n <- QC.choose (lo, max lo (min sz hi))+    let sz' = (sz - 1) `div` n+    QC.vectorOf n $ run sz' gen++divvy :: forall xs. SListI xs => NP SizedGen xs -> SizedGen (NP I xs)+divvy = hsequence . hmap (withSize (`div` n))+  where+    n = lengthSList (Proxy @xs)++divvyPair :: SizedGen a -> SizedGen b -> SizedGen (a, b)+divvyPair ga gb = unwrapTuple . tupleFromNP <$> divvy (ga :* gb :* Nil)++{-------------------------------------------------------------------------------+  Derived combinators+-------------------------------------------------------------------------------}++genListLike :: ([a] -> x) -> SizedGen a -> SizedGen x+genListLike f = fmap f . replicate (1, 5)++genMapLike :: ([(a, b)] -> x) -> SizedGen a -> SizedGen b -> SizedGen x+genMapLike f genA genB = fmap f $ replicate (1, 5) $ divvyPair genA genB++{-------------------------------------------------------------------------------+  Support for tuples+-------------------------------------------------------------------------------}++data ValidTuple f (xs :: [Type]) where+  ValidTuple :: (SListI xs, IsValidSize (Length xs)) => NP f xs -> ValidTuple f x++-- | Generate arbitrary tuple+--+-- Precondition: the generator must be able to generate values for @size >= 1@.+genTuple :: forall f. SizedGen (Some f) -> SizedGen (Some (ValidTuple f))+genTuple gen = SizedGen $ \sz -> do+    -- Pick no less than 2, and no more than 62+    n <- QC.choose (2, max 2 (min 62 sz))+    case toValidSize n of+      Nothing -> error "impossible, we pick a valid tuple size"+      Just (Some validSize@(ValidSize n' _)) ->+        case liftValidSize validSize of+          Dict -> go (sz `div` n) n' $ return . Some . ValidTuple+  where+    go :: Int+       -> SNat n+       -> (forall xs. (SListI xs, Length xs ~ n) => NP f xs -> Gen r)+       -> Gen r+    go _    SZ     k = k Nil+    go sz' (SS s) k = go sz' s $ \xs -> do+        Some x <- run sz' gen+        k $ (x :* xs)
tests/Test/RecoverRTTI/Sanity.hs view
@@ -1,5 +1,3 @@-{-# LANGUAGE NamedFieldPuns #-}- module Test.RecoverRTTI.Sanity (tests) where  import Debug.RecoverRTTI@@ -7,9 +5,11 @@ import Test.Tasty import Test.Tasty.QuickCheck -import Test.RecoverRTTI.Arbitrary import Test.RecoverRTTI.ConcreteClassifier+import Test.RecoverRTTI.QuickCheck.DepGen +import qualified Test.RecoverRTTI.QuickCheck.Sized as SG+ tests :: TestTree tests = testGroup "Test.RecoverRTTI.Sanity" [      testProperty "typeSize" prop_typeSize@@ -17,8 +17,8 @@  prop_typeSize :: Property prop_typeSize =-    forAll (Blind <$> arbitraryClassifiedGen 10) $-      \(Blind (Some ClassifiedGen{genClassifier})) ->-          counterexample ("classifier: " ++ show genClassifier)-        $ counterexample ("size: " ++ show (classifierSize genClassifier))-        $ classifierSize genClassifier <= 100+    forAll (Blind <$> SG.run 10 arbitraryConcrete) $+      \(Blind (Some (DepGen classifier _))) ->+          counterexample ("classifier: " ++ show classifier)+        $ counterexample ("size: " ++ show (sizeConcrete classifier))+        $ sizeConcrete classifier <= 100
tests/Test/RecoverRTTI/Show.hs view
@@ -8,7 +8,6 @@  import Debug.RecoverRTTI -import Test.RecoverRTTI.Arbitrary () import Test.RecoverRTTI.ConcreteClassifier  tests :: TestTree@@ -29,5 +28,5 @@ prop_anythingToString :: Some Value -> Property prop_anythingToString (Some (Value _cc x)) =       counterexample ("inferred: " ++ show (classify x))-    $ within 1_000_000+    $ within 2_000_000     $ show x === anythingToString x
tests/Test/RecoverRTTI/Staged.hs view
@@ -1,13 +1,8 @@ {-# LANGUAGE DataKinds           #-}-{-# LANGUAGE FlexibleContexts    #-}-{-# LANGUAGE FlexibleInstances   #-} {-# LANGUAGE GADTs               #-}-{-# LANGUAGE KindSignatures      #-}-{-# LANGUAGE PolyKinds           #-} {-# LANGUAGE RankNTypes          #-} {-# LANGUAGE ScopedTypeVariables #-} {-# LANGUAGE TypeApplications    #-}-{-# LANGUAGE TypeOperators       #-}  -- | Staged inference --@@ -28,29 +23,15 @@ -- In this module we do staged inference for the user-defined types used in the -- test suite. The primary purpose of this is to provide evidence that -- 'classify' gives us enough information to do so.-module Test.RecoverRTTI.Staged (-    Reclassified(..)-  , reclassify-  , classifyThenReclassify-  ) where+module Test.RecoverRTTI.Staged (classifyConcrete, reclassify) where  import Control.Monad.Except-import Data.Bifunctor-import Data.HashMap.Lazy (HashMap)-import Data.HashSet (HashSet)-import Data.Map (Map)-import Data.Set (Set) import Data.SOP hiding (NS(..)) import Data.Void-import GHC.Real import Unsafe.Coerce (unsafeCoerce) -import qualified Data.HashMap.Internal.Array as HashMap (Array)-import qualified Data.HashMap.Internal.Array as HashMap.Array-import qualified Data.Map                    as Map-import qualified Data.Set                    as Set- import Debug.RecoverRTTI+import Debug.RecoverRTTI.Classify  import Test.RecoverRTTI.ConcreteClassifier import Test.RecoverRTTI.UserDefined@@ -59,26 +40,10 @@   Reclassified values -------------------------------------------------------------------------------} --- | Reclassified values------ We cannot go directly from a @Classifier a@ to a @ConcreteClassifier a@:--- in the case of a user-defined type, @a@ will be of the form------ > UserDefined c------ for some @c@, but we want to return classifier for a specific type, maybe------ > NonRecursive Char------ Therefore instead we return a classifier for some other type @b@, but along--- with a proof that we can /coerce/ from @a@ to @b@.-data Reclassified a where-    Reclassified :: ConcreteClassifier b -> (a -> b) -> Reclassified a- -- | Classify, then reclassify-classifyThenReclassify :: a -> Except String (Reclassified a)-classifyThenReclassify x =-    case classified x of+classifyConcrete :: a -> Except String (Reclassified ConcreteClassifier a)+classifyConcrete x =+    case classify x of       Left closure ->         throwError $ "Failed to classify closure " ++ show closure       Right classifier ->@@ -87,230 +52,52 @@ -- | Reclassify values -- -- See detailed description in 'Reclassified'.-reclassify :: Classified a -> Except String (Reclassified a)-reclassify = go+reclassify :: Classifier a -> Except String (Reclassified ConcreteClassifier a)+reclassify = fmap distribReclassified . reclassify_ go   where-    go :: Classified a -> Except String (Reclassified a)-    go (Classified c x) = case c of-      -- Primitive types--      C_Bool     -> return $ Reclassified CC_Bool     id-      C_Char     -> return $ Reclassified CC_Char     id-      C_Double   -> return $ Reclassified CC_Double   id-      C_Float    -> return $ Reclassified CC_Float    id-      C_Int      -> return $ Reclassified CC_Int      id-      C_Int8     -> return $ Reclassified CC_Int8     id-      C_Int16    -> return $ Reclassified CC_Int16    id-      C_Int32    -> return $ Reclassified CC_Int32    id-      C_Int64    -> return $ Reclassified CC_Int64    id-      C_Integer  -> return $ Reclassified CC_Integer  id-      C_Ordering -> return $ Reclassified CC_Ordering id-      C_Unit     -> return $ Reclassified CC_Unit     id-      C_Word     -> return $ Reclassified CC_Word     id-      C_Word8    -> return $ Reclassified CC_Word8    id-      C_Word16   -> return $ Reclassified CC_Word16   id-      C_Word32   -> return $ Reclassified CC_Word32   id-      C_Word64   -> return $ Reclassified CC_Word64   id--      -- String types--      C_String      -> return $ Reclassified CC_String      id-      C_BS_Strict   -> return $ Reclassified CC_BS_Strict   id-      C_BS_Lazy     -> return $ Reclassified CC_BS_Lazy     id-      C_BS_Short    -> return $ Reclassified CC_BS_Short    id-      C_Text_Strict -> return $ Reclassified CC_Text_Strict id-      C_Text_Lazy   -> return $ Reclassified CC_Text_Lazy   id--      -- Aeson--      C_Value -> return $ Reclassified CC_Value id--      -- Compound--      C_Maybe        c' -> goMaybeF     fmap          CC_Maybe        c'-      C_Either       c' -> goEitherF    bimap         CC_Either       c'-      C_List         c' -> goMaybeF     fmap          CC_List         c'-      C_Ratio        c' -> goF          coerceRatio   CC_Ratio        c'-      C_Set          c' -> goMaybeF     coerceSet     CC_Set          c'-      C_Map          c' -> goMaybePairF coerceMap     CC_Map          c'-      C_IntSet          -> return $ Reclassified CC_IntSet id-      C_IntMap       c' -> goMaybeF     fmap          CC_IntMap       c'-      C_Sequence     c' -> goMaybeF     fmap          CC_Sequence     c'-      C_Tree         c' -> goF          fmap          CC_Tree         c'-      C_HashSet      c' -> goF          coerceHashSet CC_HashSet      c'-      C_HashMap      c' -> goMaybePairF coerceHashMap CC_HashMap      c'-      C_HM_Array     c' -> goMaybeF     coerceHMArray CC_HM_Array     c'-      C_Vector_Boxed c' -> goMaybeF     fmap          CC_Vector_Boxed c'--      C_Tuple (Classifiers cs) ->-        reclassifyTuple <$> (hsequence' (hmap (Comp . reclassify) cs))--      -- Reference cells--      C_STRef -> return $ Reclassified CC_STRef id-      C_TVar  -> return $ Reclassified CC_TVar  id-      C_MVar  -> return $ Reclassified CC_MVar  id--      -- Functions--      C_Fun -> return $ Reclassified CC_Fun id--      -- User-defined--      C_Custom -> do-        let (constr, _args) = fromUserDefined x+    go :: IsUserDefined a -> Except String (Reclassified ClassifyUser a)+    go (IsUserDefined x) =         firstMatch ("Unknown constructor: " ++ constr) [-            reclassifySimple      CC_User_Simple    constr-          , reclassifyTraversable CC_User_NonRec   (constr, x)-          , reclassifyTraversable CC_User_Rec      (constr, x)-          , reclassifySimple      CC_User_Unlifted  constr+            goSimple      C_Simple    constr+          , goTraversable C_NonRec   (constr, x)+          , goTraversable C_Rec      (constr, x)+          , goSimple      C_Unlifted  constr           ]--    goMaybeF :: forall f a.-         (forall x x'. (x -> x') -> f x -> f x')-      -> (forall x. MaybeF ConcreteClassifier x -> ConcreteClassifier (f x))-      -> MaybeF Classified a-      -> Except String (Reclassified (f a))-    goMaybeF _ cc FNothing =-        return $ Reclassified (cc FNothing) id-    goMaybeF coerce cc (FJust x') =-        aux <$> reclassify x'       where-        aux :: Reclassified x -> Reclassified (f x)-        aux (Reclassified c_x f_x) =-            Reclassified (cc (FJust c_x)) (coerce f_x)--    goEitherF :: forall f a b.-         (forall x x' y y'. (x -> x') -> (y -> y') -> f x y -> f x' y')-      -> (forall x y. EitherF ConcreteClassifier x y -> ConcreteClassifier (f x y))-      -> EitherF Classified a b-      -> Except String (Reclassified (f a b))-    goEitherF coerce cc (FLeft x') =-        aux <$> reclassify x'-      where-        aux :: Reclassified x -> Reclassified (f x Void)-        aux (Reclassified c_x f_x) =-            Reclassified (cc (FLeft c_x)) (coerce f_x id)-    goEitherF coerce cc (FRight y') =-        aux <$> reclassify y'-      where-        aux :: Reclassified y -> Reclassified (f Void y)-        aux (Reclassified c_y f_y) =-            Reclassified (cc (FRight c_y)) (coerce id f_y)+        (constr, _args) = fromUserDefined x -    goMaybePairF :: forall f a b.-         (forall x x' y y'. (x -> x') -> (y -> y') -> f x y -> f x' y')-      -> (forall x y. MaybePairF ConcreteClassifier x y -> ConcreteClassifier (f x y))-      -> MaybePairF Classified a b-      -> Except String (Reclassified (f a b))-    goMaybePairF _ cc FNothingPair =-        return $ Reclassified (cc FNothingPair) id-    goMaybePairF coerce cc (FJustPair x' y') =-        aux <$> reclassify x' <*> reclassify y'-      where-        aux :: Reclassified x -> Reclassified y -> Reclassified (f x y)-        aux (Reclassified c_x f_x) (Reclassified c_y f_y) =-            Reclassified (cc (FJustPair c_x c_y)) (coerce f_x f_y)+    -- Reclassification of user-defined types with no arguments+    goSimple ::+         forall a. ConstrsOf a+      => ClassifyUser a+      -> String+      -> Except String (Maybe (Reclassified ClassifyUser UserDefined))+    goSimple c constr =+        if constr `notElem` constrsOf (Proxy @a)+          then return Nothing+          else return . Just $ Reclassified c FromUsr -    goF :: forall f a.-         (forall x x'. (x -> x') -> f x -> f x')-      -> (forall x. ConcreteClassifier x -> ConcreteClassifier (f x))-      -> Classified a-      -> Except String (Reclassified (f a))-    goF coerce cc x' =-        aux <$> reclassify x'+    goTraversable ::+         forall f. (Traversable f, ConstrsOf f)+      => (forall a. Elems ClassifyUser '[a] -> ClassifyUser (f a))+      -> (String, UserDefined)+      -> Except String (Maybe (Reclassified ClassifyUser UserDefined))+    goTraversable cc = \(constr, x) ->+        if constr `notElem` constrsOf (Proxy @f) then+          return Nothing+        else+          case checkEmptyTraversable (coerceToF x) of+            Right _ -> return . Just $ Reclassified (cc ElemU) FromUsr+            Left x' -> Just . aux <$> classifyConcrete x'       where-        aux :: Reclassified x -> Reclassified (f x)-        aux (Reclassified c_x f_x) =-            Reclassified (cc c_x) (coerce f_x)--reclassifyTuple ::-     (SListI xs, IsValidSize (Length xs))-  => NP Reclassified xs -> Reclassified (WrappedTuple xs)-reclassifyTuple = \cs ->-    go cs $ \cs' f ->-      Reclassified (CC_Tuple (ConcreteClassifiers cs')) f-  where-    go :: forall xs r.-         (SListI xs, IsValidSize (Length xs))-      => NP Reclassified xs-      -> (forall ys.-               (SListI ys, Length ys ~ Length xs)-            => NP ConcreteClassifier ys-            -> (WrappedTuple xs -> WrappedTuple ys)-            -> r-         )-      -> r-    go Nil       k = k Nil id-    go (x :* xs) k = smallerIsValid (Proxy @(Length xs)) $-                       go xs $ \np f_np ->-                         case x of-                           Reclassified y f_y ->-                             k (y :* np) (bimapTuple f_y f_np)--{--------------------------------------------------------------------------------  Lift coercions to non-functor types--------------------------------------------------------------------------------}--coerceRatio :: (x -> x') -> Ratio x -> Ratio x'-coerceRatio f (x :% y) = f x :% f y--coerceSet :: (x -> x') -> Set x -> Set x'-coerceSet f = Set.fromDistinctAscList . map f . Set.toAscList--coerceMap :: (x -> x') -> (y -> y') -> Map x y -> Map x' y'-coerceMap f g = Map.fromDistinctAscList . map (bimap f g) . Map.toAscList--coerceHMArray :: (x -> x') -> HashMap.Array x -> HashMap.Array x'-coerceHMArray f arr =-    let xs = HashMap.Array.toList arr-    in HashMap.Array.fromList (length xs) (map f xs)---- Unfortunately, coercion on HashSet/HashMap is not expressible using its API-coerceHashSet :: (x -> x') -> HashSet x -> HashSet x'-coerceHashSet _ = unsafeCoerce--coerceHashMap :: (x -> x') -> (y -> y') -> HashMap x y -> HashMap x' y'-coerceHashMap _ _ = unsafeCoerce--{--------------------------------------------------------------------------------  When we reclassify values of user-defined types with type arguments, we need-  to know that if @c@ is a value of, say, @T a@, it is also a value of @T b@,-  for all @b@. This is what enables staged inference: we know it's a constructor-  of @T x@ for /some/ @x@, and then as a second step figure out what @x@ is.--------------------------------------------------------------------------------}---- | Reclassification of user-defined types with no arguments-reclassifySimple ::-     forall a. ConstrsOf a-  => ConcreteClassifier a-  -> String-  -> Except String (Maybe (Reclassified UserDefined))-reclassifySimple cc constr =-    if constr `notElem` constrsOf (Proxy @a)-      then return Nothing-      else return . Just $ Reclassified cc unsafeCoerce---- | Reclassification of user-defined types with a single argument-reclassifyTraversable ::-     forall f. (Traversable f, ConstrsOf f)-  => (forall a. MaybeF ConcreteClassifier a -> ConcreteClassifier (f a))-  -> (String, UserDefined)-  -> Except String (Maybe (Reclassified UserDefined))-reclassifyTraversable cc = \(constr, x) ->-    if constr `notElem` constrsOf (Proxy @f)-      then return Nothing-      else case checkEmptyTraversable (coerceToF x) of-             Right _ -> return . Just $ Reclassified (cc FNothing) coerceToF-             Left x' -> Just . aux <$> classifyThenReclassify x'-  where-    coerceToF :: forall a. UserDefined -> f a-    coerceToF = unsafeCoerce+        coerceToF :: forall a. UserDefined -> f a+        coerceToF = unsafeCoerce -    aux :: Reclassified a            -- Classification of the elements-        -> Reclassified UserDefined  -- Classification of the container-    aux (Reclassified c f) =-        Reclassified (cc (FJust c)) (fmap f . coerceToF)+        aux ::+             Reclassified ConcreteClassifier a+          -> Reclassified ClassifyUser UserDefined+        aux (Reclassified c pf) =+            Reclassified (cc (ElemK c)) (F1 pf `Compose` FromUsr)  {-------------------------------------------------------------------------------   Auxiliary@@ -322,17 +109,6 @@     go :: [Except e (Maybe a)] -> Except e a     go []     = throwError err     go (x:xs) = x >>= maybe (go xs) return--bimapTuple ::-      ( SListI xs-      , SListI ys-      , IsValidSize (Length (x ': xs))-      , Length xs ~ Length ys-      )-   => (x -> y)-   -> (WrappedTuple xs -> WrappedTuple ys)-   -> WrappedTuple (x ': xs) -> WrappedTuple (y ': ys)-bimapTuple f g (TCons x xs) = TCons (f x) (g xs)  -- | Check if a traversable data structure is empty --
tests/Test/RecoverRTTI/UserDefined.hs view
@@ -35,7 +35,7 @@   deriving (Show, Eq, Generic)  -- | Example of a non-recursive user-defined type-data NonRecursive a = NR1 Int | NR2 a Bool+data NonRecursive a = NR1 Int | NR2 Bool a    deriving (Show, Eq, Generic, Functor, Foldable, Traversable)  -- | Example of a recursive user-defined type