Reputation: 1746
I'm currently learning about using Nat from GHC.TypeLits
I am trying to lift an integer to the type level for testing via these definitions:
type IntNat (t::Nat) = Int
incNat :: IntNat t -> IntNat (t + 1)
incNat n = n + 1
fromInt :: Int -> IntNat t
fromInt(0) = (0 :: IntNat 0)
fromInt(n) = incNat (fromInt(n-1))
four = fromInt 4
From my understanding this should result in:
four = 4 :: IntNat (Nat 4)
However I am getting:
four = 4 :: IntNat t
Where am I going wrong, is it just that fromInt
is erasing the type information?
Upvotes: 1
Views: 242
Reputation: 64740
Because you defined a type synonym, and not a newtype, the meaning is quite different than what you seem to think. Roughly, IntNat _
will unify with Int
and thus any other IntNat _
.
You can try this out by defining other intnats and see them unify in, say, a list:
*Main> let five = 5 :: IntNat 99
*Main> let xs = [four, five] :: [IntNat 42]
*Main> :t xs
xs :: [IntNat 42]
or using a single intnat:
*Main> let four' = four :: IntNat 1000
*Main> :t four'
four' :: IntNat 1000
Instead consider using a newtype, such as newtype IntNat (n :: Nat) = IN Int
. You'd get an error like:
• Couldn't match type ‘t’ with ‘0’
‘t’ is a rigid type variable bound by
the type signature for:
fromInt :: forall (t :: Nat). Int -> IntNat t
at so.hs:12:1-26
Expected type: IntNat t
Actual type: IntNat 0
• In the expression: IN 0 :: IntNat 0
In an equation for ‘fromInt’: fromInt 0 = IN 0 :: IntNat 0
• Relevant bindings include
fromInt :: Int -> IntNat t (bound at so.hs:13:1)
This is because t
and 0
are not the same - t
is universal not existential. The offending lines are:
fromInt :: Int -> IntNat t
fromInt 0 = 0 :: IntNat 0
forall t. t == 0
is a hard proof to write
Upvotes: 2