Reid Barton:
On Sat, Oct 10, 2009 at 02:59:37PM -0400, Brad Larsen wrote:
Suppose we implement type-level naturals as so:
data Zero
data Succ a
Then, we can reflect the type-level naturals into a GADT as so (not
sure if ``reflect'' is the right terminology here):
data Nat :: * -> * where
Zero :: Nat Zero
Succ :: Nat a -> Nat (Succ a)
Using type families, we can then proceed to define type-level
addition:
type family Add a b :: *
type instance Add Zero b = b
type instance Add (Succ a) b = Succ (Add a b)
Is there any way to define type-level multiplication without
requiring
undecidable instances?
I hesitate to contradict Manuel Chakravarty on this subject... but I
posted a type-level multiplication program without undecidable
instances on this list in June:
http://www.haskell.org/pipermail/haskell-cafe/2009-June/062452.html
If you prefer to use EmptyDataDecls, you can replace the first four
lines by
data Z
data S n
data Id :: * -> *
data (:.) :: (* -> *) -> (* -> *) -> (* -> *)
And I still don't understand why that definition works while
the obvious one doesn't :)
Ok, I should have been more precise. It's not that anything about
multiplication as such is a problem. However, when you look at the
standard definitions for addition
type family Add a b :: *
type instance Add Zero b = b
type instance Add (Succ a) b = Succ (Add a b)
and multiplication
type family Mul a b :: *
type instance Mul Zero b = Zero
type instance Mul (Succ a) b = Add (Mul a b) b
then the difference is that multiplication uses nested applications of
type families (namely, Add (Mul a b) b). It is this nested
application of type families that can be abused to construct programs
that lead to non-termination of type checking (by exploiting the
openness of type families in combination with local equalities
introduced either by equality constraints in type signatures or GADT
pattern matches). Unfortunately, there doesn't seem to be a simple
syntactic criterion that would let GHC decide between harmless and
problematic uses of nested *open* family applications in family
instances. Hence, we need to rule them all out.
You circumvent that problem cleverly by the use of higher-kinded types.
Manuel
_______________________________________________
Haskell-Cafe mailing list
[email protected]
http://www.haskell.org/mailman/listinfo/haskell-cafe