14

Let's say I have a type class with n type parameters and I want any of them to uniquely determine all the other ones. Is it enough to make the dependencies form a cycle like in

class Foo a b c | a -> b, b -> c, c -> a

(linear) where there is a path from every parameter to every other one, or do I need to expand all possible paths like in

class Bar a b c | a -> b, a -> c, b -> a, b -> c, c -> a, c -> b

(quadratic)? Is there any observable difference between the two? And how about

class Baz a b c | a -> b c, b -> a c, c -> a b
Petr
  • 62,528
  • 13
  • 153
  • 317

1 Answers1

1

Operationally, all of the above are equivalent:

First of all, a -> b c is quite literally the same thing as a -> b, a -> c.

Next, assume we got Foo a b c => (a, b, c). Say, we realize that a ~ A. We find the a -> b fundep and scan through instances to find b ~ B. Again we find the b -> c fundep and realize c ~ C. Voila we got (A, B, C).

If instead we had Bar a b c => (a, b, c) with a ~ A, we would've found a -> b, and b ~ B, however before finding b -> c, we would have found a -> c.

The only difference is which fundep arrows are used to infer the types. a -> b, b -> c and a -> b, a -> c can't produce different results.

mniip
  • 796
  • 7
  • 16