{-# LANGUAGE PolyKinds #-} -- | Explicit dictionaries. -- -- When working with compound constraints such as constructed -- using 'All' or 'All2', GHC cannot always prove automatically -- what one would expect to hold. -- -- This module provides a way of explicitly proving -- conversions between such constraints to GHC. Such conversions -- still have to be manually applied. -- -- This module is new and experimental in generics-sop 0.2. -- It is therefore not yet exported via the main module and -- has to be imported explicitly. Its interface is to be -- considered even less stable than that of the rest of the -- library. Feedback is very welcome though. -- module Generics.SOP.Dict where import Data.Proxy import Generics.SOP.Classes import Generics.SOP.Constraint import Generics.SOP.NP import Generics.SOP.Sing -- | An explicit dictionary carrying evidence of a -- class constraint. -- -- The constraint parameter is separated into a -- second argument so that @'Dict' c@ is of the correct -- kind to be used directly as a parameter to e.g. 'NP'. -- -- @since 0.2 -- data Dict (c :: k -> Constraint) (a :: k) where Dict :: c a => Dict c a -- | A proof that the trivial constraint holds -- over all type-level lists. -- -- @since 0.2 -- pureAll :: SListI xs => Dict (All Top) xs pureAll = all_NP (hpure Dict) -- | A proof that the trivial constraint holds -- over all type-level lists of lists. -- -- @since 0.2 -- pureAll2 :: All SListI xss => Dict (All2 Top) xss pureAll2 = all_POP (hpure Dict) -- | Lifts a dictionary conversion over a type-level list. -- -- @since 0.2 -- mapAll :: forall c d xs . (forall a . Dict c a -> Dict d a) -> Dict (All c) xs -> Dict (All d) xs mapAll f Dict = (all_NP . hmap f . unAll_NP) Dict -- | Lifts a dictionary conversion over a type-level list -- of lists. -- -- @since 0.2 -- mapAll2 :: forall c d xss . (forall a . Dict c a -> Dict d a) -> Dict (All2 c) xss -> Dict (All2 d) xss mapAll2 f d @ Dict = (all2 . mapAll (mapAll f) . unAll2) d -- | If two constraints 'c' and 'd' hold over a type-level -- list 'xs', then the combination of both constraints holds -- over that list. -- -- @since 0.2 -- zipAll :: Dict (All c) xs -> Dict (All d) xs -> Dict (All (c `And` d)) xs zipAll dc @ Dict dd = all_NP (hzipWith (\ Dict Dict -> Dict) (unAll_NP dc) (unAll_NP dd)) -- | If two constraints 'c' and 'd' hold over a type-level -- list of lists 'xss', then the combination of both constraints -- holds over that list of lists. -- -- @since 0.2 -- zipAll2 :: All SListI xss => Dict (All2 c) xss -> Dict (All2 d) xss -> Dict (All2 (c `And` d)) xss zipAll2 dc dd = all_POP (hzipWith (\ Dict Dict -> Dict) (unAll_POP dc) (unAll_POP dd)) -- TODO: I currently don't understand why the All constraint in the beginning -- cannot be inferred. -- | If we have a constraint 'c' that holds over a type-level -- list 'xs', we can create a product containing proofs that -- each individual list element satisfies 'c'. -- -- @since 0.2 -- unAll_NP :: forall c xs . Dict (All c) xs -> NP (Dict c) xs unAll_NP Dict = hcpure (Proxy :: Proxy c) Dict -- | If we have a constraint 'c' that holds over a type-level -- list of lists 'xss', we can create a product of products -- containing proofs that all the inner elements satisfy 'c'. -- -- @since 0.2 -- unAll_POP :: forall c xss . Dict (All2 c) xss -> POP (Dict c) xss unAll_POP Dict = hcpure (Proxy :: Proxy c) Dict -- | If we have a product containing proofs that each element -- of 'xs' satisfies 'c', then 'All c' holds for 'xs'. -- -- @since 0.2 -- all_NP :: NP (Dict c) xs -> Dict (All c) xs all_NP Nil = Dict all_NP (Dict :* ds) = withDict (all_NP ds) Dict -- | If we have a product of products containing proofs that -- each inner element of 'xss' satisfies 'c', then 'All2 c' -- holds for 'xss'. -- -- @since 0.2 -- all_POP :: SListI xss => POP (Dict c) xss -> Dict (All2 c) xss all_POP = all2 . all_NP . hmap all_NP . unPOP -- TODO: Is the constraint necessary? -- | The constraint 'All2 c' is convertible to 'All (All c)'. -- -- @since 0.2 -- unAll2 :: Dict (All2 c) xss -> Dict (All (All c)) xss unAll2 Dict = Dict -- | The constraint 'All (All c)' is convertible to 'All2 c'. -- -- @since 0.2 -- all2 :: Dict (All (All c)) xss -> Dict (All2 c) xss all2 Dict = Dict -- | If we have an explicit dictionary, we can unwrap it and -- pass a function that makes use of it. -- -- @since 0.2 -- withDict :: Dict c a -> (c a => r) -> r withDict Dict x = x