|
Boost : |
From: Howard Hinnant (hinnant_at_[hidden])
Date: 2003-11-14 12:06:06
On Nov 14, 2003, at 11:15 AM, Mat Marcus wrote:
>>
>> "Me too."
>>
>> In my opinion, "named conformance" is the right way to design most
>> interfaces, whereas "structural conformance" (as above) is the wrong
>> way.
>
> [snip]
>
> Named conformance worries me, though I haven't programmed much in
> Haskell yet. One question I have is: who writes the type classes (or
> whatever you want to call these things)? I can't imagine it is the
> library author since the generic (meta-)algorithms are meant to be
> used with an unbounded set of types(, metafunctions, etc.). But a
> library's usability would seem to suffer if the user has to manually
> assert conformance to various concepts before calling generic
> algorithms. A hybrid approach where the library author provides the
> named conformance for "common types" doesn't seem to help. I want the
> generic algorithm to be written in such a way that the compiler can
> check conformance for me. Are you really worried about accidental
> structural conformance? How do things work in practice in Haskell?
Structural conformance worries me. :-)
Two reasons:
1. Checking structural conformance will inevitably lead to slower
compile times than checking named conformance simply because there is
typically more to check structurally. By how much, I have no
experience. But it worries me.
For example checking structural conformance for a random access
iterator would involve many checks:
operator*()
operator++()
operator++(int)
operator--()
...
On the other hand, a named check could be done once and for all (with a
slight backwards compatible change to iterator_traits) by checking if
iterator_traits<T> had a nested type called iterator_category which
was convertible to std::random_access_iterator_tag.
2. D&E section 15.4 glosses over this topic somewhat. Structural
conformance based on function signatures seems inflexible compared to
structural conformance based on expressions. I.e. if the expression:
T* a = b.clone();
is supposed to work, it seems over constraining to expect the member
function:
T* clone() const;
to appear. What is wrong with:
T* clone(int policy = default_policy) const;
?
I favor named conformance with an "internal" name as opposed to an
external name (e.g. a nested type such as iterator_category). But with
an extra check to keep from hijacking the nested type name across all
libraries (e.g. iterator_category must be convertible to a
std::iterator_tag).
I find requiring external registration (specialization of a traits
class) more cumbersome, and easier to forget to do.
All that being said, there is probably room for both named and
structural conformance checks. Particularly if the structural check
involves only one or two expressions/functions as opposed to a dozen.
And even more so if the member in question appears to have a universal,
unambiguous meaning, and the signature is not likely to vary (such as
clone()?).
-Howard
Boost list run by bdawes at acm.org, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk