type class design

Dear Haskellers, I've a question about type class design. When developing the set of functions for a class, there are often two or more functions, let's say f and g, where the semantics of g can be expressed by f. When writing down the code, there are two choices for g. First g is included in the type class, second g is defined outside and the signature has a context referencing the class. 1. case class Foo a where f :: ... a ... g :: ... a ... g = expr[f] 2. case class Foo a where f :: ... a ... g :: Foo a => ... a ... g = expr[f] What are the reasons for choosing 1., what are the arguments for 2.? My arguments for 1.: a) Efficiency: g may be overwritten in an instance declaration by a more efficient piece of code. b) Readability: All functions, which are logically connected are grouped together in a single syntactic construct and can be found in one place (similar to an interface in Java). My argument for 2.: c) Correctness: The semantics for g are fix (relative to f), All laws for g hold everywhere, assuming f is implemented correctly. I've sometimes the problem to balance the reasons for 1. and 2 and I doubt, that this list of criteria is complete. In the standard Haskell classes we can find both cases, even within a single class. Eq with (==) as f and (/=) as g belongs to the 1. case, so does Monad and (>>=) as f and (>>) as g. But (>=>) and the Monad class fall into the 2. case. It seem, there is no uniform or consistent strategy in the core Haskell classes. What are your best practice rules for this problem? Expecting your advices, Uwe

Hi, Uwe Schmidt wrote:
In the standard Haskell classes we can find both cases, even within a single class.
Eq with (==) as f and (/=) as g belongs to the 1. case
Note that the case of (==) and (/=) is slightly different, because not only can (/=) be defined in terms (==), but also the other way around. The default definitions of (==) and (/=) are mutually recursive, and trivially nonterminating. This leaves the choice to the instance writer to either implement (==) or (/=). Or, for performance reasons, both. Tillmann

On Fri, Oct 29, 2010 at 1:33 PM, Tillmann Rendel
Note that the case of (==) and (/=) is slightly different, because not only can (/=) be defined in terms (==), but also the other way around. The default definitions of (==) and (/=) are mutually recursive, and trivially nonterminating. This leaves the choice to the instance writer to either implement (==) or (/=). Or, for performance reasons, both.
I find these sorts of defaults deeply unsatisfying: particularly, suppose I do newtype Foo = Foo Integer deriving (Eq, Show) instance Num Foo where Foo a + Foo b = Foo (a + b) fromInteger = Foo expr = Foo 3 - Foo 2 That I haven't defined implementations for (-) or negate will not even get me a compiler warning, let alone a static error: it will just stack overflow or spin endlessly on expr. This kind of bug is notoriously difficult to track down. I'm not sure how to handle this better, though. A compiler that automatically calculated minimal complete definitions would be nice, but relatively complicated. It might be more sensible to just take all efficiency methods out of classes, and use a mechanism like rewrite rules to give an efficient implementation where possible.

On Fri, Oct 29, 2010 at 8:33 AM, Tillmann Rendel
Hi,
Uwe Schmidt wrote:
In the standard Haskell classes we can find both cases, even within a single class.
Eq with (==) as f and (/=) as g belongs to the 1. case
Note that the case of (==) and (/=) is slightly different, because not only can (/=) be defined in terms (==), but also the other way around. The default definitions of (==) and (/=) are mutually recursive, and trivially nonterminating. This leaves the choice to the instance writer to either implement (==) or (/=). Or, for performance reasons, both.
While I understand the argument in general, I've never understood why
it applies to Eq. Are there any types where it is preferable to define
(/=) instead of (==)?
--
Dave Menendez

Hi,
sorry for answering to such an old thread.
David Menendez
On Fri, Oct 29, 2010 at 8:33 AM, Tillmann Rendel
wrote: Hi,
Uwe Schmidt wrote:
In the standard Haskell classes we can find both cases, even within a single class.
Eq with (==) as f and (/=) as g belongs to the 1. case
Note that the case of (==) and (/=) is slightly different, because not only can (/=) be defined in terms (==), but also the other way around. The default definitions of (==) and (/=) are mutually recursive, and trivially nonterminating. This leaves the choice to the instance writer to either implement (==) or (/=). Or, for performance reasons, both.
While I understand the argument in general, I've never understood why it applies to Eq. Are there any types where it is preferable to define (/=) instead of (==)?
Yes for infinite data structures. Cheers, Jean-Marie

