[GHC] #16266: Allow definition of functions with absurd contexts

GHC ghc-devs at haskell.org
Fri Feb 1 07:12:37 UTC 2019


#16266: Allow definition of functions with absurd contexts
-------------------------------------+-------------------------------------
           Reporter:  So8res         |             Owner:  (none)
               Type:  feature        |            Status:  new
  request                            |
           Priority:  low            |         Milestone:
          Component:  Compiler       |           Version:  8.6.3
           Keywords:                 |  Operating System:  Unknown/Multiple
       Architecture:                 |   Type of failure:  None/Unknown
  Unknown/Multiple                   |
          Test Case:                 |        Blocked By:
           Blocking:                 |   Related Tickets:
Differential Rev(s):                 |         Wiki Page:
-------------------------------------+-------------------------------------
 Consider the following code:

 {{{#!hs
 {-# LANGUAGE AllowAmbiguousTypes #-}
 {-# LANGUAGE DataKinds #-}
 {-# LANGUAGE FlexibleContexts #-}
 {-# LANGUAGE RankNTypes #-}
 {-# LANGUAGE ScopedTypeVariables #-}
 {-# LANGUAGE TypeApplications #-}
 {-# LANGUAGE TypeFamilies #-}
 {-# LANGUAGE TypeOperators #-}
 type family Head xxs where
   Head (x ': xs) = x

 type family Tail xxs where
   Tail (x ': xs) = xs

 class IsList xs where
   isList :: (xs ~ '[] => r) -> ((xs ~ (Head xs ': Tail xs), IsList (Tail
 xs)) => r) -> r

 instance IsList '[] where isList r _ = r
 instance IsList xs => IsList (x ': xs) where isList _ r = r

 nonEmptyListsAreNotEmpty :: (xs ~ '[], xs ~ (Head xs ': Tail xs)) => r
 nonEmptyListsAreNotEmpty = error "you convinced GHC that '[] ~ (_ ': _)!"

 sillyExample :: forall x xs. IsList (x ': xs) => ()
 sillyExample = isList @(x ': xs) nonEmptyListsAreNotEmpty ()
 }}}

 In this code, we define a class IsList which serves as a proof that
 something is a type-level list, via the function isList. When using this,
 we may occasionally get into a situation where the types guarantee that
 we're working with a non-empty list, but we're asked to handle the empty-
 list case anyway. Suppose we want to define a nice little "absurd"
 function that serves as a reminder to readers that this code branch is
 impossible, in a way that GHC can verify. This function is named
 "nonEmptyListsAreNonEmpty" above.

 Unfortunately, this code fails with

 {{{
 Main.hs:25:34: error:
     • Couldn't match type ‘'[]’ with ‘Head '[] : Tail '[]’
         arising from a use of ‘nonEmptyListsAreNotEmpty’
     • In the second argument of ‘isList’, namely
         ‘nonEmptyListsAreNotEmpty’
       In the expression: isList @(x : xs) nonEmptyListsAreNotEmpty ()
       In an equation for ‘sillyExample’:
           sillyExample = isList @(x : xs) nonEmptyListsAreNotEmpty ()
    |
 25 | sillyExample = isList @(x ': xs) nonEmptyListsAreNotEmpty ()
    |                                  ^^^^^^^^^^^^^^^^^^^^^^^^
 }}}

 And indeed, the fact that [] can't be matched against (_ : _) is in some
 sense the point. More generally, whenever I'm fooling around with this
 sort of Haskell code (in which classes are being used to provide runtime
 witnesses of type-level structure), I relatively regularly find myself in
 a branch of code where the context is obviously inconsistent, wishing that
 I had some way to say "search your heart, GHC; you know this case to be
 impossible". The fact that I can't even factor out this dead code into a
 function like nonEmptyListsAreNonEmpty just seems like insult added to
 injury.

 This is a minor concern, to be sure. Nevertheless, it seems to me that we
 should be able to write this function somehow, especially given that there
 are various ways to trick GHC into accepting something analogous (by
 clever use of GADTs such as :~:, perhaps).

-- 
Ticket URL: <http://ghc.haskell.org/trac/ghc/ticket/16266>
GHC <http://www.haskell.org/ghc/>
The Glasgow Haskell Compiler


More information about the ghc-tickets mailing list