bv-sized-0.6.0: submodules/parameterized-utils/src/Data/Parameterized/Peano.hs
{-|
This defines a type 'Peano' and 'PeanoRepr' for representing a
type-level natural at runtime. These type-level numbers are defined
inductively instead of using GHC.TypeLits.
As a result, type-level computation defined recursively over these
numbers works more smoothly. (For example, see the type-level
function Repeatn below.)
Note: as in NatRepr, the runtime representation of these type-level
natural numbers is an Int.
-}
{-# LANGUAGE ConstraintKinds #-}
{-# LANGUAGE CPP #-}
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE EmptyCase #-}
{-# LANGUAGE ExplicitNamespaces #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE GADTs #-}
{-# LANGUAGE KindSignatures #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE PolyKinds #-}
{-# LANGUAGE RankNTypes #-}
{-# LANGUAGE RoleAnnotations #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TypeOperators #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE UndecidableInstances #-}
#if MIN_VERSION_base(4,9,0)
{-# OPTIONS_GHC -fno-warn-redundant-constraints #-}
#endif
#if __GLASGOW_HASKELL__ >= 805
{-# LANGUAGE NoStarIsType #-}
#endif
module Data.Parameterized.Peano
( Peano
, Z , S
, Plus, Minus, Mul, Le, Lt, Gt, Ge, Max, Min, Repeat
, plusP, minusP, mulP, maxP, minP, repeatP
, zeroP, succP, predP
, KnownPeano
, withKnownPeano
, PeanoRepr, peanoValue
, PeanoView(..), peanoView
, viewRepr
, somePeano
, mkPeanoRepr
, maxPeano
, minPeano
-- * Re-exports
, TestEquality(..)
, (:~:)(..)
, Data.Parameterized.Some.Some
) where
import Data.Parameterized.Classes
import Data.Parameterized.DecidableEq
import Data.Parameterized.Some
import Data.Hashable
import Data.Constraint
import Data.Word
import Unsafe.Coerce(unsafeCoerce)
------------------------------------------------------------------------
-- ** Peano - a unary representation of natural numbers
data Peano = Z | S Peano
-- | Peano zero
type Z = 'Z
-- | Peano successor
type S = 'S
-- Peano numbers are more about *counting* than arithmetic.
-- They are most useful as iteration arguments and list indices
-- However, for completeness, we define a few standard
-- operations.
type family Plus (a :: Peano) (b :: Peano) :: Peano where
Plus Z b = b
Plus (S a) b = S (Plus a b)
type family Minus (a :: Peano) (b :: Peano) :: Peano where
Minus Z b = Z
Minus (S a) (S b) = Minus a b
Minus a Z = a
type family Mul (a :: Peano) (b :: Peano) :: Peano where
Mul Z b = Z
Mul (S a) b = Plus a (Mul a b)
type family Le (a :: Peano) (b :: Peano) :: Bool where
Le a a = 'True
Le Z b = 'True
Le a Z = 'False
Le (S a) (S b) = Le a b
type family Lt (a :: Peano) (b :: Peano) :: Bool where
Lt a b = Le (S a) b
type family Gt (a :: Peano) (b :: Peano) :: Bool where
Gt a b = Lt b a
type family Ge (a :: Peano) (b :: Peano) :: Bool where
Ge a b = Le b a
type family Max (a :: Peano) (b :: Peano) :: Peano where
Max Z b = b
Max a Z = a
Max (S a) (S b) = S (Max a b)
type family Min (a :: Peano) (b :: Peano) :: Peano where
Min Z b = Z
Min a Z = Z
Min (S a) (S b) = S (Min a b)
-- Apply a constructor 'f' n-times to an argument 's'
type family Repeat (m :: Peano) (f :: k -> k) (s :: k) :: k where
Repeat Z f s = s
Repeat (S m) f s = f (Repeat m f s)
------------------------------------------------------------------------
-- ** Run time representation of Peano numbers
-- | The run time value, stored as an Word64
-- As these are unary numbers, we don't worry about overflow.
newtype PeanoRepr (n :: Peano) =
PeanoRepr { peanoValue :: Word64 }
-- n is Phantom in the definition, but we don't want to allow coerce
type role PeanoRepr nominal
----------------------------------------------------------
-- | Because we have optimized the runtime representation,
-- we need to have a "view" that decomposes the representation
-- into the standard form.
data PeanoView (n :: Peano) where
ZRepr :: PeanoView Z
SRepr :: PeanoRepr n -> PeanoView (S n)
-- | Test whether a number is Zero or Successor
peanoView :: PeanoRepr n -> PeanoView n
peanoView (PeanoRepr i) =
if i == 0 then unsafeCoerce ZRepr else unsafeCoerce (SRepr (PeanoRepr (i-1)))
-- | convert the view back to the runtime representation
viewRepr :: PeanoView n -> PeanoRepr n
viewRepr ZRepr = PeanoRepr 0
viewRepr (SRepr n) = PeanoRepr (peanoValue n + 1)
----------------------------------------------------------
instance Hashable (PeanoRepr n) where
hashWithSalt i (PeanoRepr x) = hashWithSalt i x
instance Eq (PeanoRepr m) where
_ == _ = True
instance TestEquality PeanoRepr where
testEquality (PeanoRepr m) (PeanoRepr n)
| m == n = Just (unsafeCoerce Refl)
| otherwise = Nothing
instance DecidableEq PeanoRepr where
decEq (PeanoRepr m) (PeanoRepr n)
| m == n = Left $ unsafeCoerce Refl
| otherwise = Right $
\x -> seq x $ error "Impossible [DecidableEq on PeanoRepr]"
instance OrdF PeanoRepr where
compareF (PeanoRepr m) (PeanoRepr n)
| m < n = unsafeCoerce LTF
| m == n = unsafeCoerce EQF
| otherwise = unsafeCoerce GTF
instance PolyEq (PeanoRepr m) (PeanoRepr n) where
polyEqF x y = (\Refl -> Refl) <$> testEquality x y
-- Display as digits, not in unary
instance Show (PeanoRepr p) where
show p = show (peanoValue p)
instance ShowF PeanoRepr
instance HashableF PeanoRepr where
hashWithSaltF = hashWithSalt
----------------------------------------------------------
-- * Implicit runtime Peano numbers
type KnownPeano = KnownRepr PeanoRepr
instance KnownRepr PeanoRepr Z where
knownRepr = viewRepr ZRepr
instance (KnownRepr PeanoRepr n) => KnownRepr PeanoRepr (S n) where
knownRepr = viewRepr (SRepr knownRepr)
newtype DI a = Don'tInstantiate (KnownPeano a => Dict (KnownPeano a))
peanoInstance :: forall a . PeanoRepr a -> Dict (KnownPeano a)
peanoInstance s = with_sing_i Dict
where
with_sing_i :: (KnownPeano a => Dict (KnownPeano a)) -> Dict (KnownPeano a)
with_sing_i si = unsafeCoerce (Don'tInstantiate si) s
-- | convert an explicit number to an implicit number
withKnownPeano :: forall n r. PeanoRepr n -> (KnownPeano n => r) -> r
withKnownPeano si r = case peanoInstance si of
Dict -> r
----------------------------------------------------------
-- * Operations on runtime numbers
-- | zero
zeroP :: PeanoRepr Z
zeroP = PeanoRepr 0
-- | Successor, Increment
succP :: PeanoRepr n -> PeanoRepr (S n)
succP (PeanoRepr i) = PeanoRepr (i+1)
-- | Get the predecessor (decrement)
predP :: PeanoRepr (S n) -> PeanoRepr n
predP (PeanoRepr i) = PeanoRepr (i-1)
plusP :: PeanoRepr a -> PeanoRepr b -> PeanoRepr (Plus a b)
plusP (PeanoRepr a) (PeanoRepr b) = PeanoRepr (a + b)
minusP :: PeanoRepr a -> PeanoRepr b -> PeanoRepr (Minus a b)
minusP (PeanoRepr a) (PeanoRepr b) = PeanoRepr (a - b)
mulP :: PeanoRepr a -> PeanoRepr b -> PeanoRepr (Mul a b)
mulP (PeanoRepr a) (PeanoRepr b) = PeanoRepr (a * b)
maxP :: PeanoRepr a -> PeanoRepr b -> PeanoRepr (Max a b)
maxP (PeanoRepr a) (PeanoRepr b) = PeanoRepr (max a b)
minP :: PeanoRepr a -> PeanoRepr b -> PeanoRepr (Min a b)
minP (PeanoRepr a) (PeanoRepr b) = PeanoRepr (min a b)
repeatP :: PeanoRepr m -> (forall a. repr a -> repr (f a)) -> repr s -> repr (Repeat m f s)
repeatP n f s = case peanoView n of
ZRepr -> s
SRepr m -> f (repeatP m f s)
------------------------------------------------------------------------
-- * Some PeanoRepr
-- | Convert a Word64 to a PeanoRepr
mkPeanoRepr :: Word64 -> Some PeanoRepr
mkPeanoRepr n = Some (PeanoRepr n)
-- | Turn an @Integral@ value into a @PeanoRepr@. Returns @Nothing@
-- if the given value is negative.
somePeano :: Integral a => a -> Maybe (Some PeanoRepr)
somePeano x | x >= 0 = Just . Some . PeanoRepr $! fromIntegral x
somePeano _ = Nothing
-- | Return the maximum of two representations.
maxPeano :: PeanoRepr m -> PeanoRepr n -> Some PeanoRepr
maxPeano x y
| peanoValue x >= peanoValue y = Some x
| otherwise = Some y
-- | Return the minimum of two representations.
minPeano :: PeanoRepr m -> PeanoRepr n -> Some PeanoRepr
minPeano x y
| peanoValue y >= peanoValue x = Some x
| otherwise = Some y
------------------------------------------------------------------------
-- LocalWords: PeanoRepr withKnownPeano runtime Peano unary