On Tue, Dec 21, 2010 at 4:30 AM, Jean-Marie Gaillourdet
Hi,
sorry for answering to such an old thread.
David Menendez
writes: On Fri, Oct 29, 2010 at 8:33 AM, Tillmann Rendel
wrote: Hi,
Uwe Schmidt wrote:
In the standard Haskell classes we can find both cases, even within a single class.
Eq with (==) as f and (/=) as g belongs to the 1. case
Note that the case of (==) and (/=) is slightly different, because not only can (/=) be defined in terms (==), but also the other way around. The default definitions of (==) and (/=) are mutually recursive, and trivially nonterminating. This leaves the choice to the instance writer to either implement (==) or (/=). Or, for performance reasons, both.
While I understand the argument in general, I've never understood why it applies to Eq. Are there any types where it is preferable to define (/=) instead of (==)?
Yes for infinite data structures.
How so?
For an infinite structure, x == y will return False or not return and
x /= y will return True or not return. We still have x /= y = not (x
== y) and I don't see any reason why one would prefer to define (/=)
instead of (==).
--
Dave Menendez

On 29 October 2010 23:28, Uwe Schmidt
Dear Haskellers,
I've a question about type class design. When developing the set of functions for a class, there are often two or more functions, let's say f and g, where the semantics of g can be expressed by f.
When writing down the code, there are two choices for g. First g is included in the type class, second g is defined outside and the signature has a context referencing the class.
1. case
class Foo a where f :: ... a ... g :: ... a ... g = expr[f]
2. case
class Foo a where f :: ... a ...
g :: Foo a => ... a ... g = expr[f]
[snip]
My argument for 2.:
c) Correctness: The semantics for g are fix (relative to f), All laws for g hold everywhere, assuming f is implemented correctly.
Another possible argument: large type classes can look daunting for both implementors and users, even if only one or two methods need to be defined for a minimal instantiation (I'm tring to work out what to do here myself, as I have some typeclasses that for efficiency reasons it might be nice to have more methods in the class, but it makes it a little overwhelming). -- Ivan Lazar Miljenovic Ivan.Miljenovic@gmail.com IvanMiljenovic.wordpress.com

Hi Ivan,
Another possible argument: large type classes can look daunting for both implementors and users, even if only one or two methods need to be defined for a minimal instantiation (I'm tring to work out what to do here myself, as I have some typeclasses that for efficiency reasons it might be nice to have more methods in the class, but it makes it a little overwhelming).
But by putting just a small part of the interface into the class does not make the live of a user any simpler. The user usually has to know the whole interface of the module. Or am I missing something? Cheers, Uwe

On 30 October 2010 22:44, Uwe Schmidt
Another possible argument: large type classes can look daunting for both implementors and users, even if only one or two methods need to be defined for a minimal instantiation (I'm tring to work out what to do here myself, as I have some typeclasses that for efficiency reasons it might be nice to have more methods in the class, but it makes it a little overwhelming).
But by putting just a small part of the interface into the class does not make the live of a user any simpler. The user usually has to know the whole interface of the module. Or am I missing something?
Well, if you have a small class then it's possible for you to split up the different types of functions to be in different modules into more logical sub-types, etc. Also, I find the documentation for the list functions in the Prelude and Data.List easier to read than the ones in ListLike [1], partially because typical documentation for class methods is typically smaller and more compact than the stand-alone functions. [1]: http://hackage.haskell.org/package/ListLike -- Ivan Lazar Miljenovic Ivan.Miljenovic@gmail.com IvanMiljenovic.wordpress.com
participants (6)
-
Ben Millwood
-
David Menendez
-
Ivan Lazar Miljenovic
-
Jean-Marie Gaillourdet
-
Tillmann Rendel
-
Uwe Schmidt