{-# language InstanceSigs, DerivingStrategies #-} {-# language PartialTypeSignatures #-} {-# OPTIONS_GHC -fno-warn-partial-type-signatures #-} {-| This module defines the 'Shaped' typeclass, which is used to generically manipulate values as fixed-points of higher-order functors in order to analyze their structure, e.g. while observing evaluation. If you just care about testing the strictness of functions over datatypes which are already instances of @Shaped@, you don't need to use this module. __Important note:__ To define new instances of 'Shaped' for types which implement 'GHC.Generic', __an empty instance will suffice__, as all the methods of 'Shaped' can be filled in by generic implementations. For example: > import GHC.Generics as GHC > import Generics.SOP as SOP > > data D = C deriving (GHC.Generic) > > instance SOP.Generic D > instance SOP.HasDatatypeInfo D > > instance Shaped D Using the @DeriveAnyClass@ extension, this can be shortened to one line: > data D = C deriving (GHC.Generic, SOP.Generic, SOP.HasDatatypeInfo, Shaped) Manual instances of 'Shaped' are necessary for types which do not or cannot implement GHC's @Generic@ typeclass, such as existential types, abstract types, and GADTs. This module is heavily based upon the approach in "Data.Functor.Foldable", which in turn is modeled after the paper "Functional Programming with Bananas, Lenses, Envelopes and Barbed Wire" (1991) by Erik Meijer, Maarten Fokkinga and Ross Paterson. If you don't yet understand recursion schemes and want to understand this module, it's probably a good idea to familiarize yourself with "Data.Functor.Foldable" before diving into this higher-order generalization. -} module Test.StrictCheck.Shaped ( Shaped(..) , module Test.StrictCheck.Shaped.Flattened -- * Fixed-points of 'Shape's , type (%)(..) -- * Folds and unfolds over fixed-points of @Shape@s , unwrap , interleave , (%) , fuse , translate , fold , unfold , unzipWith -- , reshape -- * Rendering 'Shaped' things as structured text , QName , Rendered(..) , RenderLevel(..) , renderfold -- * Tools for manually writing instances of 'Shaped' -- ** Implementing 'Shaped' for primitive types , Prim(..), unPrim , projectPrim , embedPrim , matchPrim , flatPrim , renderPrim , renderConstant -- ** Implementing 'Shaped' for container types , Containing(..) , projectContainer , embedContainer -- * Generic implementation of the methods of 'Shaped' , GShaped , GShape(..) , gProject , gEmbed , gMatch , gRender ) where import Type.Reflection import Data.Functor.Product import Data.Bifunctor import Data.Bifunctor.Flip import Data.Coerce import Generics.SOP hiding ( Shape ) import Data.Complex -- import Data.List.NonEmpty (NonEmpty(..)) import Test.StrictCheck.Shaped.Flattened -- TODO: provide instances for all of Base -- | When a type @a@ is @Shaped@, we know how to convert it into a -- representation parameterized by an arbitrary functor @f@, so that @Shape a f@ -- (the "shape of @a@ parameterized by @f@") is structurally identical to the -- topmost structure of @a@, but with @f@ wrapped around any subfields of @a@. -- -- Note that this is /not/ a recursive representation! The functor @f@ in -- question wraps the original type of the field and /not/ a @Shape@ of that -- field. -- -- For instance, the @Shape@ of @Either a b@ might be: -- -- > data EitherShape a b f -- > = LeftShape (f a) -- > | RightShape (f b) -- > -- > instance Shaped (Either a b) where -- > type Shape (Either a b) = EitherShape a b -- > ... -- -- The shape of a primitive type should be isomorphic to the primitive type, -- with the functor parameter left unused. class Typeable a => Shaped (a :: *) where -- | The @Shape@ of an @a@ is a type isomorphic to the outermost level of -- structure in an @a@, parameterized by the functor @f@, which is wrapped -- around any fields (of any type) in the original @a@. type Shape a :: (* -> *) -> * type Shape a = GShape a -- | Given a function to expand any @Shaped@ @x@ into an @f x@, expand an @a@ -- into a @Shape a f@ -- -- That is: convert the top-most level of structure in the given @a@ into a -- @Shape@, calling the provided function on each field in the @a@ to produce -- the @f x@ necessary to fill that hole in the produced @Shape a f@. -- -- Inverse to 'embed'. project :: (forall x. Shaped x => x -> f x) -> a -> Shape a f default project :: GShaped a => (forall x. Shaped x => x -> f x) -> a -> Shape a f project = gProject -- | Given a function to collapse any @f x@ into a @Shaped@ @x@, collapse a -- @Shape a f@ into merely an @a@ -- -- That is: eliminate the top-most @Shape@ by calling the provided function on -- each field in that @Shape a f@, and using the results to fill in the pieces -- necessary to build an @a@. -- -- Inverse to 'project'. embed :: (forall x. Shaped x => f x -> x) -> Shape a f -> a default embed :: GShaped a => (forall x. Shaped x => f x -> x) -> Shape a f -> a embed = gEmbed -- | Given two @Shape@s of the same type @a@ but parameterized by potentially -- different functors @f@ and @g@, pattern-match on them to expose a uniform -- view on their fields (a 'Flattened' @(Shape a)@) to a continuation which -- may operate on those fields to produce some result -- -- If the two supplied @Shape@s do not structurally match, only the fields of -- the first are given to the continuation. If they do match, the fields of -- the second are also given, along with type-level proof that the types of -- the two sets of fields align. -- -- This very general operation subsumes equality testing, mapping, zipping, -- shrinking, and many other structural operations over @Shaped@ things. -- -- It is somewhat difficult to manually write instances for this method, but -- consulting its generic implementation 'gMatch' may prove helpful. -- -- See "Test.StrictCheck.Shaped.Flattened" for more information. match :: Shape a f -> Shape a g -> (forall xs. All Shaped xs => Flattened (Shape a) f xs -> Maybe (Flattened (Shape a) g xs) -> result) -> result default match :: GShaped a => Shape a f -> Shape a g -> (forall xs. All Shaped xs => Flattened (Shape a) f xs -> Maybe (Flattened (Shape a) g xs) -> result) -> result match = gMatch -- | Convert a @Shape a@ whose fields are some unknown constant type into a -- 'RenderLevel' filled with that type -- -- This is a specialized pretty-printing mechanism which allows for displaying -- counterexamples in a structured format. See the documentation for -- 'RenderLevel'. render :: Shape a (K x) -> RenderLevel x default render :: (GShaped a, HasDatatypeInfo a) => Shape a (K x) -> RenderLevel x render = gRender -- * Fixed-points of 'Shape's -- | A value of type @f % a@ has the same structure as an @a@, but with the -- structure of the functor @f@ interleaved at every field (including ones of -- types other than @a@). Read this type aloud as "a interleaved with f's". newtype (f :: * -> *) % (a :: *) :: * where Wrap :: f (Shape a ((%) f)) -> f % a -- | Look inside a single level of an interleaved @f % a@. Inverse to the 'Wrap' -- constructor. unwrap :: f % a -> f (Shape a ((%) f)) unwrap (Wrap fs) = fs -- * Folds and unfolds over fixed-points of @Shape@s -- | Map a function across all the fields in a 'Shape' -- -- This function may change the functor over which the @Shape@ is parameterized. -- It can assume recursively that all the fields in the @Shape@ are themselves -- instances of @Shaped@ (which they should be!). This means that you can nest -- calls to @translate@ recursively. translate :: forall a f g. Shaped a => (forall x. Shaped x => f x -> g x) -> Shape a f -> Shape a g translate t d = match @a d d $ \flat _ -> unflatten $ mapFlattened @Shaped t flat -- | The equivalent of a fold (catamorphism) over recursively 'Shaped' values -- -- Given a function which folds an @f@ containing some @Shape x g@ into a @g x@, -- recursively fold any interleaved @f % a@ into a @g a@. fold :: forall a f g. (Functor f, Shaped a) => (forall x. Shaped x => f (Shape x g) -> g x) -> f % a -> g a fold alg = alg . fmap (translate @a (fold alg)) . unwrap -- | The equivalent of an unfold (anamorphism) over recursively 'Shaped' values -- -- Given a function which unfolds an @f x@ into a @g@ containing some @Shape x -- f@, corecursively unfold any @f a@ into an interleaved @g % a@. unfold :: forall a f g. (Functor g, Shaped a) => (forall x. Shaped x => f x -> g (Shape x f)) -> f a -> g % a unfold coalg = Wrap . fmap (translate @a (unfold coalg)) . coalg -- TODO: mapM, foldM, unfoldM, ... -- | Fuse the interleaved @f@-structure out of a recursively interleaved @f % -- a@, given some way of fusing a single level @f x -> x@. -- -- This is a special case of 'fold'. fuse :: (Functor f, Shaped a) => (forall x. f x -> x) -> (f % a -> a) fuse e = e . fold (fmap (embed e)) -- | Interleave an @f@-structure at every recursive level of some @a@, given -- some way of generating a single level of structure @x -> f x@. -- -- This is a special case of 'unfold'. interleave :: (Functor f, Shaped a) => (forall x. x -> f x) -> (a -> f % a) interleave p = unfold (fmap (project p)) . p -- | An infix synonym for 'interleave' (%) :: forall a f. (Functor f, Shaped a) => (forall x. x -> f x) -> a -> f % a (%) = interleave -- | A higher-kinded @unzipWith@, operating over interleaved structures -- -- Given a function splitting some @f x@ into a functor-product @Product g h x@, -- recursively split an interleaved @f % a@ into two interleaved structures: -- one built of @g@-shapes and one of @h@-shapes. -- -- Note that @Product ((%) g) ((%) h) a@ is isomorphic to @(g % a, h % a)@; to -- get the latter, pattern-match on the 'Pair' constructor of 'Product'. unzipWith :: (All Functor [f, g, h], Shaped a) => (forall x. f x -> (g x, h x)) -> (f % a -> (g % a, h % a)) unzipWith split = unPair . fold (crunch . pair . split) where crunch :: forall x g h. (Shaped x, Functor g, Functor h) => Product g h (Shape x (Product ((%) g) ((%) h))) -> Product ((%) g) ((%) h) x crunch = pair . bimap (Wrap . fmap (translate @x (fst . unPair))) (Wrap . fmap (translate @x (snd . unPair))) . unPair pair :: (l x, r x) -> Product l r x pair = uncurry Pair unPair :: Product l r x -> (l x, r x) unPair (Pair lx rx) = (lx, rx) -- | TODO: document this strange function {- reshape :: forall b a f g. (Shaped a, Shaped b, Functor f) => (f (Shape b ((%) g)) -> g (Shape b ((%) g))) -> (forall x. Shaped x => f % x -> g % x) -> f % a -> g % a reshape homo hetero d = case eqTypeRep (typeRep @a) (typeRep @b) of Nothing -> hetero d Just HRefl -> Wrap $ homo . fmap (translate @a (reshape @b homo hetero)) $ unwrap d -} ---------------------------------- -- Rendering shapes for display -- ---------------------------------- -- | Convert an @f % a@ into a structured pretty-printing representation, -- suitable for further display/processing renderfold :: forall a f. (Shaped a, Functor f) => f % a -> Rendered f renderfold = unK . fold oneLevel where oneLevel :: forall x. Shaped x => f (Shape x (K (Rendered f))) -> K (Rendered f) x oneLevel = K . RWrap . fmap (render @x) -- | A @QName@ is a qualified name -- -- Note: -- > type ModuleName = String -- > type DatatypeName = String type QName = (ModuleName, DatatypeName, String) -- | @RenderLevel@ is a functor whose outer shape contains all the information -- about how to pretty-format the outermost @Shape@ of some value. We use -- parametricity to make it difficult to construct incorrect 'render' methods, -- by asking the user merely to produce a single @RenderLevel@ and stitching -- nested @RenderLevel@s into complete 'Rendered' trees. data RenderLevel x = ConstructorD QName [x] -- ^ A prefix constructor, and a list of its fields | InfixD QName Associativity Fixity x x -- ^ An infix constructor, its associativity and fixity, and its two fields | RecordD QName [(QName, x)] -- ^ A record constructor, and a list of its field names paired with fields | CustomD Fixity [Either (Either String (ModuleName, String)) (Fixity, x)] -- ^ A custom pretty-printing representation (i.e. for abstract types), which -- records a fixity and a list of tokens of three varieties: 1) raw strings, -- 2) qualified strings (from some module), or 3) actual fields, annotated -- with their fixity deriving (Eq, Ord, Show, Functor) -- | @Rendered f@ is the fixed-point of @f@ composed with 'RenderLevel': it -- alternates between @f@ shapes and @RenderLevel@s. Usually, @f@ will be the -- identity functor 'I', but not always. data Rendered f = RWrap (f (RenderLevel (Rendered f))) ---------------------------------------------------- -- Tools for manually writing instances of Shaped -- ---------------------------------------------------- -- | The @Shape@ of a spine-strict container (i.e. a @Map@ or @Set@) is the same -- as a container of demands on its elements. However, this does not have the -- right /kind/ to be used as a @Shape@. -- -- The @Containing@ newtype solves this problem. By defining the @Shape@ of some -- container @(C a)@ to be @(C `Containing` a)@, you can use the methods -- @projectContainer@ and @embedContainer@ to implement @project@ and @embed@ -- for your container type (although you will still need to manually define -- @match@ and @render@). newtype Containing h a f = Container (h (f a)) deriving (Eq, Ord, Show) -- | Generic implementation of @project@ for any container type whose @Shape@ -- is represented as a @Containing@ newtype projectContainer :: (Functor c, Shaped a) => (forall x. Shaped x => x -> f x) -> c a -> Containing c a f projectContainer p x = Container (fmap p x) -- | Generic implementation of @embed@ for any container type whose @Shape@ -- is represented as a @Containing@ newtype embedContainer :: (Functor c, Shaped a) => (forall x. Shaped x => f x -> x) -> Containing c a f -> c a embedContainer e (Container x) = fmap e x -- TODO: helper functions for matching and prettying containers -- | The @Shape@ of a primitive type should be equivalent to the type itself. -- However, this does not have the right /kind/ to be used as a @Shape@. -- -- The @Prim@ newtype solves this problem. By defining the @Shape@ of some -- primitive type @p@ to be @Prim p@, you can use the methods @projectPrim@, -- @embedPrim@, @matchPrim@, and @prettyPrim@ to completely fill in the -- definition of the @Shaped@ class for a primitive type. -- -- __Note:__ It is only appropriate to use this @Shape@ representation when a -- type really is primitive, in that it contains no interesting substructure. -- If you use the @Prim@ representation inappropriately, StrictCheck will not be -- able to inspect the richer structure of the type in question. newtype Prim (x :: *) (f :: * -> *) = Prim x deriving (Eq, Ord, Show) deriving newtype (Num) -- | Get the wrapped @x@ out of a @Prim x f@ (inverse to the @Prim@ constructor) unPrim :: Prim x f -> x unPrim (Prim x) = x -- | Generic implementation of @project@ for any primitive type whose @Shape@ is -- is represented as a @Prim@ newtype projectPrim :: (forall x. Shaped x => x -> f x) -> a -> Prim a f projectPrim _ = Prim -- | Generic implementation of @embed@ for any primitive type whose @Shape@ is -- is represented as a @Prim@ newtype embedPrim :: (forall x. Shaped x => f x -> x) -> Prim a f -> a embedPrim _ = unPrim -- | Generic implementation of @match@ for any primitive type whose @Shape@ is -- is represented as a @Prim@ newtype with an underlying @Eq@ instance matchPrim :: Eq a => Prim a f -> Prim a g -> (forall xs. All Shaped xs => Flattened (Prim a) f xs -> Maybe (Flattened (Prim a) g xs) -> result) -> result matchPrim (Prim a) (Prim b) k = k (flatPrim a) (if a == b then (Just (flatPrim b)) else Nothing) -- | Helper for writing @match@ instances for primitive types which don't have -- @Eq@ instance -- -- This generates a @Flattened@ appropriate for using in the implementation of -- @match@. For more documentation on how to use this, see the documentation of -- 'match'. flatPrim :: a -> Flattened (Prim a) g '[] flatPrim x = Flattened (const (Prim x)) Nil -- | Generic implementation of @render@ for any primitive type whose @Shape@ is -- is represented as a @Prim@ newtype renderPrim :: Show a => Prim a (K x) -> RenderLevel x renderPrim (Prim a) = renderConstant (show a) -- | Given some @string@, generate a custom pretty-printing representation which -- just shows the string renderConstant :: String -> RenderLevel x renderConstant s = CustomD 11 [Left (Left s)] -- TODO: What about demands for abstract types with > 1 type of unbounded-count field? {- withFieldsContainer :: forall c a f result. (forall r h. c (h a) -> (forall x. Shaped x => [h x] -> (forall g. [g x] -> c (g a)) -> r) -> r) -> Containing c a f -> (forall xs. All Shaped xs => NP f xs -> (forall g. NP g xs -> Containing c a g) -> result) -> result withFieldsContainer viaContaining (Container c) cont = viaContaining c $ \list un -> withNP @Shaped list (Container . un) cont -- TODO: Make this work for any number of lists of fields, by carefully using -- unsafeCoerce to deal with unknown list lengths withFieldsViaList :: forall demand f result. (forall r h. demand h -> (forall x. Shaped x => [h x] -> (forall g. [g x] -> demand g) -> r) -> r) -> demand f -> (forall xs. All Shaped xs => NP f xs -> (forall g. NP g xs -> demand g) -> result) -> result withFieldsViaList viaList demand cont = viaList demand $ \list un -> withNP @Shaped list un cont withNP :: forall c demand result f x. c x => [f x] -> (forall g. [g x] -> demand g) -> (forall xs. All c xs => NP f xs -> (forall g. NP g xs -> demand g) -> result) -> result withNP list unList cont = withUnhomogenized @c list $ \np -> cont np (unList . homogenize) withConcatenated :: NP (NP f) xss -> (forall xs. NP f xs -> r) -> r withConcatenated pop cont = case pop of Nil -> cont Nil (xs :* xss) -> withConcatenated xss (withPrepended xs cont) where withPrepended :: NP f ys -> (forall zs. NP f zs -> r) -> (forall zs. NP f zs -> r) withPrepended pre k rest = case pre of Nil -> k rest (x :* xs) -> withPrepended xs (k . (x :*)) rest homogenize :: All ((~) a) as => NP f as -> [f a] homogenize Nil = [] homogenize (a :* as) = a : homogenize as withUnhomogenized :: forall c a f r. c a => [f a] -> (forall as. (All c as, All ((~) a) as) => NP f as -> r) -> r withUnhomogenized [] k = k Nil withUnhomogenized (a : as) k = withUnhomogenized @c as $ \np -> k (a :* np) -} --------------------------------------------------- -- Generic implementation of the Shaped methods -- --------------------------------------------------- -- | The 'Shape' used for generic implementations of 'Shaped' -- -- This wraps a sum-of-products representation from "Generics.SOP". newtype GShape a f = GS (NS (NP f) (Code a)) -- | The collection of constraints necessary for a type to be given a generic -- implementation of the 'Shaped' methods type GShaped a = ( Generic a , Shape a ~ GShape a , All2 Shaped (Code a) , SListI (Code a) , All SListI (Code a) ) -- | Generic 'project' gProject :: GShaped a => (forall x. Shaped x => x -> f x) -> a -> Shape a f gProject p !(from -> sop) = GS (unSOP (hcliftA (Proxy @Shaped) (p . unI) sop)) -- | Generic 'embed' gEmbed :: GShaped a => (forall x. Shaped x => f x -> x) -> Shape a f -> a gEmbed e !(GS d) = to (hcliftA (Proxy @Shaped) (I . e) (SOP d)) -- | Generic 'match' gMatch :: forall a f g result. GShaped a => Shape a f -> Shape a g -> (forall xs. All Shaped xs => Flattened (Shape a) f xs -> Maybe (Flattened (Shape a) g xs) -> result) -> result gMatch !(GS df) !(GS dg) cont = go @(Code a) df (Just dg) $ \flatF mflatG -> cont (flatGD flatF) (flatGD <$> mflatG) where go :: forall xss r. (All SListI xss, All2 Shaped xss) => NS (NP f) xss -> Maybe (NS (NP g) xss) -> (forall xs. All Shaped xs => Flattened (Flip SOP xss) f xs -> Maybe (Flattened (Flip SOP xss) g xs) -> r) -> r go (Z (fieldsF :: _ xs)) (Just (Z fieldsG)) k = k @xs (flatZ fieldsF) (Just (flatZ fieldsG)) go (Z (fieldsF :: _ xs)) _ k = -- Nothing | Just (S _) k @xs (flatZ fieldsF) Nothing go (S moreF) Nothing k = go moreF Nothing $ \(flatF :: _ xs) _ -> k @xs (flatS flatF) Nothing go (S moreF) (Just (Z _)) k = go moreF Nothing $ \(flatF :: _ xs) _ -> k @xs (flatS flatF) Nothing go (S moreF) (Just (S moreG)) k = go moreF (Just moreG) $ \(flatF :: _ xs) mflatG -> k @xs (flatS flatF) (flatS <$> mflatG) flatZ :: forall h xs xss. NP h xs -> Flattened (Flip SOP (xs : xss)) h xs flatZ = Flattened (Flip . SOP . Z) flatS :: forall h xs xs' xss. Flattened (Flip SOP xss) h xs -> Flattened (Flip SOP (xs' : xss)) h xs flatS (Flattened un fields) = Flattened (Flip . SOP . S . coerce . un) fields flatGD :: forall t h xs. Flattened (Flip SOP (Code t)) h xs -> Flattened (GShape t) h xs flatGD (Flattened un fields) = Flattened (GS . coerce . un) fields -- | Generic 'render' gRender :: forall a x. (HasDatatypeInfo a, GShaped a) => Shape a (K x) -> RenderLevel x gRender (GS demand) = case info of ADT m d cs -> renderC m d demand cs Newtype m d c -> renderC m d demand (c :* Nil) where info = datatypeInfo (Proxy @a) renderC :: forall as. ModuleName -> DatatypeName -> NS (NP (K x)) as -> NP ConstructorInfo as -> RenderLevel x renderC m d subShape constructors = case (subShape, constructors) of (Z demandFields, c :* _) -> case c of Constructor name -> ConstructorD (m, d, name) $ hcollapse demandFields Infix name associativity fixity -> case demandFields of (K a :* K b :* Nil) -> InfixD (m, d, name) associativity fixity a b Record name fieldsInfo -> RecordD (m, d, name) $ zip ( hcollapse . hliftA (\(FieldInfo f) -> K (m, d, f)) $ fieldsInfo ) (hcollapse demandFields) (S another, _ :* different) -> renderC m d another different --------------- -- Instances -- --------------- instance Shaped () instance Shaped Bool instance Shaped Ordering instance Shaped a => Shaped (Maybe a) instance (Shaped a, Shaped b) => Shaped (Either a b) instance Shaped a => Shaped [a] instance (Typeable a, Typeable b) => Shaped (a -> b) where type Shape (a -> b) = Prim (a -> b) project = projectPrim embed = embedPrim match (Prim f) (Prim g) k = k (flatPrim f) (Just $ flatPrim g) render _ = renderConstant ("<function> :: " ++ show (typeRep @(a -> b))) instance Shaped Char where type Shape Char = Prim Char project = projectPrim embed = embedPrim match = matchPrim render = renderPrim instance Shaped Word where type Shape Word = Prim Word project = projectPrim embed = embedPrim match = matchPrim render = renderPrim instance Shaped Int where type Shape Int = Prim Int project = projectPrim embed = embedPrim match = matchPrim render = renderPrim instance Shaped Double where type Shape Double = Prim Double project = projectPrim embed = embedPrim match = matchPrim render = renderPrim instance Shaped Float where type Shape Float = Prim Float project = projectPrim embed = embedPrim match = matchPrim render = renderPrim instance Shaped Rational where type Shape Rational = Prim Rational project = projectPrim embed = embedPrim match = matchPrim render = renderPrim instance Shaped Integer where type Shape Integer = Prim Integer project = projectPrim embed = embedPrim match = matchPrim render = renderPrim instance (Typeable a, Eq a, Show a) => Shaped (Complex a) where type Shape (Complex a) = Prim (Complex a) project = projectPrim embed = embedPrim match = matchPrim render = renderPrim -- instance Generic (NonEmpty a) -- instance HasDatatypeInfo (NonEmpty a) -- instance Shaped a => Shaped (NonEmpty a) where -- Tree -- Map k -- Seq -- Set -- IntMap -- IntSet instance (Shaped a, Shaped b) => Shaped (a, b) instance (Shaped a, Shaped b, Shaped c) => Shaped (a, b, c) instance (Shaped a, Shaped b, Shaped c, Shaped d) => Shaped (a, b, c, d) instance ( Shaped a, Shaped b, Shaped c, Shaped d, Shaped e ) => Shaped (a, b, c, d, e) instance ( Shaped a, Shaped b, Shaped c, Shaped d, Shaped e, Shaped f ) => Shaped (a, b, c, d, e, f) instance ( Shaped a, Shaped b, Shaped c, Shaped d, Shaped e, Shaped f , Shaped g ) => Shaped (a, b, c, d, e, f, g) -- instance ( Shaped a, Shaped b, Shaped c, Shaped d, Shaped e, Shaped f -- , Shaped g, Shaped h -- ) => Shaped -- (a, b, c, d, e, f, g, h) -- instance ( Shaped a, Shaped b, Shaped c, Shaped d, Shaped e, Shaped f -- , Shaped g, Shaped h, Shaped i -- ) => Shaped -- (a, b, c, d, e, f, g, h, i) -- instance ( Shaped a, Shaped b, Shaped c, Shaped d, Shaped e, Shaped f -- , Shaped g, Shaped h, Shaped i, Shaped j -- ) => Shaped -- (a, b, c, d, e, f, g, h, i, j) -- instance ( Shaped a, Shaped b, Shaped c, Shaped d, Shaped e, Shaped f -- , Shaped g, Shaped h, Shaped i, Shaped j, Shaped k -- ) => Shaped -- (a, b, c, d, e, f, g, h, i, j, k) -- instance ( Shaped a, Shaped b, Shaped c, Shaped d, Shaped e, Shaped f -- , Shaped g, Shaped h, Shaped i, Shaped j, Shaped k, Shaped l -- ) => Shaped -- (a, b, c, d, e, f, g, h, i, j, k, l) -- instance ( Shaped a, Shaped b, Shaped c, Shaped d, Shaped e, Shaped f -- , Shaped g, Shaped h, Shaped i, Shaped j, Shaped k, Shaped l -- , Shaped m -- ) => Shaped -- (a, b, c, d, e, f, g, h, i, j, k, l, m) -- instance ( Shaped a, Shaped b, Shaped c, Shaped d, Shaped e, Shaped f -- , Shaped g, Shaped h, Shaped i, Shaped j, Shaped k, Shaped l -- , Shaped m, Shaped n -- ) => Shaped -- (a, b, c, d, e, f, g, h, i, j, k, l, m, n) -- instance ( Shaped a, Shaped b, Shaped c, Shaped d, Shaped e, Shaped f -- , Shaped g, Shaped h, Shaped i, Shaped j, Shaped k, Shaped l -- , Shaped m, Shaped n, Shaped o -- ) => Shaped -- (a, b, c, d, e, f, g, h, i, j, k, l, m, n, o) -- instance ( Shaped a, Shaped b, Shaped c, Shaped d, Shaped e, Shaped f -- , Shaped g, Shaped h, Shaped i, Shaped j, Shaped k, Shaped l -- , Shaped m, Shaped n, Shaped o, Shaped p -- ) => Shaped -- (a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p) -- instance ( Shaped a, Shaped b, Shaped c, Shaped d, Shaped e, Shaped f -- , Shaped g, Shaped h, Shaped i, Shaped j, Shaped k, Shaped l -- , Shaped m, Shaped n, Shaped o, Shaped p, Shaped q -- ) => Shaped -- (a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q) -- instance ( Shaped a, Shaped b, Shaped c, Shaped d, Shaped e, Shaped f -- , Shaped g, Shaped h, Shaped i, Shaped j, Shaped k, Shaped l -- , Shaped m, Shaped n, Shaped o, Shaped p, Shaped q, Shaped r -- ) => Shaped -- (a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r) -- instance ( Shaped a, Shaped b, Shaped c, Shaped d, Shaped e, Shaped f -- , Shaped g, Shaped h, Shaped i, Shaped j, Shaped k, Shaped l -- , Shaped m, Shaped n, Shaped o, Shaped p, Shaped q, Shaped r -- , Shaped s -- ) => Shaped -- (a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s) -- instance ( Shaped a, Shaped b, Shaped c, Shaped d, Shaped e, Shaped f -- , Shaped g, Shaped h, Shaped i, Shaped j, Shaped k, Shaped l -- , Shaped m, Shaped n, Shaped o, Shaped p, Shaped q, Shaped r -- , Shaped s, Shaped t -- ) => Shaped -- (a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t) -- instance ( Shaped a, Shaped b, Shaped c, Shaped d, Shaped e, Shaped f -- , Shaped g, Shaped h, Shaped i, Shaped j, Shaped k, Shaped l -- , Shaped m, Shaped n, Shaped o, Shaped p, Shaped q, Shaped r -- , Shaped s, Shaped t, Shaped u -- ) => Shaped -- (a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u) -- instance ( Shaped a, Shaped b, Shaped c, Shaped d, Shaped e, Shaped f -- , Shaped g, Shaped h, Shaped i, Shaped j, Shaped k, Shaped l -- , Shaped m, Shaped n, Shaped o, Shaped p, Shaped q, Shaped r -- , Shaped s, Shaped t, Shaped u, Shaped v -- ) => Shaped -- (a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v) -- instance ( Shaped a, Shaped b, Shaped c, Shaped d, Shaped e, Shaped f -- , Shaped g, Shaped h, Shaped i, Shaped j, Shaped k, Shaped l -- , Shaped m, Shaped n, Shaped o, Shaped p, Shaped q, Shaped r -- , Shaped s, Shaped t, Shaped u, Shaped v, Shaped w -- ) => Shaped -- (a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w) -- instance ( Shaped a, Shaped b, Shaped c, Shaped d, Shaped e, Shaped f -- , Shaped g, Shaped h, Shaped i, Shaped j, Shaped k, Shaped l -- , Shaped m, Shaped n, Shaped o, Shaped p, Shaped q, Shaped r -- , Shaped s, Shaped t, Shaped u, Shaped v, Shaped w, Shaped x -- ) => Shaped -- (a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x) -- instance ( Shaped a, Shaped b, Shaped c, Shaped d, Shaped e, Shaped f -- , Shaped g, Shaped h, Shaped i, Shaped j, Shaped k, Shaped l -- , Shaped m, Shaped n, Shaped o, Shaped p, Shaped q, Shaped r -- , Shaped s, Shaped t, Shaped u, Shaped v, Shaped w, Shaped x -- , Shaped y -- ) => Shaped -- (a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y) -- instance ( Shaped a, Shaped b, Shaped c, Shaped d, Shaped e, Shaped f -- , Shaped g, Shaped h, Shaped i, Shaped j, Shaped k, Shaped l -- , Shaped m, Shaped n, Shaped o, Shaped p, Shaped q, Shaped r -- , Shaped s, Shaped t, Shaped u, Shaped v, Shaped w, Shaped x -- , Shaped y, Shaped z -- ) => Shaped -- (a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z)