Alejandro Serrano Mena | 24 Oct 12:37 2013
Picon

Why do I need UndecidableInstances?

Dear café,
I'm trying to compile a set of simple examples using Functional Dependencies. One of the first ones is the case of natural numbers, which I've defined along with some operations as:

data Zero
data Succ a

class Plus x y z | x y -> z
instance Plus Zero x x
instance Plus x y z => Plus (Succ x) y (Succ z)

class Max x y z | x y -> z
instance Max Zero     y    y
instance Max (Succ x) Zero (Succ x)
instance Max x y z => Max (Succ x) (Succ y) (Succ z)

I thought the compiler would accept this only with the extensions EmptyDataDecls, MultiParamTypeClasses, FunctionalDependencies and FlexibleInstances. But, to my surprise, GHC is also asking me for UndecidableInstances. Why is this the case?

Thanks in advance :)
_______________________________________________
Haskell-Cafe mailing list
Haskell-Cafe <at> haskell.org
http://www.haskell.org/mailman/listinfo/haskell-cafe
Stijn van Drongelen | 24 Oct 15:08 2013
Picon

Re: Why do I need UndecidableInstances?

On Thu, Oct 24, 2013 at 12:37 PM, Alejandro Serrano Mena <trupill <at> gmail.com> wrote:
Dear café,
I'm trying to compile a set of simple examples using Functional Dependencies. One of the first ones is the case of natural numbers, which I've defined along with some operations as:

data Zero
data Succ a

class Plus x y z | x y -> z
instance Plus Zero x x
instance Plus x y z => Plus (Succ x) y (Succ z)

class Max x y z | x y -> z
instance Max Zero     y    y
instance Max (Succ x) Zero (Succ x)
instance Max x y z => Max (Succ x) (Succ y) (Succ z)

I thought the compiler would accept this only with the extensions EmptyDataDecls, MultiParamTypeClasses, FunctionalDependencies and FlexibleInstances. But, to my surprise, GHC is also asking me for UndecidableInstances. Why is this the case?

Thanks in advance :)

Hi Alejandro,

The error messages mention that the 'Coverage Condition' fails for the last instance of both classes. This condition is defined in the manual of GHC (http://www.haskell.org/ghc/docs/7.4.1/html/users_guide/type-class-extensions.html#instance-rules):

> For each functional dependency, tvs_left -> tvs_right, of the class, every type variable in S(tvs_right) must appear in S(tvs_left), where S is the substitution mapping each type variable in the class declaration to the corresponding type in the instance declaration.

Let's look at the Plus instance: every type variable in S(z) must appear in S(x) or S(y). This is false for both the context and the instance type: the variable 'z' in "Plus x y z" occurs in neither 'x' or 'y', and the variable 'z' in "Plus (Succ x) y (Succ z)" occurs in neither 'Succ x' or 'y'.

I'm not familiar enough (yet) with the reasons behind this rule to explain why it's there (if anyone can explain this in detail, please do!), but this should be a direct answer to your question: it's against the rules.

Kind regards,

Stijn van Drongelen
_______________________________________________
Haskell-Cafe mailing list
Haskell-Cafe <at> haskell.org
http://www.haskell.org/mailman/listinfo/haskell-cafe
adam vogt | 24 Oct 16:56 2013
Picon

Re: Why do I need UndecidableInstances?

On Thu, Oct 24, 2013 at 9:08 AM, Stijn van Drongelen <rhymoid <at> gmail.com> wrote:
> I'm not familiar enough (yet) with the reasons behind this rule to explain
> why it's there (if anyone can explain this in detail, please do!), but this
> should be a direct answer to your question: it's against the rules.

Hello,

Perhaps those rules are unnecessarily restrictive? Maybe the check
could be changed to something like "at least one of the types is
shrinking if all the other are not growing", rather than the current
rule that seems to be "all types must be smaller in the recursive
call". The equivalent code using type families doesn't need
undecidable instances in my ghc-7.6.2.

{-# LANGUAGE DataKinds #-}
{-# LANGUAGE TypeFamilies #-}
data Nat = Zero | Succ Nat

type family Plus (a :: Nat) (b :: Nat) :: Nat

type instance Plus Zero x = x
type instance Plus (Succ x) y = Succ (Plus x y)

type family Max (x :: Nat) (y :: Nat) :: Nat
type instance Max Zero y = y
type instance Max (Succ x) Zero = Succ x
type instance Max (Succ a) (Succ b) = Succ (Max a b)

Also using normal data types instead of the datakinds doesn't make a difference.

Regards,
Adam

Gmane