decidable-0.3.1.0: src/Data/Type/Universe.hs
{-# LANGUAGE CPP #-}
{-# LANGUAGE DeriveDataTypeable #-}
{-# LANGUAGE DeriveFunctor #-}
{-# LANGUAGE DeriveGeneric #-}
{-# LANGUAGE DeriveTraversable #-}
{-# LANGUAGE EmptyCase #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE GADTs #-}
{-# LANGUAGE InstanceSigs #-}
{-# LANGUAGE LambdaCase #-}
{-# LANGUAGE RankNTypes #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE StandaloneDeriving #-}
{-# LANGUAGE TemplateHaskell #-}
{-# LANGUAGE TypeApplications #-}
{-# LANGUAGE TypeFamilyDependencies #-}
{-# LANGUAGE TypeInType #-}
{-# LANGUAGE TypeOperators #-}
-- |
-- Module : Data.Type.Universe
-- Copyright : (c) Justin Le 2018
-- License : BSD3
--
-- Maintainer : justin@jle.im
-- Stability : experimental
-- Portability : non-portable
--
-- A type family for "containers", intended for allowing lifting of
-- predicates on @k@ to be predicates on containers @f k@.
--
module Data.Type.Universe (
-- * Universe
Elem, In, Universe(..)
, singAll
-- ** Instances
, Index(..), IJust(..), IRight(..), NEIndex(..), ISnd(..), IIdentity(..)
-- ** Predicates
, All, WitAll(..), NotAll
, Any, WitAny(..), None
, Null, NotNull
-- *** Specialized
, IsJust, IsNothing, IsRight, IsLeft
-- * Decisions and manipulations
, decideAny, decideAll
, genAll, igenAll
, splitSing
, pickElem
) where
import Data.Either.Singletons hiding (IsLeft, IsRight)
import Data.Functor.Identity
import Data.Functor.Identity.Singletons
import Data.Kind
import Data.List.NonEmpty (NonEmpty(..))
import Data.List.Singletons hiding (Elem, ElemSym0, ElemSym1, ElemSym2, All, Any, Null)
import Data.Maybe.Singletons hiding (IsJust, IsNothing)
import Data.Singletons
import Data.Singletons.Decide
import Data.Tuple.Singletons
import Data.Type.Functor.Product
import Data.Type.Predicate
import Data.Type.Predicate.Logic
import GHC.Generics ((:*:)(..))
import Prelude hiding (any, all)
import qualified Data.List.NonEmpty.Singletons as NE
-- | A @'WitAny' p as@ is a witness that, for at least one item @a@ in the
-- type-level collection @as@, the predicate @p a@ is true.
data WitAny f :: (k ~> Type) -> f k -> Type where
WitAny :: Elem f as a -> p @@ a -> WitAny f p as
-- | An @'Any' f p@ is a predicate testing a collection @as :: f a@ for the
-- fact that at least one item in @as@ satisfies @p@. Represents the
-- "exists" quantifier over a given universe.
--
-- This is mostly useful for its 'Decidable' and 'TFunctor' instances,
-- which lets you lift predicates on @p@ to predicates on @'Any' f p@.
data Any f :: Predicate k -> Predicate (f k)
type instance Apply (Any f p) as = WitAny f p as
-- | A @'WitAll' p as@ is a witness that the predicate @p a@ is true for all
-- items @a@ in the type-level collection @as@.
newtype WitAll f p (as :: f k) = WitAll { runWitAll :: forall a. Elem f as a -> p @@ a }
-- | An @'All' f p@ is a predicate testing a collection @as :: f a@ for the
-- fact that /all/ items in @as@ satisfy @p@. Represents the "forall"
-- quantifier over a given universe.
--
-- This is mostly useful for its 'Decidable', 'Provable', and 'TFunctor'
-- instances, which lets you lift predicates on @p@ to predicates on @'All'
-- f p@.
data All f :: Predicate k -> Predicate (f k)
type instance Apply (All f p) as = WitAll f p as
instance (Universe f, Decidable p) => Decidable (Any f p) where
decide = decideAny @f @_ @p $ decide @p
instance (Universe f, Decidable p) => Decidable (All f p) where
decide = decideAll @f @_ @p $ decide @p
instance (Universe f, Provable p) => Decidable (NotNull f ==> Any f p) where
instance Provable p => Provable (NotNull f ==> Any f p) where
prove _ (WitAny i s) = WitAny i (prove @p s)
instance (Universe f, Provable p) => Provable (All f p) where
prove xs = WitAll $ \i -> prove @p (indexSing i xs)
instance Universe f => TFunctor (Any f) where
tmap f xs (WitAny i x) = WitAny i (f (indexSing i xs) x)
instance Universe f => TFunctor (All f) where
tmap f xs a = WitAll $ \i -> f (indexSing i xs) (runWitAll a i)
instance Universe f => DFunctor (All f) where
dmap f xs a = idecideAll (\i x -> f x (runWitAll a i)) xs
-- | Typeclass for a type-level container that you can quantify or lift
-- type-level predicates over.
class FProd f => Universe (f :: Type -> Type) where
-- | 'decideAny', but providing an 'Elem'.
idecideAny
:: forall k (p :: k ~> Type) (as :: f k). ()
=> (forall a. Elem f as a -> Sing a -> Decision (p @@ a)) -- ^ predicate on value
-> (Sing as -> Decision (Any f p @@ as)) -- ^ predicate on collection
-- | 'decideAll', but providing an 'Elem'.
idecideAll
:: forall k (p :: k ~> Type) (as :: f k). ()
=> (forall a. Elem f as a -> Sing a -> Decision (p @@ a)) -- ^ predicate on value
-> (Sing as -> Decision (All f p @@ as)) -- ^ predicate on collection
allProd
:: forall p g. ()
=> (forall a. Sing a -> p @@ a -> g a)
-> All f p --> TyPred (Prod f g)
prodAll
:: forall p g as. ()
=> (forall a. g a -> p @@ a)
-> Prod f g as
-> All f p @@ as
-- | Predicate that a given @as :: f k@ is empty and has no items in it.
type Null f = (None f Evident :: Predicate (f k))
-- | Predicate that a given @as :: f k@ is not empty, and has at least one
-- item in it.
type NotNull f = (Any f Evident :: Predicate (f k))
-- | A @'None' f p@ is a predicate on a collection @as@ that no @a@ in @as@
-- satisfies predicate @p@.
type None f p = (Not (Any f p) :: Predicate (f k))
-- | A @'NotAll' f p@ is a predicate on a collection @as@ that at least one
-- @a@ in @as@ does not satisfy predicate @p@.
type NotAll f p = (Not (All f p) :: Predicate (f k))
-- | Lifts a predicate @p@ on an individual @a@ into a predicate that on
-- a collection @as@ that is true if and only if /any/ item in @as@
-- satisfies the original predicate.
--
-- That is, it turns a predicate of kind @k ~> Type@ into a predicate
-- of kind @f k ~> Type@.
--
-- Essentially tests existential quantification.
decideAny
:: forall f k (p :: k ~> Type). Universe f
=> Decide p -- ^ predicate on value
-> Decide (Any f p) -- ^ predicate on collection
decideAny f = idecideAny (const f)
-- | Lifts a predicate @p@ on an individual @a@ into a predicate that on
-- a collection @as@ that is true if and only if /all/ items in @as@
-- satisfies the original predicate.
--
-- That is, it turns a predicate of kind @k ~> Type@ into a predicate
-- of kind @f k ~> Type@.
--
-- Essentially tests universal quantification.
decideAll
:: forall f k (p :: k ~> Type). Universe f
=> Decide p -- ^ predicate on value
-> Decide (All f p) -- ^ predicate on collection
decideAll f = idecideAll (const f)
-- | Split a @'Sing' as@ into a proof that all @a@ in @as@ exist.
splitSing
:: forall f k (as :: f k). Universe f
=> Sing as
-> All f (TyPred Sing) @@ as
splitSing = prodAll id . singProd
-- | Automatically generate a witness for a member, if possible
pickElem
:: forall f k (as :: f k) a. (Universe f, SingI as, SingI a, SDecide k)
=> Decision (Elem f as a)
pickElem = mapDecision (\case WitAny i Refl -> i)
(\case i -> WitAny i Refl)
. decide @(Any f (TyPred ((:~:) a)))
$ sing
-- | 'genAll', but providing an 'Elem'.
igenAll
:: forall f k (p :: k ~> Type) (as :: f k). Universe f
=> (forall a. Elem f as a -> Sing a -> p @@ a) -- ^ always-true predicate on value
-> (Sing as -> All f p @@ as) -- ^ always-true predicate on collection
igenAll f = prodAll (\(i :*: x) -> f i x) . imapProd (:*:) . singProd
-- | If @p a@ is true for all values @a@ in @as@, then we have @'All'
-- p as@. Basically witnesses the definition of 'All'.
genAll
:: forall f k (p :: k ~> Type). Universe f
=> Prove p -- ^ always-true predicate on value
-> Prove (All f p) -- ^ always-true predicate on collection
genAll f = prodAll f . singProd
-- | Split a @'Sing' as@ into a proof that all @a@ in @as@ exist.
singAll
:: forall f k (as :: f k). Universe f
=> Sing as
-> All f Evident @@ as
singAll = prodAll id . singProd
-- | Test that a 'Maybe' is 'Just'.
--
-- @since 0.1.2.0
type IsJust = (NotNull Maybe :: Predicate (Maybe k))
-- | Test that a 'Maybe' is 'Nothing'.
--
-- @since 0.1.2.0
type IsNothing = (Null Maybe :: Predicate (Maybe k))
-- | Test that an 'Either' is 'Right'
--
-- @since 0.1.2.0
type IsRight = (NotNull (Either j) :: Predicate (Either j k))
-- | Test that an 'Either' is 'Left'
--
-- @since 0.1.2.0
type IsLeft = (Null (Either j) :: Predicate (Either j k))
instance Universe [] where
idecideAny
:: forall k (p :: k ~> Type) (as :: [k]). ()
=> (forall a. Elem [] as a -> Sing a -> Decision (p @@ a))
-> Sing as
-> Decision (Any [] p @@ as)
idecideAny f = \case
SNil -> Disproved $ \case
WitAny i _ -> case i of {}
x `SCons` xs -> case f IZ x of
Proved p -> Proved $ WitAny IZ p
Disproved v -> case idecideAny @[] @_ @p (f . IS) xs of
Proved (WitAny i p) -> Proved $ WitAny (IS i) p
Disproved vs -> Disproved $ \case
WitAny IZ p -> v p
WitAny (IS i) p -> vs (WitAny i p)
idecideAll
:: forall k (p :: k ~> Type) (as :: [k]). ()
=> (forall a. Elem [] as a -> Sing a -> Decision (p @@ a))
-> Sing as
-> Decision (All [] p @@ as)
idecideAll f = \case
SNil -> Proved $ WitAll $ \case {}
x `SCons` xs -> case f IZ x of
Proved p -> case idecideAll @[] @_ @p (f . IS) xs of
Proved a -> Proved $ WitAll $ \case
IZ -> p
IS i -> runWitAll a i
Disproved v -> Disproved $ \a -> v $ WitAll (runWitAll a . IS)
Disproved v -> Disproved $ \a -> v $ runWitAll a IZ
allProd
:: forall p g. ()
=> (forall a. Sing a -> p @@ a -> g a)
-> All [] p --> TyPred (Prod [] g)
allProd f = go
where
go :: Sing as -> WitAll [] p as -> Prod [] g as
go = \case
SNil -> \_ -> RNil
x `SCons` xs -> \a -> f x (runWitAll a IZ)
:& go xs (WitAll (runWitAll a . IS))
prodAll
:: forall p g as. ()
=> (forall a. g a -> p @@ a)
-> Prod [] g as
-> All [] p @@ as
prodAll f = go
where
go :: Prod [] g bs -> All [] p @@ bs
go = \case
RNil -> WitAll $ \case {}
x :& xs -> WitAll $ \case
IZ -> f x
IS i -> runWitAll (go xs) i
instance Universe Maybe where
idecideAny f = \case
SNothing -> Disproved $ \case WitAny i _ -> case i of {}
SJust x -> case f IJust x of
Proved p -> Proved $ WitAny IJust p
Disproved v -> Disproved $ \case
WitAny IJust p -> v p
idecideAll f = \case
SNothing -> Proved $ WitAll $ \case {}
SJust x -> case f IJust x of
Proved p -> Proved $ WitAll $ \case IJust -> p
Disproved v -> Disproved $ \a -> v $ runWitAll a IJust
allProd f = \case
SNothing -> \_ -> PNothing
SJust x -> \a -> PJust (f x (runWitAll a IJust))
prodAll f = \case
PNothing -> WitAll $ \case {}
PJust x -> WitAll $ \case IJust -> f x
instance Universe (Either j) where
idecideAny f = \case
SLeft _ -> Disproved $ \case WitAny i _ -> case i of {}
SRight x -> case f IRight x of
Proved p -> Proved $ WitAny IRight p
Disproved v -> Disproved $ \case
WitAny IRight p -> v p
idecideAll f = \case
SLeft _ -> Proved $ WitAll $ \case {}
SRight x -> case f IRight x of
Proved p -> Proved $ WitAll $ \case IRight -> p
Disproved v -> Disproved $ \a -> v $ runWitAll a IRight
allProd f = \case
SLeft w -> \_ -> PLeft w
SRight x -> \a -> PRight (f x (runWitAll a IRight))
prodAll f = \case
PLeft _ -> WitAll $ \case {}
PRight x -> WitAll $ \case IRight -> f x
instance Universe NonEmpty where
idecideAny
:: forall k (p :: k ~> Type) (as :: NonEmpty k). ()
=> (forall a. Elem NonEmpty as a -> Sing a -> Decision (p @@ a))
-> Sing as
-> Decision (Any NonEmpty p @@ as)
idecideAny f (x NE.:%| xs) = case f NEHead x of
Proved p -> Proved $ WitAny NEHead p
Disproved v -> case idecideAny @[] @_ @p (f . NETail) xs of
Proved (WitAny i p) -> Proved $ WitAny (NETail i) p
Disproved vs -> Disproved $ \case
WitAny i p -> case i of
NEHead -> v p
NETail i' -> vs (WitAny i' p)
idecideAll
:: forall k (p :: k ~> Type) (as :: NonEmpty k). ()
=> (forall a. Elem NonEmpty as a -> Sing a -> Decision (p @@ a))
-> Sing as
-> Decision (All NonEmpty p @@ as)
idecideAll f (x NE.:%| xs) = case f NEHead x of
Proved p -> case idecideAll @[] @_ @p (f . NETail) xs of
Proved ps -> Proved $ WitAll $ \case
NEHead -> p
NETail i -> runWitAll ps i
Disproved v -> Disproved $ \a -> v $ WitAll (runWitAll a . NETail)
Disproved v -> Disproved $ \a -> v $ runWitAll a NEHead
allProd
:: forall p g. ()
=> (forall a. Sing a -> p @@ a -> g a)
-> All NonEmpty p --> TyPred (Prod NonEmpty g)
allProd f (x NE.:%| xs) a =
f x (runWitAll a NEHead)
:&| allProd @[] @p f xs (WitAll (runWitAll a . NETail))
prodAll
:: forall p g as. ()
=> (forall a. g a -> p @@ a)
-> Prod NonEmpty g as
-> All NonEmpty p @@ as
prodAll f (x :&| xs) = WitAll $ \case
NEHead -> f x
NETail i -> runWitAll (prodAll @[] @p f xs) i
instance Universe ((,) j) where
idecideAny f (STuple2 _ x) = case f ISnd x of
Proved p -> Proved $ WitAny ISnd p
Disproved v -> Disproved $ \case WitAny ISnd p -> v p
idecideAll f (STuple2 _ x) = case f ISnd x of
Proved p -> Proved $ WitAll $ \case ISnd -> p
Disproved v -> Disproved $ \a -> v $ runWitAll a ISnd
allProd f (STuple2 w x) a = PTup w $ f x (runWitAll a ISnd)
prodAll f (PTup _ x) = WitAll $ \case ISnd -> f x
-- | The single-pointed universe.
instance Universe Identity where
idecideAny f (SIdentity x) =
mapDecision (WitAny IId)
(\case WitAny IId p -> p)
$ f IId x
idecideAll f (SIdentity x) =
mapDecision (\p -> WitAll $ \case IId -> p)
(\y -> runWitAll y IId)
$ f IId x
allProd f (SIdentity x) a = PIdentity $ f x (runWitAll a IId)
prodAll f (PIdentity x) = WitAll $ \case IId -> f x