[GHC] #14937: QuantifiedConstraints: Reify implication constraints from terms lacking them

GHC ghc-devs at haskell.org
Wed Nov 7 15:59:54 UTC 2018


#14937: QuantifiedConstraints: Reify implication constraints from terms lacking
them
-------------------------------------+-------------------------------------
        Reporter:  Bj0rn             |                Owner:  (none)
            Type:  feature request   |               Status:  new
        Priority:  normal            |            Milestone:
       Component:  Compiler          |              Version:  8.5
      Resolution:                    |             Keywords:
                                     |  QuantifiedConstraints
Operating System:  Unknown/Multiple  |         Architecture:
                                     |  Unknown/Multiple
 Type of failure:  None/Unknown      |            Test Case:
      Blocked By:                    |             Blocking:
 Related Tickets:  #14822, #15635    |  Differential Rev(s):
       Wiki Page:                    |
-------------------------------------+-------------------------------------

Comment (by Bj0rn):

 Here's a motivating example where I wish that I was able to show the
 compiler that one (quantified) constraint implies another. In this
 example, I'm using the `singletons` library. The
 `Data.Singletons.TypeLits` module contains

 {{{#!hs
 data instance Sing (n :: Nat) = KnownNat n => SNat

 instance KnownNat n => SingI n where
   sing = SNat
 }}}

 where the compiler learns of the implication `forall n. KnownNat n =>
 SingI n`. However the reverse implication `forall n. SingI n => KnownNat
 n` is also true as evidenced by:

 {{{#!hs
 data Dict :: Constraint -> Type where
     Dict :: c => Dict c

 singIToKnownNat :: forall n. SingI n => Dict (KnownNat n)
 singIToKnownNat = case sing @n of SNat -> Dict
 }}}

 The crux is that there's is no way to rephrase `singIToKnownNat` in terms
 of constraint implication:


 {{{#!hs
 singIImpliesKnownNat :: forall r. ((forall n. SingI n => KnownNat n) => r)
 -> r
 singIImpliesKnownNat a = undefined          -- Can't implement this?
 }}}

 What this ticket asks for is some way to write a correct definition of
 things like `singIImpliesKnownNat`. Something similar to the definition of
 `singIToKnownNat`, I would imagine.

 Here's the full example code:

 {{{#!hs
 -- GHC 8.6.2, Singletons 2.5

 {-# LANGUAGE GADTs #-}
 {-# LANGUAGE TypeInType #-}
 {-# LANGUAGE ScopedTypeVariables #-}
 {-# LANGUAGE TypeApplications #-}
 {-# LANGUAGE ConstraintKinds #-}
 {-# LANGUAGE RankNTypes #-}
 {-# LANGUAGE QuantifiedConstraints #-}

 module ReifyImplications where

 import Data.Kind
 import GHC.TypeNats
 import Data.Singletons
 import Data.Singletons.TypeLits
 import Data.Type.Equality

 data EmptyVec :: Nat -> Type where
     EmptyVec :: EmptyVec 0

 instance KnownNat n => Read (EmptyVec n) where
     readsPrec _ "EmptyVec" =
         case sameNat @n @0 Proxy Proxy of
             Just Refl -> [(EmptyVec, "")]
             Nothing -> []

 data Some1 :: (k -> Type) -> Type where
     Some1 :: Sing a -> f a -> Some1 f

 readKnowingTypeIndex ::
     forall k (f :: k -> Type). (forall x. SingI x => Read (f x)) =>
         SomeSing k -> String -> Some1 f
 readKnowingTypeIndex (SomeSing s) str =
     Some1 s $ withSingI s $ read str

 readKnowingNat ::
     forall (f :: Nat -> Type). (forall n. KnownNat n => Read (f n)) =>
         SomeSing Nat -> String -> Some1 f
 readKnowingNat (SomeSing (SNat :: Sing a)) str =
     Some1 (SNat @a) $ read str              -- Can't write this in terms
 of readKnowingTypeIndex?

 ev :: SomeSing Nat -> Some1 EmptyVec
 --ev s = readKnowingTypeIndex s "EmptyVec"  -- Could not deduce (KnownNat
 x).
 --ev s = readKnowingNat s "EmptyVec"        -- OK, but this doesn't work
 on every kind. Only Nat.

 singIImpliesKnownNat :: forall r. ((forall n. SingI n => KnownNat n) => r)
 -> r
 singIImpliesKnownNat a = undefined          -- Can't implement this?

 ev s =
     singIImpliesKnownNat $
         readKnowingTypeIndex s "EmptyVec"   -- OK, if singIImpliesKnownNat
 was real.


 -- Now, this is easy to implement. But it's not practically usable in the
 definition of `ev`.
 data Dict :: Constraint -> Type where
     Dict :: c => Dict c

 singIToKnownNat :: forall n. SingI n => Dict (KnownNat n)
 singIToKnownNat = case sing @n of SNat -> Dict


 -- Bonus: readKnowingNat written in terms of readKnowingTypeIndex
 readKnowingNat' ::
     forall (f :: Nat -> Type). (forall n. KnownNat n => Read (f n)) =>
         SomeSing Nat -> String -> Some1 f
 readKnowingNat' s str =
     singIImpliesKnownNat $
         readKnowingTypeIndex s str
 }}}

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


More information about the ghc-tickets mailing list