packages feed

htree-0.1.1.0: src/Data/HTree/Constraint.hs

{-# LANGUAGE UndecidableSuperClasses #-}

-- | A couple of types to work with Constraints
module Data.HTree.Constraint
  ( -- * proving a constraint
    Has (..)

    -- ** synonyms for proving a constraint
  , HasTypeable
  , HasIs

    -- ** helpers to work with constraints
  , proves
  , Charge

    -- * Dict
  , type Dict
  , pattern Dict

    -- ** functions for working with 'Dict's
  , withDict
  )
where

import Data.Kind (Constraint, Type)
import Data.Proxy (Proxy (Proxy))
import Type.Reflection (Typeable)

-- | a functor useful for proving a constraint for some type
--
-- >>> import Data.Functor.Identity
-- >>> Proves @Eq (Identity (5 :: Int))
-- Proves (Identity 5)
type Has :: forall k. (k -> Constraint) -> (k -> Type) -> k -> Type
data Has c f k where
  Proves :: c k => f k -> Has c f k

-- | transform a 'Constraint' in something of kind @k -> 'Constraint'@ to be
--   able to use it in 'Has'
type Charge :: Constraint -> k -> Constraint
class c => Charge c a

instance c => Charge c a

-- | a Dict witnesses some constraint
type Dict :: Constraint -> Type
type Dict c = Has (Charge c) Proxy ()

-- | match on a 'Dict'
pattern Dict :: forall (c :: Constraint). forall. c => Dict c
pattern Dict = Proves Proxy

{-# COMPLETE Dict #-}

-- | destructing a 'Dict'
withDict :: Dict c -> (c => r) -> r
withDict d k = proves d (const k)

-- | destruct a 'Has'
proves :: Has c f a -> (c a => f a -> r) -> r
proves (Proves x) k = k x

-- | 'Has' but specialised to 'Typeable'
type HasTypeable :: (k -> Type) -> k -> Type
type HasTypeable = Has Typeable

-- | 'Has' but specialised to a constant type, @Some (HasIs k f)@ is isomorphic to @f k@
type HasIs :: k -> (k -> Type) -> k -> Type
type HasIs k = Has ((~) k)

deriving stock instance Show (f k) => Show (Has c f k)