-- This module should compile with -XIncoherentInstances, but didn't in 7.4


{- Here we define all the stuff that is needed for our singleton
   types:
 - phantom types (when GHC 7.4 arrives, the user-defined kinds)
 - corresponding singleton types

These are basically the constructs from Omega,
reimplemented in Haskell for our purposes. -}

{-# LANGUAGE GADTs, KindSignatures, StandaloneDeriving,
             RankNTypes, TypeFamilies, FlexibleInstances, IncoherentInstances #-}
module TypeMachinery where

-- The natural numbers:
--  o first the phantom types

data Z; data S n

-- o the using the above the singleton type Nat'

data Nat' :: * -> * where
  Z :: Nat' Z
  S :: Nat' n -> Nat' (S n)

deriving instance Show (Nat' a)

-- Type-level addition

type family Plus m n :: *
type instance Plus Z n = n
type instance Plus (S m) n = S (Plus m n)

-- Nat' addition

plus :: Nat' a -> Nat' b -> Nat' (Plus a b)
plus Z n = n
plus (S m) n = S (plus m n)

-- Equality on Nat'

sameNat' :: Nat' a -> Nat' b -> Bool
sameNat' Z Z = True
sameNat' (S m) (S n) = sameNat' m n
sameNat' _ _ = False

-- A data type for existentially hiding
-- (e.g.) Nat' values

data Hidden :: (* -> *) -> * where
  Hide :: Show (a n) => a n -> Hidden a

deriving instance Show (Hidden t)

toNat' :: Integral i => i -> Hidden Nat'
toNat' 0 = Hide Z
toNat' n = case toNat' (n - 1) of
           Hide n -> Hide (S n)

-- Now we are ready to make Hidden Nat' an Integral type

instance Eq (Hidden Nat') where
  Hide a == Hide b = sameNat' a b

instance Ord (Hidden Nat') where
  Hide Z `compare` Hide Z = EQ
  Hide Z `compare` Hide _ = LT
  Hide _ `compare` Hide Z = GT
  Hide (S m) `compare` Hide (S n) = Hide m `compare` Hide n

instance Enum (Hidden Nat') where
  toEnum = toEnum . fromIntegral
  fromEnum = fromIntegral

instance Num (Hidden Nat') where
  fromInteger = toNat'
  signum (Hide Z) = 0
  signum _ = 1
  abs n = n
  Hide a + Hide b = Hide $ plus a b
  a * b = fromInteger $ toInteger a * toInteger b
  negate a = error "negate(Hidden Nat')"

instance Real (Hidden Nat') where
  toRational = toRational . toInteger

instance Integral (Hidden Nat') where
  toInteger (Hide Z) = 0
  toInteger (Hide (S n)) = 1 + toInteger (Hide n)
  quotRem a b = let (a', b') = toInteger a `quotRem` toInteger b in (fromInteger a', fromInteger b')

-- McBride's Fin data type. By counting backwards from the
-- result index, it only admits a fixed number of inhabitants.

data Fin :: * -> * where
    Stop :: Fin (S Z)
    Retreat :: Fin s -> Fin (S s)

deriving instance Show (Fin a)

