decidable-0.3.1.1: src/Data/Type/Universe.hs
{-# LANGUAGE CPP #-}
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE EmptyCase #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE GADTs #-}
{-# LANGUAGE InstanceSigs #-}
{-# LANGUAGE LambdaCase #-}
{-# LANGUAGE PolyKinds #-}
{-# LANGUAGE RankNTypes #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TypeApplications #-}
{-# LANGUAGE TypeFamilyDependencies #-}
{-# 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 qualified Data.List.NonEmpty.Singletons as NE
import Data.List.Singletons hiding (
All,
Any,
Elem,
ElemSym0,
ElemSym1,
ElemSym2,
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 (all, any)
-- | 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)
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).
() =>
-- | predicate on value
(forall a. Elem f as a -> Sing a -> Decision (p @@ a)) ->
-- | predicate on collection
(Sing as -> Decision (Any f p @@ as))
-- | 'decideAll', but providing an 'Elem'.
idecideAll ::
forall k (p :: k ~> Type) (as :: f k).
() =>
-- | predicate on value
(forall a. Elem f as a -> Sing a -> Decision (p @@ a)) ->
-- | predicate on collection
(Sing as -> Decision (All f p @@ as))
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 =>
-- | predicate on value
Decide p ->
-- | predicate on collection
Decide (Any f p)
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 =>
-- | predicate on value
Decide p ->
-- | predicate on collection
Decide (All f p)
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 =>
-- | always-true predicate on value
(forall a. Elem f as a -> Sing a -> p @@ a) ->
-- | always-true predicate on collection
(Sing as -> All f p @@ as)
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 =>
-- | always-true predicate on value
Prove p ->
-- | always-true predicate on collection
Prove (All f p)
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