{-# LANGUAGE StrictData #-} {-# LANGUAGE UndecidableInstances #-} {-# LANGUAGE UndecidableSuperClasses #-} -- | This module provides a type-level representation for term-level -- 'P.Rational's. This type-level representation is also named 'P.Rational', -- So import this module qualified to avoid name conflicts. -- -- @ -- import "KindRational" qualified as KR -- @ -- -- The implementation details are the same as the ones for type-level 'Natural's -- in "GHC.TypeNats" as of @base-4.18@, and it will continue to evolve together -- with @base@, trying to follow its core API as much as possible until the day -- @base@ provides its own type-level rationals, making this module redundant. module KindRational {--} ( -- * Rational kind Rational , type (%) , type (/) , Normalize , Num , Den -- Prelude support , rational , fromPrelude , toPrelude , showsPrecTypeLit -- * Types ⇔ Terms , KnownRational(rationalSing) , rationalVal , SomeRational(..) , someRationalVal , sameRational -- * Singletons , SRational , pattern SRational , fromSRational , fromSRational' , withSomeSRational , withKnownRational -- * Arithmethic , type (+) , type (*) , type (-) , Negate , Sign , Abs , Recip , Div , div , Rem , rem , DivRem , divRem , I.Round(..) -- * Decimals , Terminating , withTerminating , Terminates , terminates -- * Comparisons , CmpRational , cmpRational , eqRationalRep -- * Extra -- -- | This stuff should be exported by the "Data.Type.Ord" module. , type (==?), type (==), type (/=?), type (/=) ) --} where import Control.Exception qualified as Ex import Control.Monad import Data.Proxy import Data.Singletons import Data.Singletons.Decide import Data.Type.Bool (If) import Data.Type.Coercion import Data.Type.Equality (TestEquality(..)) import Data.Type.Ord import GHC.Base (WithDict(..)) import GHC.Exts (TYPE, Constraint) import GHC.Read qualified as Read import GHC.Real qualified as P (Ratio(..), (%)) import GHC.Show (appPrec, appPrec1) import GHC.TypeLits qualified as L import GHC.TypeNats qualified as N import KindInteger (Integer, N, P) import KindInteger (type (==?), type (==), type (/=?), type (/=)) import KindInteger qualified as I import Numeric.Natural (Natural) import Prelude hiding (Rational, Integer, Num, div, rem) import Prelude qualified as P import Text.ParserCombinators.ReadPrec as Read import Text.Read.Lex qualified as Read import Unsafe.Coerce(unsafeCoerce) -------------------------------------------------------------------------------- -- | Type-level version of 'P.Rational'. Use '/' to construct one, use '%' to -- pattern-match on it. -- -- 'Rational' is mostly used as a kind, with its types constructed -- using '/'. However, it might also be used as type, with its terms -- constructed using 'rational' or 'fromPrelude'. One reason why you may want a -- 'Rational' at the term-level is so that you embed it in larger data-types -- (for example, this 'Rational' embeds the 'I.Integer' similarly offered by -- the "KindInteger" module). But perhaps more importantly, this 'Rational' -- offers better safety than the 'P.Rational' from "Prelude", since it's not -- possible to construct one with a zero denominator, or so large that -- operating with it would exhaust system resources. Notwithstanding this, for -- ergonomic reasons, all of the functions exported by this module take -- "Prelude" 'Rational's as input and produce "Prelude" 'Rational's as outputs. -- Internally, however, the beforementioned checks are always performed, and -- fail with 'Ex.throw' if necessary. If you want to be sure those 'error's -- never happen, just filter your "Prelude" 'Rational's with 'fromPrelude'. In -- practice, it's very unlikely that you will be affected by this unless if -- you are unsafelly constructing "Prelude" 'Rational's. data Rational = -- | This constructor is /unsafe/ because it doesn't check for the things -- that 'rational' checks for. -- -- * At the term-level, safely construct a 'Rational' using 'rational' -- or 'fromPrelude' instead. -- -- * At the type-level, safely construct a 'Rational' using '/'. -- -- * We keep the numerator and denominator unnormalized because we use them -- to implement 'SDecide', 'TestEquality' and 'TestCoercion'. Even if “1/2” -- and “2/4” mean the same, in 'SDecide' and friends we treat them as the -- different types that they are. I.Integer :% Natural -- | Arithmethic equality. That is, \(\frac{1}{2} == \frac{2}{4}\). instance Eq Rational where a == b = toPrelude a == toPrelude b instance Ord Rational where compare a b = compare (toPrelude a) (toPrelude b) a <= b = toPrelude a <= toPrelude b -- | Same as "Prelude" 'P.Rational'. instance Show Rational where showsPrec p = showsPrec p . toPrelude -- | Same as "Prelude" 'P.Rational'. instance Read Rational where readPrec = Read.parens $ Read.prec 7 $ do -- 7 is GHC.Real.ratioPrec n :: P.Integer <- Read.step Read.readPrec Read.expectP (Read.Symbol "%") d :: P.Integer <- Read.step Read.readPrec Just r <- pure (rational n d) pure r -- | Shows the 'Rational' as it appears literally at the type-level. -- -- This is remerent from normal 'show' for 'Rational', which shows -- the term-level value. -- -- @ -- 'shows' 0 ('rationalVal' ('Proxy' \@(1'/'2))) \"z\" == \"1 % 2z\" -- 'showsPrecTypeLit' 0 ('rationalVal' ('Proxy' \@(1'/'2))) \"z\" == \"P 1 % 2z\" -- @ showsPrecTypeLit :: Int -> Rational -> ShowS showsPrecTypeLit p (n :% d) = showParen (p > appPrec) $ I.showsPrecTypeLit appPrec n . showString " % " . shows d -- | Make a term-level "KindRational" 'Rational' number, provided that -- the numerator is not @0@, and that its numerator and denominator are -- not so large that they would exhaust system resources. The 'Rational' -- is 'Normalize'd. rational :: (Integral num, Integral den) => num -> den -> Maybe Rational rational = \(toInteger -> n) (toInteger -> d) -> do guard (d /= 0 && abs n <= max_ && abs d <= max_) pure $ let n1 P.:% d1 = n P.% d -- 'P.%' normalizes in I.fromPrelude n1 :% fromInteger d1 where max_ :: P.Integer -- Some big enough number. TODO: Pick good number. max_ = 10 ^ (1000 :: Int) -- | Try to obtain a term-level "KindRational" 'Rational' from a term-level -- "Prelude" 'P.Rational'. This can fail if the "Prelude" 'P.Rational' is -- infinite, or if it is so big that it would exhaust system resources. -- -- @ -- 'fromPrelude' . 'toPrelude' == 'Just' -- 'fmap' 'toPrelude' . 'fromPrelude' == 'Just' -- @ fromPrelude :: P.Rational -> Maybe Rational fromPrelude (n P.:% d) = rational n d -- | Like 'fromPrelude', but 'Ex.throw's in situations where -- 'fromPrelude' fails with 'Nothing'. unsafeFromPrelude :: P.Rational -> Rational unsafeFromPrelude = \case n P.:% d | d == 0 -> Ex.throw Ex.RatioZeroDenominator | abs n > max_ || abs d > max_ -> Ex.throw Ex.Overflow | otherwise -> let n1 P.:% d1 = n P.% d -- 'P.%' normalizes in I.fromPrelude n1 :% fromInteger d1 where max_ :: P.Integer -- Some big enough number. TODO: Pick good number. max_ = 10 ^ (1000 :: Int) -- | Like 'unsafeFromPrelude', but returns a "Prelude" 'P.Rational'. unsafeCheckPrelude :: P.Rational -> P.Rational unsafeCheckPrelude = toPrelude . unsafeFromPrelude -- | Convert a term-level "KindRational" 'Rational' into a 'Normalized' -- term-level "Prelude" 'P.Rational'. -- -- @'fromPrelude' . 'toPrelude' == 'Just'@ toPrelude :: Rational -> P.Rational toPrelude (n :% d) = I.toPrelude n P.% toInteger d -- 'P.%' normalizes. -------------------------------------------------------------------------------- -- | 'Normalize'd /'Num'erator/ of the type-level 'Rational'. type Num (r :: Rational) = Num_ (Normalize r) :: Integer type family Num_ (r :: Rational) :: Integer where Num_ (n :% _) = n -- | 'Normalize'd /'Den'ominator/ of the type-level 'Rational'. type Den (r :: Rational) = Den_ (Normalize r) :: Natural type family Den_ (r :: Rational) :: Natural where Den_ (_ :% d) = d -- | Pattern-match on a type-level 'Rational'. -- -- __NB:__ When /constructing/ a 'Rational' number, prefer to use '/', -- which not only accepts more polymorphic inputs, but also 'Normalize's -- the type-level 'Rational'. Also note that while @n '%' 0@ is a valid -- type, all tools in the "KindRational" will reject such input. type (n :: I.Integer) % (d :: Natural) = n :% d :: Rational -- | Normalize a type-level 'Rational' so that a /0/ denominator fails to -- type-check, and that the 'Num'erator and denominator have no common factors. -- -- Only 'Normalize'd 'Rational's can be reliably constrained for equality -- using '~'. -- -- All of the functions in the "KindRational" module accept both -- 'Normalize'd and non-'Normalize'd inputs, but they always produce -- 'Normalize'd output. type family Normalize (r :: Rational) :: Rational where Normalize (_ % 0) = L.TypeError ('L.Text "KindRational: Denominator is zero") Normalize (P 0 % _) = P 0 % 1 Normalize (N 0 % _) = P 0 % 1 Normalize (P n % d) = P (L.Div n (GCD n d)) % L.Div d (GCD n d) Normalize (N n % d) = N (L.Div n (GCD n d)) % L.Div d (GCD n d) -------------------------------------------------------------------------------- infixl 6 +, - infixl 7 *, / type (/) :: kn -> kd -> Rational -- | @n'/'d@ constructs and 'Normalize's a type-level 'Rational' -- with numerator @n@ and denominator @d@. -- -- This type-family accepts any combination of 'Natural', 'Integer' and -- 'Rational' as input. -- -- @ -- ('/') :: 'Natural' -> 'Natural' -> 'Rational' -- ('/') :: 'Natural' -> 'Integer' -> 'Rational' -- ('/') :: 'Natural' -> 'Rational' -> 'Rational' -- -- ('/') :: 'Integer' -> 'Natural' -> 'Rational' -- ('/') :: 'Integer' -> 'Integer' -> 'Rational' -- ('/') :: 'Integer' -> 'Rational' -> 'Rational' -- -- ('/') :: 'Rational' -> 'Natural' -> 'Rational' -- ('/') :: 'Rational' -> 'Integer' -> 'Rational' -- ('/') :: 'Rational' -> 'Rational' -> 'Rational' -- @ -- -- It's not possible to pattern-match on @n'/'d@. Instead, you must -- pattern match on @x'%'y@, where @x'%'y ~ n'/'d@. type family n / d :: Rational where -- Natural/Natural (n :: Natural) / (d :: Natural) = Normalize (P n % d) -- Natural/Integer (n :: Natural) / (P d :: Integer) = Normalize (P n % d) (n :: Natural) / (N d :: Integer) = Normalize (N n % d) -- Natural/Rational (n :: Natural) / (d :: Rational) = (P n % 1) * Recip d -- Integer/Natural (i :: Integer) / (d :: Natural) = Normalize (i % d) -- Integer/Integer (P n :: Integer) / (P d :: Integer) = Normalize (P n % d) (N n :: Integer) / (N d :: Integer) = Normalize (P n % d) (P n :: Integer) / (N d :: Integer) = Normalize (N n % d) (N n :: Integer) / (P d :: Integer) = Normalize (N n % d) -- Integer/Rational (n :: Integer) / (d :: Rational) = (n % 1) * Recip d -- Rational/Natural (n :: Rational) / (d :: Natural) = n * Recip (P d % 1) -- Rational/Integer (n :: Rational) / (d :: Integer) = n * Recip (d % 1) -- Rational/Rational (n :: Rational) / (d :: Rational) = n * Recip d -------------------------------------------------------------------------------- -- | /'Negate'/ a type-level 'Rational'. Also known as /additive inverse/. type family Negate (r :: Rational) :: Rational where Negate (P n % d) = Normalize (N n % d) Negate (N n % d) = Normalize (P n % d) -- | Sign of type-level 'Rational's, as a type-level 'Integer'. -- -- * @'P' 0@ if zero. -- -- * @'P' 1@ if positive. -- -- * @'N' 1@ if negative. type Sign (r :: Rational) = I.Sign (Num r) :: Integer -- | /'Abs'olute/ value of a type-level 'Rational'. type Abs (r :: Rational) = Normalize (P (I.Abs (Num_ r)) % Den_ r) :: Rational -------------------------------------------------------------------------------- -- | @a t'*' b@ multiplies type-level 'Rational's @a@ and @b@. type (a :: Rational) * (b :: Rational) = Mul_ (Normalize a) (Normalize b) :: Rational type family Mul_ (a :: Rational) (b :: Rational) where Mul_ (n1 % d1) (n2 % d2) = Normalize ((n1 I.* n2) % (d1 L.* d2)) -- | /'Recip'rocal/ of the type-level 'Rational'. -- Also known as /multiplicative inverse/. type Recip (a :: Rational) = Recip_ (Normalize a) :: Rational type family Recip_ (a :: Rational) :: Rational where Recip_ (P n % d) = Normalize (P d % n) Recip_ (N n % d) = Normalize (N d % n) -- | @a t'+' b@ adds type-level 'Rational's @a@ and @b@. type (a :: Rational) + (b :: Rational) = Add_ (Normalize a) (Normalize b) :: Rational type family Add_ (a :: Rational) (r :: Rational) :: Rational where Add_ (an % ad) (bn % bd) = Normalize ((an I.* P bd I.+ bn I.* P ad) % (ad L.* bd)) -- | @a t'-' b@ subtracts the type-level 'Rational' @b@ from -- the type-level 'Rational' @a@. type (a :: Rational) - (b :: Rational) = a + Negate b :: Rational -------------------------------------------------------------------------------- -- | Quotient of the 'Div'ision of the 'Num'erator of type-level 'Rational' @a@ -- by its 'Den'ominator, using the specified 'I.Round'ing @r@. -- -- @ -- forall (r :: 'I.Round') (a :: 'Rational'). -- ('Den' a '/=' 0) => -- 'Rem' r a '==' a '-' 'Div' r a '%' 1 -- @ -- -- Use this to approximate a type-level 'Rational' to an 'Integer'. type Div (r :: I.Round) (a :: Rational) = Div_ r (Normalize a) :: Integer type Div_ (r :: I.Round) (a :: Rational) = I.Div r (Num_ a) (P (Den_ a)) :: Integer -- | 'Rem'ainder from 'Div'iding the 'Num'erator of the type-level 'Rational' -- @a@ by its 'Den'ominator, using the specified 'I.Round'ing @r@. -- -- @ -- forall (r :: 'I.Round') (a :: 'Rational'). -- ('Den' a '/=' 0) => -- 'Rem' r a '==' a '-' 'Div' r a '%' 1 -- @ type Rem (r :: I.Round) (a :: Rational) = Snd (DivRem r a) :: Rational -- | Get both the quotient and the 'Rem'ainder of the 'Div'ision of the -- 'Num'erator of type-level 'Rational' @a@ by its 'Den'ominator, -- using the specified 'I.Round'ing @r@. -- -- @ -- forall (r :: 'I.Round') (a :: 'Rational'). -- ('Den' a '/=' 0) => -- 'DivRem' r a '==' '('Div' r a, 'Rem' r a) -- @ type DivRem (r :: I.Round) (a :: Rational) = DivRem_ r (Normalize a) :: (Integer, Rational) type DivRem_ (r :: I.Round) (a :: Rational) = DivRem__ (Den_ a) (I.DivRem r (Num_ a) (P (Den_ a))) :: (Integer, Rational) type DivRem__ (d :: Natural) (qm :: (Integer, Integer)) = '(Fst qm, Normalize (Snd qm % d)) :: (Integer, Rational) -- | Term-level version of 'Div'. -- -- Takes a "Prelude" 'P.Rational' as input, returns a "Prelude" 'P.Integer'. div :: I.Round -> P.Rational -> P.Integer div r = let f = I.div r in \a -> let (n P.:% d) = unsafeCheckPrelude a in f n d -- | Term-level version of 'Rem'. -- -- Takes a "Prelude" 'P.Rational' as input, returns a "Prelude" 'P.Rational'. rem :: I.Round -> P.Rational -> P.Rational rem r = snd . divRem r -- | Term-level version of 'DivRem'. -- -- Takes a "Prelude" 'P.Rational' as input, returns a pair of "Prelude" -- 'P.Rational's /(quotient, remerence)/. -- -- @ -- forall ('r' :: 'I.Round') (a :: 'P.Rational'). -- ('P.denominator' a 'P./=' 0) => -- 'divRem' r a 'P.==' ('div' r a, 'rem' r a) -- @ divRem :: I.Round -> P.Rational -> (P.Integer, P.Rational) divRem r = let f = I.divRem r in \a -> let (n P.:% d) = unsafeCheckPrelude a (q, m) = f n d in (q, m P.% d) -- (m % d) == (a - q) -------------------------------------------------------------------------------- -- | 'Constraint' version of @'Terminates' r@. Satisfied by all type-level -- 'Rational's that can be represented as a finite decimal number. -- Written as a class rather than as a type-synonym so that downstream doesn't -- need to use UndecidableSuperClasses. class (KnownRational r, Terminates r ~ True) => Terminating (r :: Rational) -- Note: Even if @Terminates r ~ 'False@, GHC shows our @TypeError@ first. instance ( KnownRational r , Terminates r ~ 'True , If (Terminates r) (() :: Constraint) (L.TypeError ('L.Text "‘" 'L.:<>: 'L.ShowType r 'L.:<>: 'L.Text "’ is not a terminating " 'L.:<>: 'L.ShowType Rational)) ) => Terminating r withTerminating :: forall r a . KnownRational r => (Terminating r => a) -> Maybe a withTerminating g = do guard (terminates' (rationalVal' (Proxy @r))) case unsafeCoerce (Dict @(Terminating (P 1 % 1))) of (Dict :: Dict (Terminating r)) -> pure g -- | Whether the type-level 'Rational' terminates. That is, whether -- it can be fully represented as a finite decimal number. type Terminates (r :: Rational) = Terminates_ (Den r) :: Bool type family Terminates_ (n :: Natural) :: Bool where Terminates_ 5 = 'True Terminates_ 2 = 'True Terminates_ 1 = 'True Terminates_ d = Terminates_5 d (L.Mod d 5) -- @Terminates_5@ is here to prevent @Terminates_@ from recursing into -- @Terminates_ (Div d 5)@ if it would diverge. type family Terminates_5 (d :: Natural) (md5 :: Natural) :: Bool where Terminates_5 d 0 = Terminates_ (L.Div d 5) Terminates_5 d _ = Terminates_2 d (L.Mod d 2) -- @Terminates_2@ is here to prevent @Terminates_5@ from recursing into -- @Terminates_ (Div d 2)@ if it would diverge, and also to prevent calculating -- @Mod d 2@ unless necessary. type family Terminates_2 (d :: Natural) (md2 :: Natural) :: Bool where Terminates_2 d 0 = Terminates_ (L.Div d 2) Terminates_2 _ _ = 'False -- | Term-level version of the "Terminates" function. -- Takes a "Prelude" 'P.Rational' as input. terminates :: P.Rational -> Bool terminates = terminates' . unsafeFromPrelude -- | Term-level version of the "Terminates" function. -- Takes a "KindRational" 'P.Rational' as input. terminates' :: Rational -> Bool terminates' = \(_ :% d) -> go d where go = \case 5 -> True 2 -> True 1 -> True n | (q, 0) <- P.divMod n 5 -> go q | (q, 0) <- P.divMod n 2 -> go q _ -> False -------------------------------------------------------------------------------- -- | Comparison of type-level 'Rational's, as a function. type CmpRational (a :: Rational) (b :: Rational) = CmpRational_ (Normalize a) (Normalize b) :: Ordering type family CmpRational_ (a :: Rational) (b :: Rational) :: Ordering where CmpRational_ a a = 'EQ CmpRational_ (an % ad) (bn % bd) = I.CmpInteger (an I.* P bd) (bn I.* P ad) -- | "Data.Type.Ord" support for type-level 'Rational's. type instance Compare (a :: Rational) (b :: Rational) = CmpRational a b -------------------------------------------------------------------------------- -- | This class gives the rational associated with a type-level rational. -- There are instances of the class for every rational. class KnownRational (r :: Rational) where rationalSing :: SRational r -- | This instance checks that @r@ 'Normalize's, but the obtained 'SRational' is -- not normalized. This is so that 'SDecide', 'TestEquality' and 'TestCoercion' -- behave as expected. If you want a 'Normalize'd 'SRational', then use -- @'rationalSing' \@('Normalize' r)@. instance forall r n d. ( Normalize r ~ n % d , I.KnownInteger (Num_ r) , L.KnownNat (Den_ r) ) => KnownRational r where rationalSing = let n = I.fromSInteger' (I.SInteger @(Num_ r)) d = N.natVal (Proxy @(Den_ r)) in UnsafeSRational (n :% d) -- | Term-level "KindRational" 'Rational' representation of the type-level -- 'Rational' @r@. rationalVal' :: forall r proxy. KnownRational r => proxy r -> Rational rationalVal' _ = case rationalSing :: SRational r of UnsafeSRational x -> x -- | Term-level "Prelude" 'P.Rational' representation of the type-level -- 'Rational' @r@. rationalVal :: forall r proxy. KnownRational r => proxy r -> P.Rational rationalVal = toPrelude . rationalVal' -- | This type represents unknown type-level 'Rational'. data SomeRational = forall n. KnownRational n => SomeRational (Proxy n) -- | Convert a term-level "Prelude" 'Rational' into an unknown -- type-level 'Rational'. someRationalVal :: P.Rational -> SomeRational someRationalVal r = withSomeSRational (unsafeFromPrelude r) $ \(sr :: SRational r) -> withKnownRational sr (SomeRational @r Proxy) instance Eq SomeRational where SomeRational x == SomeRational y = rationalVal x P.== rationalVal y instance Ord SomeRational where SomeRational x <= SomeRational y = rationalVal x <= rationalVal y compare (SomeRational x) (SomeRational y) = compare (rationalVal x) (rationalVal y) instance Show SomeRational where showsPrec p (SomeRational x) = showsPrec p (rationalVal x) instance Read SomeRational where readsPrec p xs = do (a, ys) <- readsPrec p xs [(someRationalVal a, ys)] -- | We either get evidence that this function was instantiated with the -- same type-level 'Rational's, or 'Nothing'. sameRational :: forall a b proxy1 proxy2 . (KnownRational a, KnownRational b) => proxy1 a -> proxy2 b -> Maybe (a :~: b) sameRational _ _ = testEquality (rationalSing @a) (rationalSing @b) -- | Like 'sameRational', but if the type-level 'Rational's aren't equal, this -- additionally provides proof of 'LT' or 'GT'. cmpRational :: forall a b proxy1 proxy2 . (KnownRational a, KnownRational b) => proxy1 a -> proxy2 b -> OrderingI a b cmpRational x y = case compare (rationalVal x) (rationalVal y) of EQ -> case unsafeCoerce Refl :: CmpRational a b :~: 'EQ of Refl -> case unsafeCoerce Refl :: a :~: b of Refl -> EQI LT -> case unsafeCoerce Refl :: (CmpRational a b :~: 'LT) of Refl -> LTI GT -> case unsafeCoerce Refl :: (CmpRational a b :~: 'GT) of Refl -> GTI -------------------------------------------------------------------------------- -- | Singleton type for a type-level 'Rational' @r@. newtype SRational (r :: Rational) = UnsafeSRational Rational type role SRational nominal -- | A explicitly bidirectional pattern synonym relating an 'SRational' to a -- 'KnownRational' constraint. -- -- As an __expression__: Constructs an explicit @'SRational' r@ value from an -- implicit @'KnownRational' r@ constraint: -- -- @ -- 'SRational' @r :: 'KnownRational' r => 'SRational' r -- @ -- -- As a __pattern__: Matches on an explicit @'SRational' r@ value bringing -- an implicit @'KnownRational' r@ constraint into scope: -- -- @ -- f :: 'SRational' r -> .. -- f SRational = {- SRational r in scope -} -- @ pattern SRational :: forall r. () => KnownRational r => SRational r pattern SRational <- (knownRationalInstance -> KnownRationalegerInstance) where SRational = rationalSing -- | An internal data type that is only used for defining the 'SRational' pattern -- synonym. data KnownRationalegerInstance (r :: Rational) where KnownRationalegerInstance :: KnownRational r => KnownRationalegerInstance r -- | An internal function that is only used for defining the 'SRational' pattern -- synonym. knownRationalInstance :: SRational r -> KnownRationalegerInstance r knownRationalInstance si = withKnownRational si KnownRationalegerInstance instance Show (SRational r) where showsPrec p (UnsafeSRational r) = showParen (p > appPrec) $ showString "SRational @" . showsPrecTypeLit appPrec1 r -- | Note that this checks for type equality, not arithmetic equality. -- That is, @'P' 1 '%' 2@ and @'P' 2 '%' 4@ are not equal types, -- even if they are arithmetically equal. instance TestEquality SRational where testEquality = decideEquality {-# INLINE testEquality #-} -- | Note that this checks for type equality, not arithmetic equality. -- That is, @'P' 1 '%' 2@ and @'P' 2 '%' 4@ are not equal types, -- even if they are arithmetically equal. instance TestCoercion SRational where testCoercion = decideCoercion {-# INLINE testCoercion #-} -- | Return the term-level "Prelude" 'P.Rational' number corresponding -- to @r@ in a @'SRational' r@ value. This 'P.Rational' is 'Normalize'd. fromSRational :: SRational r -> P.Rational fromSRational (UnsafeSRational r) = toPrelude r -- | Return the term-level "KindRational" 'Rational' number corresponding -- to @r@ in a @'SRational' r@ value. This 'Rational' is not 'Normalize'd. fromSRational' :: SRational r -> Rational fromSRational' (UnsafeSRational r) = r -- | Whether the internal representation of the 'Rational's are equal. -- -- Note that this is not the same as '(==)'. Use '(==)' unless you -- know what you are doing. eqRationalRep :: Rational -> Rational -> Bool eqRationalRep (ln :% ld) (rn :% rd) = I.eqIntegerRep ln rn && ld P.== rd {-# INLINE eqRationalRep #-} -- | Convert an explicit @'SRational' r@ value into an implicit -- @'KnownRational' r@ constraint. withKnownRational :: forall r rep (a :: TYPE rep). SRational r -> (KnownRational r => a) -> a withKnownRational = withDict @(KnownRational r) -- | Convert a "Prelude" 'P.Rational' number into an @'SRational' n@ value, -- where @n@ is a fresh type-level 'Rational'. withSomeSRational :: forall rep (a :: TYPE rep). Rational -> (forall r. SRational r -> a) -> a withSomeSRational r k = k (UnsafeSRational r) -- It's very important to keep this NOINLINE! See the docs at "GHC.TypeNats" {-# NOINLINE withSomeSRational #-} -------------------------------------------------------------------------------- type instance Sing = SRational -- | Note that this checks for type equality, not arithmetic equality. -- That is, @'P' 1 '%' 2@ and @'P' 2 '%' 4@ are not equal types, -- even if they are arithmetically equal. instance SDecide Rational where UnsafeSRational l %~ UnsafeSRational r = case eqRationalRep l r of True -> Proved (unsafeCoerce Refl) False -> Disproved (\Refl -> error "SDecide.Rational") -------------------------------------------------------------------------------- -- Extra stuff that doesn't belong here. -- | /Greatest Common Divisor/ of 'Natural' numbers @a@ and @b@. type GCD (a :: Natural) (b :: Natural) = I.GCD (P a) (P b) :: Natural data Dict c where Dict :: c => Dict c type family Fst (ab :: (a, b)) :: a where Fst '(a, b) = a type family Snd (ab :: (a, b)) :: b where Snd '(a, b) = b