How can I ‘convince’ GHC that I've excluded a certain case?

One thing you can do is pass around an alternative for empty lists:

lastDef :: a -> List e a -> a
lastDef a Nil = a
lastDef _ (Cons a b) = lastDef a b

Then wrap it up once at the top level.

last :: NEList a -> a
last (Cons a b) = lastDef a b

Extending this pattern to foldr and foldr1 is left as an exercise for the reader.


You've "defined" NEList (I say that in a loose way) the same way base defines NonEmpty: as a single element tacked onto the head of a potentially empty list.

data NonEmpty a = a :| [a]

Another presentation of NonEmpty instead places that single element at the end.

data NonEmpty a = Single a | Multiple a (NonEmpty a)

This presentation, no surprise, makes eLast easy:

eLast (Single x) = x
eLast (Multiple _ xs) = eLast xs

Whenever you want multiple sets of constructors on the same type, look to pattern synonyms. Instead of the base NonEmpty, we can also translate to your List.

pattern Single :: forall e a. () => e ~ 'NotEmpty => a -> List e a
pattern Single x = Cons x Nil
pattern Multiple :: forall e a. () => e ~ 'NotEmpty => a -> List 'NotEmpty a -> List e a
pattern Multiple x xs <- Cons x xs@(Cons _ _)
  where Multiple x xs = Cons x xs
-- my dormant bidirectional pattern synonyms GHC proposal would allow just
-- pattern Multiple x (Cons y xs) = Cons x (Cons y xs)
-- but current pattern synonyms are a little stupid, so Multiple is ugly
{-# COMPLETE Nil, Single, Multiple :: List #-}
-- Single and Multiple are actually patterns on List e a, not List NotEmpty a
-- Therefore the COMPLETE must include Nil, or else we'd show that all
-- Lists are nonempty (\case { Single _ -> Refl; Multiple _ _ -> Refl })
-- They are, however, still complete on List NotEmpty a
-- GHC will "infer" this by "trying" the Nil constructor and deeming it impossible

Giving

eLast :: NEList a -> a
eLast (Single x) = x
eLast (Multiple _ xs) = eLast xs