Can't declare patterns COMPLETE in some cases?
Summary
Suppose we create a pattern synonym, which has a class constraint in its type. E.g., something like
pattern Null :: CanBeNull a => a
pattern Null <- (cIsNull -> True) where
Null = cMkEmpty
where cIsNull
and cMkEmpty
are methods of our class CanBeNull
. (Fuller example later, but this is the sort of case covered.)
If we want to declare a set of patterns that includes this Null
pattern COMPLETE
, by providing a COMPLETE
pragma -- for instance because we have a NotNull
pattern elsewhere -- it doesn't seem possible to do so, which seems like a bug in ghc's ability to handle COMPLETE
pragmas. (Or, maybe, the observed behaviour is intended, and COMPLETE
pragmas can't mention classes, in which case this is a feature request to allow them to do so - I'm happy either way. Or perhaps I'm just misunderstanding the GHC documentation, in which case this might be a request for improved documentation.)
E.g., suppose we then write a function that matches on our set of patterns, and compile our code with ghc -Wincomplete-patterns
.
All the ways of declaring sets of such pattern synonyms complete seem to result in either (a) compiler errors, or (b) not to work: in that ghc (when passed a -Wincomplete-patterns
flag) still reports matches on them as non-exhaustive.
Steps to reproduce
- Suppose we want to create patterns that match a class of "cons-able" things -- they can either be
Null
, or aSingleton
, orCons x xs
. - Create a file "X.hs", with contents (provided also as an attachment, X.hs).
X.hs
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE PatternSynonyms #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE ViewPatterns #-}
{-# LANGUAGE RankNTypes #-}
-- needed for ConsableConstraint
module X
where
class Consable c where
type Elem c
cIsNull :: c -> Bool
cIsSingleton :: c -> Maybe (Elem c)
cMkEmpty :: c
cMkSingleton :: Elem c -> c
cCons :: Elem c -> c -> c
cUncons :: c -> Maybe (Elem c, c)
pattern Null :: Consable c => c
pattern Null <- (cIsNull -> True) where
Null = cMkEmpty
pattern Singleton :: Consable a => Elem a -> a
pattern Singleton el <- (cIsSingleton -> Just el) where
Singleton el = cMkSingleton el
pattern Cons :: Consable c => Elem c -> c -> c
pattern Cons el c <- (cUncons -> Just (el, c)) where
Cons el c = cCons el c
-- {-# COMPLETE Null, Singleton, Cons #-}
-- results in compiler error:
-- • A type signature must be provided for a set of polymorphic pattern synonyms.
-- • In {-# COMPLETE Null, Singleton, Cons #-}
-- {-# COMPLETE Null, Singleton, Cons :: c #-}
-- results in compiler error:
-- error: parse error on input ‘c’
-- {-# COMPLETE Null, Singleton, Cons :: Consable #-}
-- doesn't work: still get "Pattern match(es) are non-exhaustive"
-- {-# COMPLETE Null, Singleton, Cons :: Consable c => c #-}
-- results in compiler error:
-- error: parse error on input ‘c’
--type ConsableConstraint c = (Consable c) => c
--{-# COMPLETE Null, Singleton, Cons :: ConsableConstraint #-}
-- doesn't work: still get "Pattern match(es) are non-exhaustive"
type ConsableConstraint' = forall c . (Consable c) => c
{-# COMPLETE Null, Singleton, Cons :: ConsableConstraint' #-}
-- doesn't work: still get "Pattern match(es) are non-exhaustive"
foo :: (Consable c) => c -> String
foo x = case x of
Null -> "is null"
Singleton _x -> "is a singleton"
_x `Cons` _xs -> "is non-empty, and not a singleton"
Compile it with:
$ ghc -Wincomplete-patterns src/X.hs
Expected behavior
ghc should not emit an "incomplete patterns" warning: the source code declares that the set of Null
and Singleton
and Cons
patterns are complete. Although they are polymorphic, it provides a type signature which -- as far as I can tell -- satisfies the requirements mentioned in the complete-sigs Wiki page.
(As an aside: I note that the GHC documentation seems incomplete, as it mentions COMPLETE pragmas, but fails to explain that they can be given type signatures -- and indeed, must be given type signatures, when polymorphic, if one is to avoid an "A type signature must be provided for a set of polymorphic pattern synonyms" error from ghc
. I am happy to file the possibly incomplete documentation as a separate issue if desired.)
In comments in the attached file, I've noted a few alternative ways one might try to declare a set of patterns like this COMPLETE
.
One can't say something like
{-# COMPLETE Null, Singleton, Cons :: Consable c => c -#}
because this results in compiler error (error: parse error on input ‘c’
). To avoid mentioning type variables, one might try something like
type ConsableConstraint' = forall c . (Consable c) => c
{-# COMPLETE Null, Singleton, Cons :: ConsableConstraint' #-}
but that doesn't seem to work either.
Environment
- GHC version used: 8.8.4
- Operating System: Ubuntu 18.04.5, kernel 5.4.0-58-generic
- System Architecture: x86_64