I am trying to have the value of an integer value represented at the type level at compile time such that I can check it against some constraints.
I would like users of the DSL to not have to write something like:
five = inc $ inc $ inc $ inc $ inc $ zero
Hence what I've been trying to create is a function which elevates that value to the type level.
ie
fromInt :: Int -> repr n
fromInt n = __
This is what I have tried so far:
data Z
data S a
data IntNat a = IN Int
zero = (IN 0 :: IntNat Z)
inc :: IntNat a -> IntNat (S a)
inc (IN i) = (IN (i + 1))
fromInt :: Int -> IntNat a
fromInt 0 = (IN 0 :: IntNat Z)
fromInt n = inc (fromInt (n - 1))
This however fails since I don't have a general enough representation of the IntNat, IntNat Z ~/~ IntNat S Z.
Is this approach generally flawed, or is it a case of needing to enclose S Z in a type family / type class?
The other classic example of this would be to generate a vector of a specific length when using type level length annotated vectors. That function should address the same problem that I am having.