{-# LANGUAGE AllowAmbiguousTypes #-}
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE GADTs #-}
{-# LANGUAGE KindSignatures #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TypeApplications #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE TypeOperators #-}
{-# LANGUAGE UndecidableInstances #-}
module Overloaded.Numerals (
FromNumeral (..),
defaultFromNumeral,
) where
import Data.Fin (Fin (..))
import Data.Proxy (Proxy (..))
import Data.Word (Word16, Word32, Word64, Word8)
import GHC.Exts (Constraint)
import GHC.TypeLits (ErrorMessage (..), Symbol, TypeError)
import GHC.TypeNats (type (<=?), KnownNat, Nat, natVal)
import Numeric.Natural (Natural)
import qualified Data.Type.Nat as N
class FromNumeral (n :: Nat) a where
fromNumeral :: a
defaultFromNumeral :: forall n a. (KnownNat n, Integral a) => a
defaultFromNumeral = fromIntegral $ natVal (Proxy :: Proxy n)
instance KnownNat n => FromNumeral n Natural where
fromNumeral = natVal (Proxy :: Proxy n)
instance KnownNat n => FromNumeral n Integer where
fromNumeral = defaultFromNumeral @n
instance KnownNat n => FromNumeral n Int where
fromNumeral = defaultFromNumeral @n
type family OverflowCheck (n :: Nat) (t :: Symbol) (b :: Bool) :: Constraint where
OverflowCheck n t 'True = ()
OverflowCheck n t 'False = TypeError
('Text "Overflowing " ':<>: 'Text t ':<>: 'Text ": " ':<>: 'ShowType n)
instance (KnownNat n, OverflowCheck n "Word8" (n <=? 0xff)) => FromNumeral n Word8 where
fromNumeral = defaultFromNumeral @n
instance (KnownNat n, OverflowCheck n "Word8" (n <=? 0xffff)) => FromNumeral n Word16 where
fromNumeral = defaultFromNumeral @n
instance (KnownNat n, OverflowCheck n "Word8" (n <=? 0xffffffff)) => FromNumeral n Word32 where
fromNumeral = defaultFromNumeral @n
instance (KnownNat n, OverflowCheck n "Word8" (n <=? 0xffffffffffffffff)) => FromNumeral n Word64 where
fromNumeral = defaultFromNumeral @n
instance KnownNat n => FromNumeral n N.Nat where
fromNumeral = defaultFromNumeral @n
type family IsLess (n :: N.Nat) (m :: N.Nat) (p :: Nat) (q :: Nat) :: Constraint where
IsLess 'N.Z ('N.S m) p q = ()
IsLess ('N.S n) ('N.S m) p q = IsLess n m p q
IsLess ('N.S n) 'N.Z p q = TypeError ('ShowType p ':<>: 'Text " is not less than " ':<>: 'ShowType q)
class FinFromNumeral (n :: N.Nat) (m :: N.Nat) where
finFromNumeral :: Proxy n -> Fin m
instance FinFromNumeral 'N.Z ('N.S m) where
finFromNumeral _ = FZ
instance FinFromNumeral n m => FinFromNumeral ('N.S n) ('N.S m) where
finFromNumeral _ = FS (finFromNumeral (Proxy :: Proxy n))
instance (FinFromNumeral (N.FromGHC n) m, IsLess (N.FromGHC n) m n (N.ToGHC m)) => FromNumeral n (Fin m) where
fromNumeral = finFromNumeral (Proxy :: Proxy (N.FromGHC n))