{-# OPTIONS_GHC -Wno-orphans #-}
{-# LANGUAGE UndecidableSuperClasses #-}
{-# OPTIONS_GHC -Wno-redundant-constraints #-}
module Util.Peano
(
Peano
, Nat (Z, S)
, ToPeano
, FromPeano
, KnownPeano (..)
, SingNat (SZ, SS)
, peanoVal'
, peanoValSing
, Length
, At
, Drop
, Take
, IsLongerThan
, LongerThan
, RequireLongerThan
, IsLongerOrSameLength
, LongerOrSameLength
, RequireLongerOrSameLength
, requireLongerThan
, requireLongerOrSameLength
) where
import Data.Constraint (Dict(..))
import Data.Singletons (Sing, SingI(..))
import Data.Vinyl (Rec(..))
import Data.Vinyl.TypeLevel (Nat(..), RLength)
import GHC.TypeLits (ErrorMessage(..), TypeError)
import GHC.TypeNats (type (+), type (-))
import qualified GHC.TypeNats as GHC (Nat)
import Unsafe.Coerce (unsafeCoerce)
import Util.Type
type Peano = Nat
type family ToPeano (n :: GHC.Nat) :: Peano where
ToPeano 0 = 'Z
ToPeano a = 'S (ToPeano (a - 1))
type family FromPeano (n :: Peano) :: GHC.Nat where
FromPeano 'Z = 0
FromPeano ('S a) = 1 + FromPeano a
class KnownPeano (n :: Peano) where
peanoVal :: proxy n -> Natural
instance KnownPeano 'Z where
peanoVal :: proxy 'Z -> Natural
peanoVal _ = 0
instance KnownPeano a => KnownPeano ('S a) where
peanoVal :: proxy ('S a) -> Natural
peanoVal _ = KnownPeano a => Natural
forall (n :: Peano). KnownPeano n => Natural
peanoVal' @a Natural -> Natural -> Natural
forall a. Num a => a -> a -> a
+ 1
peanoVal' :: forall n. KnownPeano n => Natural
peanoVal' :: Natural
peanoVal' = Proxy n -> Natural
forall (n :: Peano) (proxy :: Peano -> *).
KnownPeano n =>
proxy n -> Natural
peanoVal (Proxy n
forall k (t :: k). Proxy t
Proxy @n)
peanoValSing :: forall n. KnownPeano n => Sing n -> Natural
peanoValSing :: Sing n -> Natural
peanoValSing _ = KnownPeano n => Natural
forall (n :: Peano). KnownPeano n => Natural
peanoVal' @n
instance KnownPeano a => MockableConstraint (KnownPeano a) where
provideConstraintUnsafe :: Dict (KnownPeano a)
provideConstraintUnsafe = Dict (KnownPeano a)
forall (a :: Constraint). a => Dict a
Dict
data SingNat (n :: Nat) where
SZ :: SingNat 'Z
SS :: (SingI n, KnownPeano n) => SingNat n -> SingNat ('S n)
deriving stock instance Show (SingNat (n :: Nat))
deriving stock instance Eq (SingNat (n :: Nat))
instance NFData (SingNat (n :: Nat)) where
rnf :: SingNat n -> ()
rnf SZ = ()
rnf (SS n :: SingNat n
n) = SingNat n -> ()
forall a. NFData a => a -> ()
rnf SingNat n
n
type instance Sing = SingNat
instance SingI 'Z where
sing :: Sing 'Z
sing = Sing 'Z
SingNat 'Z
SZ
instance (SingI n, KnownPeano n) => SingI ('S n) where
sing :: Sing ('S n)
sing = SingNat n -> SingNat ('S n)
forall (n :: Peano).
(SingI n, KnownPeano n) =>
SingNat n -> SingNat ('S n)
SS (SingI n => Sing n
forall k (a :: k). SingI a => Sing a
sing @n)
type family Length l :: Peano where
Length l = RLength l
type family At (n :: Peano) s where
At 'Z (x ': _) = x
At ('S n) (_ ': xs) = At n xs
At a '[] =
TypeError
('Text "You try to access to non-existing element of the stack, n = " ':<>:
'ShowType (FromPeano a))
type family Drop (n :: Peano) (s :: [k]) :: [k] where
Drop 'Z s = s
Drop ('S _) '[] = '[]
Drop ('S n) (_ ': s) = Drop n s
type family Take (n :: Peano) (s :: [k]) :: [k] where
Take 'Z _ = '[]
Take _ '[] = '[]
Take ('S n) (a ': s) = a ': Take n s
type family IsLongerThan (l :: [k]) (a :: Peano) :: Bool where
IsLongerThan (_ ': _) 'Z = 'True
IsLongerThan (_ ': xs) ('S a) = IsLongerThan xs a
IsLongerThan '[] _ = 'False
type LongerThan l a = IsLongerThan l a ~ 'True
type family IsLongerOrSameLength (l :: [k]) (a :: Peano) :: Bool where
IsLongerOrSameLength _ 'Z = 'True
IsLongerOrSameLength (_ ': xs) ('S a) = IsLongerOrSameLength xs a
IsLongerOrSameLength '[] ('S _) = 'False
type LongerOrSameLength l a = IsLongerOrSameLength l a ~ 'True
type family OfLengthWithTail (acc :: GHC.Nat) (l :: [k]) :: GHC.Nat where
OfLengthWithTail a '[] = a
OfLengthWithTail a (_ ': xs) = OfLengthWithTail (a + 1) xs
type LengthWithTail l = OfLengthWithTail 0 l
type family RequireLongerThan' (l :: [k]) (a :: Nat) :: Constraint where
RequireLongerThan' l a =
If (IsLongerThan l a)
(() :: Constraint)
(TypeError
('Text "Stack element #" ':<>: 'ShowType (FromPeano a) ':<>:
'Text " is not accessible" ':$$:
'Text "Current stack has size of only " ':<>:
'ShowType (LengthWithTail l) ':<>:
'Text ":" ':$$: 'ShowType l
))
class (RequireLongerThan' l a, LongerThan l a) =>
RequireLongerThan (l :: [k]) (a :: Peano)
instance (RequireLongerThan' l a, LongerThan l a) =>
RequireLongerThan l a
type family RequireLongerOrSameLength' (l :: [k]) (a :: Peano) :: Constraint where
RequireLongerOrSameLength' l a =
If (IsLongerOrSameLength l a)
(() :: Constraint)
(TypeError
('Text "Expected stack with length >= " ':<>: 'ShowType (FromPeano a) ':$$:
'Text "Current stack has size of only " ':<>:
'ShowType (LengthWithTail l) ':<>:
'Text ":" ':$$: 'ShowType l
))
class (RequireLongerOrSameLength' l a, LongerOrSameLength l a) =>
RequireLongerOrSameLength (l :: [k]) (a :: Peano)
instance (RequireLongerOrSameLength' l a, LongerOrSameLength l a) =>
RequireLongerOrSameLength l a
instance MockableConstraint (RequireLongerOrSameLength l a) where
provideConstraintUnsafe :: Dict (RequireLongerOrSameLength l a)
provideConstraintUnsafe = Dict (RequireLongerOrSameLength '[] 'Z)
-> Dict (RequireLongerOrSameLength l a)
forall a b. a -> b
unsafeCoerce (Dict (RequireLongerOrSameLength '[] 'Z)
-> Dict (RequireLongerOrSameLength l a))
-> Dict (RequireLongerOrSameLength '[] 'Z)
-> Dict (RequireLongerOrSameLength l a)
forall a b. (a -> b) -> a -> b
$ RequireLongerOrSameLength '[] 'Z =>
Dict (RequireLongerOrSameLength '[] 'Z)
forall (a :: Constraint). a => Dict a
Dict @(RequireLongerOrSameLength '[] 'Z)
instance MockableConstraint (RequireLongerThan l a) where
provideConstraintUnsafe :: Dict (RequireLongerThan l a)
provideConstraintUnsafe = Dict (RequireLongerThan '[()] 'Z) -> Dict (RequireLongerThan l a)
forall a b. a -> b
unsafeCoerce (Dict (RequireLongerThan '[()] 'Z) -> Dict (RequireLongerThan l a))
-> Dict (RequireLongerThan '[()] 'Z)
-> Dict (RequireLongerThan l a)
forall a b. (a -> b) -> a -> b
$ RequireLongerThan '[()] 'Z => Dict (RequireLongerThan '[()] 'Z)
forall (a :: Constraint). a => Dict a
Dict @(RequireLongerThan '[()] 'Z)
requireLongerThan
:: Rec any stk
-> Sing n
-> Maybe (Dict (RequireLongerThan stk n))
requireLongerThan :: Rec any stk -> Sing n -> Maybe (Dict (RequireLongerThan stk n))
requireLongerThan RNil _ = Maybe (Dict (RequireLongerThan stk n))
forall a. Maybe a
Nothing
requireLongerThan (_ :& _xs :: Rec any rs
_xs) SZ = Dict (RequireLongerThan stk n)
-> Maybe (Dict (RequireLongerThan stk n))
forall a. a -> Maybe a
Just Dict (RequireLongerThan stk n)
forall (a :: Constraint). a => Dict a
Dict
requireLongerThan (_ :& xs :: Rec any rs
xs) (SS n) = do
Dict (RequireLongerThan rs n)
Dict <- Rec any rs -> Sing n -> Maybe (Dict (RequireLongerThan rs n))
forall k (any :: k -> *) (stk :: [k]) (n :: Peano).
Rec any stk -> Sing n -> Maybe (Dict (RequireLongerThan stk n))
requireLongerThan Rec any rs
xs Sing n
SingNat n
n
Dict (RequireLongerThan stk n)
-> Maybe (Dict (RequireLongerThan stk n))
forall (m :: * -> *) a. Monad m => a -> m a
return Dict (RequireLongerThan stk n)
forall (a :: Constraint). a => Dict a
Dict
requireLongerOrSameLength
:: Rec any stk
-> Sing n
-> Maybe (Dict (RequireLongerOrSameLength stk n))
requireLongerOrSameLength :: Rec any stk
-> Sing n -> Maybe (Dict (RequireLongerOrSameLength stk n))
requireLongerOrSameLength _ SZ = Dict (RequireLongerOrSameLength stk n)
-> Maybe (Dict (RequireLongerOrSameLength stk n))
forall a. a -> Maybe a
Just Dict (RequireLongerOrSameLength stk n)
forall (a :: Constraint). a => Dict a
Dict
requireLongerOrSameLength RNil (SS _) = Maybe (Dict (RequireLongerOrSameLength stk n))
forall a. Maybe a
Nothing
requireLongerOrSameLength (_ :& xs :: Rec any rs
xs) (SS n) = do
Dict (RequireLongerOrSameLength rs n)
Dict <- Rec any rs
-> Sing n -> Maybe (Dict (RequireLongerOrSameLength rs n))
forall k (any :: k -> *) (stk :: [k]) (n :: Peano).
Rec any stk
-> Sing n -> Maybe (Dict (RequireLongerOrSameLength stk n))
requireLongerOrSameLength Rec any rs
xs Sing n
SingNat n
n
Dict (RequireLongerOrSameLength stk n)
-> Maybe (Dict (RequireLongerOrSameLength stk n))
forall (m :: * -> *) a. Monad m => a -> m a
return Dict (RequireLongerOrSameLength stk n)
forall (a :: Constraint). a => Dict a
Dict