Yes, that would be helpful. In fact, all slightly incompatible versions would be useful! What a problem.
It is unclear what such a class can mean, which makes it difficult to use, because inevitably you will come across types where there are several default options, and if it does not immediately clear the one that the instance provides, you will lose all the advantages of having the class first of all.
A few examples:
For Monoid
instances Monoid
you obviously expect the identity element to be the default. But now you are again faced with the problem of many types having two or more reasonable Monoid
instances. Is the default value of Integer
0 or 1? For Monoid
, the standard library uses newtype
wrappers, but this is inconvenient and makes it difficult to work with wrapped types - it works fine with Monoid
because you get access to mconcat
and the like, but you can’t do anything interesting by default.
For Functor
types with an "empty" value, which gives an obvious default value. This is what MonadPlus
and Alternative
, and also overlap with Monoid
, and if the memory serves me at least one type where these three instances are not identical. What do you choose when there is more than one choice? Consider the lists: you can blindly add them by providing an arbitrary Monoid
, with an empty list as a person; but for Monoids
lists Monoids
you can also zipWith mappend
by providing a raised monoid with repeat mempty
as an identity. Many functors have similar instances of Monoid
, but not always both - so what you choose for lists will be conceptually incompatible with some other Functor
!
For device types such as ()
, it's not hard to choose a default value! But what about transfers? Does it make sense to choose the first constructor? Sometimes, but not always. How do people familiar with the class know?
How about Bounded
? If none of the above applies, you can use minBound
. But some of the above types may be Bounded
, so you will confuse the questions if their default value is not their minimum value.
In principle, there is enough coincidence, which seems to make sense ... but in fact you have at least three different types of classes, and trying to unify them is probably not as useful as it seems at first glance.
If you can sort things out a bit and give a clear, consistent semantic interpretation of the default value without rethinking Monoid
or another existing class to make the type class easy to use, stop and think about choosing a default, great! But I will not hope that this works.
However, it is obvious that a reasonable case, which is not covered by any class of the standard type, are singleton like ()
. In most cases, this is not very useful - for obvious reasons! - perhaps that's why there is no such class. However, one place where such a class is extremely useful is when you do something that is related to type type tinans, because that type represents the same value both at the type level and at the term level, therefore the class for such types allows you to freely manipulate type level values, and then call the term that comes with it, so you can pass it to some other function that can, for example, select an instance of a type class based on it. For this reason, I have a class on these lines in my ever-incomplete hackery type library , for example:
class TermProxy t where term :: t -- This makes explicit the lexical pun of () having type (). instance TermProxy () where term = () instance (TermProxy a, TermProxy b) => TermProxy (a, b) where term = (term, term)
I doubt such a class is very useful in any other context.