
On Sun, Nov 01, 2009 at 08:32:34AM -0800, Michael Mossey wrote:
Brent Yorgey wrote:
The m -> e thing isn't a constraint that needs to be satisfied; it gives some extra information to help the compiler with inferring which instance to use. In particular, "m -> e" says "the type chosen for m DETERMINES the type chosen for e"; put another way, "there cannot be two instances with the same type for m but different types for e". So in this case you could not also make an instance
MonadError String (Either e).
Thanks, Brent. Now what I'm a bit confused about: if you wrote
instance (Error e) => MonadError e (Either e)
and no other instance with Either e, then the compiler would have only one choice. So why would it need the extra information in the functional dependency?
Because type classes are open: when compiling a module, the compiler is never allowed to assume that the instances it sees are the only instances, because there could always be another instance declared in a module it hasn't seen yet.
On the other hand, if you added
instance (Error e) => MonadError String (Either e)
and didn't include the functional dependency, the compiler would still run into a problem with overlapping instances and have no way to decide, which I presume is still an error.
True. In the case of this particular *instance*, the functional dependency doesn't really add all that much. But that doesn't mean the functional dependency on the *class* is useless; there can be other instances of the class as well. -Brent