1010{-# LANGUAGE OverloadedStrings #-}
1111{-# LANGUAGE PartialTypeSignatures #-}
1212{-# LANGUAGE PatternSynonyms #-}
13+ {-# LANGUAGE PolyKinds #-}
1314{-# LANGUAGE QuantifiedConstraints #-}
1415{-# LANGUAGE RankNTypes #-}
1516{-# LANGUAGE ScopedTypeVariables #-}
2324{-# OPTIONS_GHC -Wno-orphans #-}
2425{-# OPTIONS_GHC -Wno-redundant-constraints #-}
2526
26- -- The pattern completeness checker is much weaker before ghc-9.0. Rather than introducing redundant
27- -- cases and turning off the overlap check in newer ghc versions we disable the check for old
28- -- versions.
29- #if __GLASGOW_HASKELL__ < 900
30- {-# OPTIONS_GHC -Wno-incomplete-patterns #-}
31- #endif
32-
3327module Constrained.Spec.SumProd (
3428 IsNormalType ,
29+ ProdAsListComputes ,
3530 caseOn ,
3631 branch ,
3732 branchW ,
@@ -133,6 +128,7 @@ import Constrained.TheKnot (
133128 prodSnd_ ,
134129 prod_ ,
135130 )
131+ import Constrained.TypeErrors
136132import Data.Typeable (Typeable )
137133import GHC.TypeLits (Symbol )
138134import GHC.TypeNats
@@ -250,20 +246,43 @@ type family ResultType t where
250246 ResultType (a -> b ) = ResultType b
251247 ResultType a = a
252248
253- type IsNormalType a = (Cases a ~ '[a ], Args a ~ '[a ], IsProd a , CountCases a ~ 1 )
249+ type IsNormalType a =
250+ ( AssertComputes
251+ (Cases a )
252+ ( Text " Failed to compute Cases in a use of IsNormalType for "
253+ :$$: ShowType a
254+ :<>: Text " , are you missing an IsNormalType constraint?"
255+ )
256+ , Cases a ~ '[a ]
257+ , AssertComputes
258+ (Args a )
259+ ( Text " Failed to compute Args in a use of IsNormalType for "
260+ :<>: ShowType a
261+ :<>: Text " , are you missing an IsNormalType constraint?"
262+ )
263+ , Args a ~ '[a ]
264+ , IsProd a
265+ , CountCases a ~ 1
266+ )
254267
255268type family Cases t where
256269 Cases (Sum a b ) = a : Cases b
257270 Cases a = '[a ]
258271
259272type IsProductType a =
260273 ( HasSimpleRep a
274+ , AssertComputes
275+ (Cases (SimpleRep a ))
276+ ( Text " Failed to compute Cases in a use of IsProductType for "
277+ :$$: ShowType a
278+ :<>: Text " , are you missing an IsProductType constraint?"
279+ )
261280 , Cases (SimpleRep a ) ~ '[SimpleRep a ]
262281 , SimpleRep a ~ SumOver (Cases (SimpleRep a ))
263282 , IsProd (SimpleRep a )
264283 , HasSpec (SimpleRep a )
265284 , TypeSpec a ~ TypeSpec (SimpleRep a )
266- , All HasSpec (ProductAsList a )
285+ , All HasSpec (Args ( SimpleRep a ) )
267286 )
268287
269288type ProductAsList a = Args (SimpleRep a )
@@ -472,15 +491,45 @@ branchW ::
472491branchW w body =
473492 Weighted (Just w) (bind (buildBranch @ p body . toArgs @ a ))
474493
494+ -- | ProdAsListComputes is here to make sure that in situations like this:
495+ --
496+ -- > type family Foobar k
497+ -- >
498+ -- > ex :: HasSpec (Foobar k) => Specification (Int, Foobar k)
499+ -- > ex = constrained $ \ p -> match p $ \ i _ -> (i ==. 10)
500+ --
501+ -- Where you're trying to work with an unevaluated type family in constraints.
502+ -- You get reasonable type errors prompting you to add the @IsNormalType (Foobar k)@ constraint
503+ -- like this:
504+ --
505+ -- > • Type list computation is stuck on
506+ -- > Args (Foobar k)
507+ -- > Have you considered adding an IsNormalType or ProdAsListComputes constraint?
508+ -- > • In the first argument of ‘($)’, namely ‘match p’
509+ -- > In the expression: match p $ \ i _ -> (i ==. 10)
510+ -- > In the second argument of ‘($)’, namely
511+ -- > ‘\ p -> match p $ \ i _ -> (i ==. 10)’
512+ -- > |
513+ -- > 503 | ex = constrained $ \ p -> match p $ \ i _ -> (i ==. 10)
514+ -- > | ^^^^^
515+ --
516+ -- Which should help you come to the conclusion that you need to do something
517+ -- like this for everything to compile:
518+ --
519+ -- > ex :: (HasSpec (Foobar k), IsNormalType (Foobar k)) => Specification (Int, Foobar k)
520+ type ProdAsListComputes a =
521+ AssertSpineComputes
522+ (Text " Have you considered adding an IsNormalType or ProdAsListComputes constraint?" )
523+ (ProductAsList a )
524+
475525match ::
476526 forall p a .
477527 ( IsProductType a
478528 , IsPred p
479529 , GenericRequires a
530+ , ProdAsListComputes a
480531 ) =>
481- Term a ->
482- FunTy (MapList Term (ProductAsList a )) p ->
483- Pred
532+ Term a -> FunTy (MapList Term (ProductAsList a )) p -> Pred
484533match p m = caseOn p (branch @ p m)
485534
486535-- NOTE: `ResultType r ~ Term a` is NOT a redundant constraint,
@@ -536,11 +585,13 @@ forAll' ::
536585 , All HasSpec (Args (SimpleRep a ))
537586 , IsPred p
538587 , IsProd (SimpleRep a )
588+ , IsProductType a
539589 , HasSpec a
540590 , GenericRequires a
591+ , ProdAsListComputes a
541592 ) =>
542593 Term t ->
543- FunTy (MapList Term (Args ( SimpleRep a ) )) p ->
594+ FunTy (MapList Term (ProductAsList a )) p ->
544595 Pred
545596forAll' xs f = forAll xs $ \ x -> match @ p x f
546597
@@ -554,10 +605,12 @@ constrained' ::
554605 , All HasSpec (Args (SimpleRep a ))
555606 , IsProd (SimpleRep a )
556607 , HasSpec a
608+ , IsProductType a
557609 , IsPred p
558610 , GenericRequires a
611+ , ProdAsListComputes a
559612 ) =>
560- FunTy (MapList Term (Args ( SimpleRep a ) )) p ->
613+ FunTy (MapList Term (ProductAsList a )) p ->
561614 Specification a
562615constrained' f = constrained $ \ x -> match @ p x f
563616
@@ -572,12 +625,15 @@ reify' ::
572625 , IsProd (SimpleRep b )
573626 , HasSpec a
574627 , HasSpec b
628+ , IsProductType b
629+ , IsProd a
575630 , IsPred p
576631 , GenericRequires b
632+ , ProdAsListComputes b
577633 ) =>
578634 Term a ->
579635 (a -> b ) ->
580- FunTy (MapList Term (Args ( SimpleRep b ) )) p ->
636+ FunTy (MapList Term (ProductAsList b )) p ->
581637 Pred
582638reify' a r f = reify a r $ \ x -> match @ p x f
583639
0 commit comments