-- Copyright 2016 Julian Hall. See LICENSE file at top level for details. -- TODO - are we still using all of these extensions? {-# LANGUAGE GADTs,ConstraintKinds,RankNTypes,FlexibleInstances #-} {-# LANGUAGE ScopedTypeVariables,KindSignatures,PolyKinds #-} {-# LANGUAGE TypeFamilies,MultiParamTypeClasses,UndecidableInstances #-} {-# LANGUAGE DataKinds,TypeOperators,FlexibleContexts #-} -- | Provides a type for keeping a list of typeclass dictionaries that may or -- may not exist for a given type, and utility functions for working with the -- list. module Data.Type.LTDict where import GHC.Exts (Constraint) import Data.ConstrainedDynamic(ClassConstraint(..)) import Data.Typeable import Data.Type.HasClass -- | Optionally provide a dictionary for a constraint and a type. data MaybeHasClass :: (* -> Constraint) -> * -> * where JustHasClass :: cs t => MaybeHasClass cs t DoesNotHaveClass :: MaybeHasClass cs t -- | Maintain a list of dictionaries encapsulating many constraints -- over a single type (where they are available). data LTDict :: [* -> Constraint] -> * -> * where LTDCons :: MaybeHasClass cs t -> LTDict css t -> LTDict (cs ': css) t LTDNil :: LTDict '[] t class TypeConstraintBuilder mhctype (cs :: * -> Constraint) t (flag :: k) where buildTypeConstraint :: proxy flag -> mhctype cs t instance (HasClass cs t True) => TypeConstraintBuilder MaybeHasClass cs t True where buildTypeConstraint _ = case classDict (Proxy :: Proxy cs) (Proxy :: Proxy t) (Proxy :: Proxy true) of TDict -> JustHasClass instance {-# OVERLAPPABLE #-} f ~ False => TypeConstraintBuilder MaybeHasClass cs t f where buildTypeConstraint _ = DoesNotHaveClass -- | Produce an appropriate 'MaybeHasClass' value for the context. -- Callers should ensure that appropriate 'HasClass' instances are in scope -- at the point of calling, or declare them as part of their context. checkClass :: forall cs t f . (HasClass cs t f, TypeConstraintBuilder MaybeHasClass cs t f) => MaybeHasClass cs t checkClass = buildTypeConstraint (Proxy :: Proxy f) -- | A class for building objects that recurse over a list of constraints. -- Instances are provided for building 'LTDict' values, but could be used -- for other values too. class LTDictBuilder dtype (css :: [(* -> Constraint)]) t where buildLTDict :: dtype css t instance (HasClass cs t f, TypeConstraintBuilder MaybeHasClass cs t f, LTDictBuilder LTDict css t) => LTDictBuilder LTDict (cs ': css) t where buildLTDict = LTDCons checkClass (buildLTDict :: LTDict css t) instance LTDictBuilder LTDict '[] t where buildLTDict = LTDNil -- | Functions for extracting the list of constraints in an LTDict -- as 'TypeRep's of 'ClassConstraint' types. class LTDictConstraintLister (css :: [(* -> Constraint)]) where -- Return all constraints in the list getAllConstraints :: LTDict css a -> [TypeRep] -- Return only the matched constraints in the list getMatchedConstraints :: LTDict css a -> [TypeRep] -- Return only the unmatched constraints in the list getUnmatchedConstraints :: LTDict css a -> [TypeRep] instance LTDictConstraintLister '[] where getAllConstraints _ = [] getMatchedConstraints _ = [] getUnmatchedConstraints _ = [] instance (Typeable cs, LTDictConstraintLister css) => LTDictConstraintLister (cs ': css) where getAllConstraints (LTDCons _ t) = typeOf (ClassConstraint :: ClassConstraint cs) : getAllConstraints t getMatchedConstraints (LTDCons JustHasClass t) = typeOf (ClassConstraint :: ClassConstraint cs) : getMatchedConstraints t getMatchedConstraints (LTDCons DoesNotHaveClass t) = getMatchedConstraints t getUnmatchedConstraints (LTDCons DoesNotHaveClass t) = typeOf (ClassConstraint :: ClassConstraint cs) : getUnmatchedConstraints t getUnmatchedConstraints (LTDCons JustHasClass t) = getUnmatchedConstraints t -- | A class for handling searches for a specific single constraint in the -- list. class LTDictSearch (css :: [(* -> Constraint)]) (cs :: * -> Constraint) where ltdSearch :: proxy cs -> LTDict css a -> MaybeHasClass cs a instance LTDictSearch '[] cs where ltdSearch _ _ = DoesNotHaveClass instance LTDictSearch (cs ': css) cs where ltdSearch _ (LTDCons m _) = m instance {-# OVERLAPPABLE #-} LTDictSearch css cs => LTDictSearch (unmatched ': css) cs where ltdSearch p (LTDCons _ t) = ltdSearch p t