{-# LANGUAGE UndecidableInstances #-} {-# LANGUAGE QuantifiedConstraints #-} {-# LANGUAGE MultiParamTypeClasses #-} {-# LANGUAGE FlexibleInstances #-} {-# LANGUAGE TypeSynonymInstances #-} {-# LANGUAGE ConstraintKinds #-} {-# LANGUAGE GADTs #-} {-# LANGUAGE TypeFamilies #-} {-# LANGUAGE DataKinds #-} {-# LANGUAGE PolyKinds #-} {-# LANGUAGE TypeOperators #-} {-# LANGUAGE RankNTypes #-} {-# LANGUAGE TypeApplications #-} {-# LANGUAGE ScopedTypeVariables #-} {-# LANGUAGE PatternSynonyms #-} module Generics.Simplistic.Util ( -- * Utility Functions and Types (&&&) , (***) , (:->) , (<.>) -- * Poly-kind indexed product functionality , (:*:)(..) , Delta , curry' , uncurry' , delta , deltaMap -- * Poly-kind indexed sums , Sum(..) , either' , either'' -- * Constraints , Implies , Trivial -- * Higher-order Eq and Show , EqHO(..) , ShowHO(..) -- * Existential Wrapper , Exists(..) , exMap , exMapM , exElim -- * Elem functionality , Elem , NotElem , HasElem(..) , ElemPrf(..) , IsElem, sameTy -- * Witnessing and All constraints , All , Witness(..) , witness , witnessPrf , weq , wshow ) where import Data.Kind (Constraint) import Data.Proxy (Proxy(..)) import Data.Functor.Sum import Data.Functor.Const import Data.Type.Equality import Control.Arrow ((***) , (&&&)) import GHC.Generics ((:*:)(..), V1 , U1(..)) -- |Lifted curry curry' :: ((f :*: g) x -> a) -> f x -> g x -> a curry' f fx gx = f (fx :*: gx) -- |Lifted uncurry uncurry' :: (f x -> g x -> a) -> (f :*: g) x -> a uncurry' f (fx :*: gx) = f fx gx -- |Natural transformations type f :-> g = forall n . f n -> g n -- |Diagonal indexed functor type Delta f = f :*: f -- |Duplicates its argument delta :: f :-> Delta f delta fx = fx :*: fx -- |Applies the same function to both components of the pair deltaMap :: (f :-> g) -> Delta f :-> Delta g deltaMap f (x :*: y) = f x :*: f y -- |Higher-order sum eliminator either' :: (f :-> r) -> (g :-> r) -> Sum f g :-> r either' f _ (InL x) = f x either' _ g (InR x) = g x -- |Just like 'either'', but the result type is of kind Star either'' :: (forall x . f x -> a) -> (forall y . g y -> a) -> Sum f g r -> a either'' f g = getConst . either' (Const . f) (Const . g) infixr 8 <.> -- |Kleisli Composition (<.>) :: (Monad m) => (b -> m c) -> (a -> m b) -> a -> m c f <.> g = (>>= f) . g -- |Constraint implication class (c => d) => Implies c d instance (c => d) => Implies c d -- |Trivial constraint class Trivial c instance Trivial c -- |Higher order , poly kinded, version of 'Eq' class EqHO (f :: ki -> *) where eqHO :: forall k . f k -> f k -> Bool instance Eq a => EqHO (Const a) where eqHO (Const a) (Const b) = a == b instance (EqHO f, EqHO g) => EqHO (f :*: g) where eqHO (fx :*: gx) (fy :*: gy) = eqHO fx fy && eqHO gx gy instance (EqHO f, EqHO g) => EqHO (Sum f g) where eqHO (InL fx) (InL fy) = eqHO fx fy eqHO (InR gx) (InR gy) = eqHO gx gy eqHO _ _ = False instance EqHO V1 where eqHO _ _ = True instance EqHO U1 where eqHO _ _ = True -- |Higher order, poly kinded, version of 'Show'; We provide -- the same 'showsPrec' mechanism. The documentation of "Text.Show" -- has a good example of the correct usage of 'showsPrec': -- -- > -- > infixr 5 :^: -- > data Tree a = Leaf a | Tree a :^: Tree a -- > -- > instance (Show a) => Show (Tree a) where -- > showsPrec d (Leaf m) = showParen (d > app_prec) $ -- > showString "Leaf " . showsPrec (app_prec+1) m -- > where app_prec = 10 -- > -- > showsPrec d (u :^: v) = showParen (d > up_prec) $ -- > showsPrec (up_prec+1) u . -- > showString " :^: " . -- > showsPrec (up_prec+1) v -- > where up_prec = 5 -- class ShowHO (f :: ki -> *) where showHO :: forall k . f k -> String showsPrecHO :: forall k . Int -> f k -> ShowS {-# MINIMAL showHO | showsPrecHO #-} showHO fx = showsPrecHO 0 fx "" showsPrecHO _ fx s = showHO fx ++ s instance Show a => ShowHO (Const a) where showsPrecHO d (Const a) = showParen (d > app_prec) $ showString "Const " . showsPrec (app_prec + 1) a where app_prec = 10 instance (ShowHO f , ShowHO g) => ShowHO (f :*: g) where showsPrecHO d (x :*: y) = showParen (d > app_prec) $ showsPrecHO (app_prec+1) x . showString " :*: " . showsPrecHO (app_prec+1) y where app_prec = 10 instance (ShowHO f , ShowHO g) => ShowHO (Sum f g) where showsPrecHO d (InL fx) = showParen (d > app_prec) $ showString "InL " . showsPrecHO (app_prec + 1) fx where app_prec = 10 showsPrecHO d (InR gx) = showParen (d > app_prec) $ showString "InR " . showsPrecHO (app_prec + 1) gx where app_prec = 10 -- |Existential type wrapper. This comesin particularly -- handy when we want to add mrsop terms to -- some container. See "Generics.MRSOP.Holes.Unify" for example. data Exists (f :: k -> *) :: * where Exists :: f x -> Exists f -- |Maps over 'Exists' exMap :: (forall x . f x -> g x) -> Exists f -> Exists g exMap f (Exists x) = Exists (f x) -- |Maps a monadic actino over 'Exists' exMapM :: (Monad m) => (forall x . f x -> m (g x)) -> Exists f -> m (Exists g) exMapM f (Exists x) = Exists <$> f x -- |eliminates an 'Exists' exElim :: (forall x . f x -> a) -> Exists f -> a exElim f (Exists x) = f x instance ShowHO f => Show (Exists f) where show = exElim showHO -- Boolean predicate about being element of a list type family IsElem (a :: k) (as :: [ k ]) :: Bool where IsElem a (a ': as) = 'True IsElem a (b ': as) = IsElem a as IsElem a '[] = 'False -- An actual proof that something is an element data ElemPrf a as where Here :: ElemPrf a (a ': as) There :: ElemPrf a as -> ElemPrf a (b ': as) -- Constructing these proofs class HasElem a as where hasElem :: ElemPrf a as instance {-# OVERLAPPING #-} HasElem a (a ': as) where hasElem = Here instance {-# OVERLAPPABLE #-} (HasElem a as) => HasElem a (b ': as) where hasElem = There hasElem -- |We will carry constructive information on the -- constraint. Forcing 'IsElem' to true type Elem a as = (IsElem a as ~ 'True , HasElem a as) -- |Negation of 'Elem' type NotElem a as = IsElem a as ~ 'False -- |Returns whether two types are the same, given that -- both belong to the same list. sameTy :: forall fam x y . (Elem x fam , Elem y fam) => Proxy fam -> Proxy x -> Proxy y -> Maybe (x :~: y) sameTy _ _ _ = go (hasElem :: ElemPrf x fam) (hasElem :: ElemPrf y fam) where go :: ElemPrf x fam' -> ElemPrf b fam' -> Maybe (x :~: b) go Here Here = Just Refl go (There rr) (There y) = go rr y go _ _ = Nothing type family All (c :: k -> Constraint) (xs :: [k]) :: Constraint where All c '[] = () All c (x ': xs) = (c x , All c xs) -- |Carries information about @x@ being an instance of @c@ data Witness c x where Witness :: (c x) => Witness c x -- |Provides the witness that @x@ is an instance of @c@ witness :: forall x xs c . (HasElem x xs , All c xs) => Proxy xs -> Witness c x witness _ = witnessPrf (hasElem :: ElemPrf x xs) -- |Fetches the 'Eq' instance for an element of a list weq :: forall x xs . (All Eq xs , Elem x xs) => Proxy xs -> x -> x -> Bool weq p = case witness p :: Witness Eq x of Witness -> (==) -- |Fetches the 'Eq' instance for an element of a list wshow :: forall x xs . (All Show xs , Elem x xs) => Proxy xs -> x -> String wshow p = case witness p :: Witness Show x of Witness -> show -- |Provides the witness that @x@ is an instance of @c@ witnessPrf :: (All c xs) => ElemPrf x xs -> Witness c x witnessPrf Here = Witness witnessPrf (There p) = witnessPrf p