Given the D1535 effort in GHC 8.0 I was curious how an example from Richard's post What are type families? would fare:
% ghci -Wall -XTypeFamilies -ignore-dot-ghciGHCi, version 8.1.20160105: http://www.haskell.org/ghc/ :? for helpPrelude> :set +mPrelude> type family F1 a wherePrelude| F1 Int = BoolPrelude|Prelude> letPrelude| sillyId :: F1 Char -> F1 CharPrelude| sillyId x = xPrelude|Prelude>
This seems to fly, even though as the blog post noted, ”[...] sillyId can’t ever be called on a value.” Isn't the clause redundant and should be defined using EmptyCase (#2431 (closed)) as sillyId = \case?
It seems GHC doesn't care that the pattern match in the equation absurd2 _ = ... is redundant either:
Empty data declarations are Haskell 2010, so it doesn't seem reasonable to give a warning that suggests something outside the Haskell 2010 language. If EmptyCase is already on, though, it could make sense to issue a warning in your second example.
I don't understand your first example. Isn't the clause sillyId x = case x of {} just as "redundant" as the clause sillyId x = x was in the first place?
I would say that case x of {} is just a case expression with no alternatives. It is, itself, still an ordinary expression. So both versions look like sillyId x = <expr> and should be equivalent as far as redundancy of the outer pattern match, the one done by sillyId, is concerned. Since you cannot write a definition with 0 equations, this "redundancy" is unavoidable. (Unless you use LambdaCase and EmptyCase, I guess.)
Are you suggesting treating case x of {} as some kind of special form?
Also, I'm not sure a "stuck" type family application is really empty in the same sense as an empty data type. But #10746 (closed) makes it hard to tell what GHC thinks about this.
We much prefer (B). Why? Because GHC can figure out that (True :~: False) is an empty type. So (B) has no partiality and GHC should be able to compile with -Wincomplete-patterns. (Though the pattern match checking is not yet clever enough to do that.) On the other hand (A) looks dangerous, and GHC doesn’t check to make sure that, in fact, the function can never get called.
I wasn't sure whether the "Though the pattern match checking is not yet clever enough to do that." statement had changed in 8, I'll edit the code to enable LambdaCase as well.
The manual there is talking about partiality, or completeness of pattern matches, not redundancy. (B) is complete because the pattern match in absurd is complete, and the pattern match in the case is also complete. But the pattern match in absurd is "redundant", regardless of what happens on the right-hand side of the equation.
Actually nothing is redundant in the above examples. As we discuss in the paper (section about
laziness), (sillyId undefined) will give you the right hand-side so the clause:
sillyIdx=x
is not redundant at all. Of course, this is not the case for:
sillyId2::F1Char->F1CharsillyId2(!x)=x
because it is strict in the argument, so no WHNF can have the type F1 Char. But even in this
case, the clause is not actually redundant, it just has an inaccessible RHS (remember this is
not Agda). Pattern matching is not just used for discrimination in Haskell but also for evaluation,
which I see as a side effect. Hence, the clause may have an inaccessible RHS, but by removing it
the expression is not evaluated and you change the semantics of sillyId2.
I agree with George (gkaracha). sillyId x = x can't be called on a value, but Haskell isn't call-by-value. I will use Void instead of F1 Char, because the type family bit isn't the interesting part here.
silly1 :: Void -> Voidsilly1 x = xsilly2 :: Void -> Voidsilly2 x = error "urk"
silly1 undefined and silly2 undefined have different runtime behavior; the pattern is not redundant.