There is a package for this sort of thing: type-level. It's a bit intimidating, and I haven't really explored it yet. But you shouldn't need that much power, so you can roll your own simple bits.
If you're willing to use UndecidableInstances
, things are pretty easy. Something approximately like this (I don't know exactly how the naturals are defined in that library; if it doesn't use DataKinds
, you may have to use the *
kind instead of the Nat
kind, and you may have to write 'Succ
and 'Zero
instead of Succ
and Zero
--I'm not too clear on that aspect):
{-# LANGUAGE UndecidableInstances, TypeFamilies, DataKinds, TypeOperators #-}
module TAR where
-- You wouldn't actually use this line, because the library
-- provides the naturals
data Nat = Zero | Succ Nat
-- Digits and Ten to get things started
type One = Succ Zero
type Two = Succ One
type Three = Succ Two
type Four = Succ Three
type Five = Succ Four
type Six = Succ Five
type Seven = Succ Six
type Eight = Succ Seven
type Nine = Succ Eight
type Ten = Succ Nine
type family Plus (a::Nat) (b::Nat) where
Plus Zero n = n
Plus (Succ m) n = Plus m (Succ n)
type family Times (a::Nat) (b::Nat) where
Times Zero n = Zero
Times (Succ m) n = Plus n (Times m n)
type Decimal (a::[Nat]) = Decimal' a Zero
type family Decimal' (a::[Nat]) (n::Nat) where
Decimal' '[] n = n
Decimal' (a ': bs) n = Decimal' bs (Plus a (Times Ten n))
Then you can write something like
Decimal '[One, Two, Five]
to mean 125.