Luke wrote:

Thanks for your very detailed explanation of your views on the Pure
MMD scheme, Damian.  I finally understand why you're opposed to it.  I
could never really buy your previous argument: "Manhattan distance is
better".

That was never my *argument*, merely my statement-of-position. ;-)


Indeed, pure MMD will be ambiguous in more cases.  If you think
narrowly, then it causes you to write many disambiguating cases which
*usually* end up being what a manhattan metric would give you anyway. But you can get away from that terrible duplication by being more
precise about your types.  You can define type classes using empty
roles[1], where you simply organize your type dag into abstractions
that make sense to your dispatcher.  The upshot of all this is that it
forces you to think in a way so that the "*usually*" above becomes an
"always".  And it involves defining more (very small) types that make
sense to humans, not gratuitously many MMD variants which are pretty
hard to think about.

I don't see how this is a big win. You're still be forced to explicitly disambiguate. Either by defining extra variants (which is tedious), or by defining extra types (which is subtle).


You just made my primary argument against manhattan distance for me. If you change something in the middle of the class hierarchy,
manhattan distance causes multimethods on the leaves to change
semantics.  You say that pure MMD causes them to break when a change
occurs.  Isn't that better than changing?

No. If the dispatch behaviour changes under a Manhattan metric, then it only ever changes to a more specific variant. Since MMD is all about choosing the most specific variant, that's entirely appropriate, in the light of the new information. If you change type relationships, any semantics based on type relationships must naturally change.

On the other hand, under a pure ordering scheme, if you change type relationships, any semantics based on type relationships immediately *break*. That's not a graceful response to additional information. Under pure ordering, if you make a change to the type hierarchy, you have to *keep* making changes until the dispatch semantics stabilize again. For most developers that will mean a bout of "evolutionary programming", where they try adding extra types in a semi-random fashion until they seem to get the result they want. :-(


Perhaps I've made this argument before, but let me just ask a
question:  if B derives from A, C derives from A, and D derives from
C, is it sensible to say that D is "more derived" from A than B is? Now consider the following definitions:

    class A { }
    class B is A {
        method foo () { 1 }
        method bar () { 2 }
        method baz () { 3 }
    }
    class C is A {
        method foo () { 1 }
    }
    class D is C {
        method bar () { 2 }
    }

Now it looks like B is more derived than D is.  But that is, of
course, impossible to tell.  Basically I'm saying that you can't tell
the relative relationship of D and B when talking about A.  They're
both derived by some "amount" that is impossible for a compiler to
detect.  What you *can* say is that D is more derived than C.

Huh. I don't understand this at all.

In MMD you have an argument of a given type and you're trying to find the most specifically compatible parameter. That means you only ever look upwards in a hierarchy. If your argument is of type D, then you can unequivocally say that C is more compatible than A (because they share more common components), and you can also say that B is not compatible at all. The relative derivation distances of B and D *never* matter since they can never be in competition, when viewed from the perspective of a particular argument.

What we're really talking about here is how do we *combine* the compatibility measures of two or more arguments to determine the best overall fit. Pure Ordering does it in a "take my bat and go home" manner, Manhattan distance does it by weighing all arguments equally.


In conclusion, the reason that manhattan distance scares me so, and
the reason that I'm not satisfied with "use mmd 'pure'" is that for
the builtins that heavily use MMD, we require *precision rather than
dwimmyness*.  A module author who /inserts/ a type in the standard
hierarchy can change the semantics of things that aren't aware that
that type even exists.  If you're going to go messing with the
standard types, you'd better be clear about your abstractions, and if
you're not, the program deserves to die, not "dwim" around it.

That *might* be an argument that builtins ought to do "pure ordering" dispatch, but it isn't an argument in the more general case. Most people won't be futzing around with the standard type hierarchy, except at the leaves, where it doesn't matter. Most people will be using MMD for applications development, on their own type hierarchies. Someone who's (for example) building an image processing library wants to be able to add/merge/mask any two image types using the most specific available method. Adding a new image type (or supertype) might change which method is most specific, but won't change the image processor's desire. Indeed, changing the dispatch to a now more-specific method *is* the image processor's desire.

So, in counter-conclusion, Pure Ordering MMD maximizes ambiguity and thereby imposes extra development costs to achieve (generally) the same effect as Manhattan MMD achieves automatically. Both schemes can be made to detect ambiguities, and under both schemes those ambiguities can be resolved by the judicious addition of either variants or interim classes. However, by default, when POMMD detects an ambiguity, it just gives up and makes that ambiguity fatal, whereas MMMD always automatically resolves ambiguities in a plausible manner. I know which approach I'd prefer.


Oh, and the mmd style should probably look like:

    multi foo (...) is mmd<pure> {...}
    multi bar (...) is mmd<manhattan> {...}

Rather than a pragma.

The pragma would merely set the default, from which traits could cause particular multis to vary.


[1] And I find this to be useful even when using manhattan distance. I'd like to be able to define such type classes out-of-band, like:

    role Foo
        defines Bar  # Bar does Foo now
        defines Baz  # Baz does Foo now
    { }

You *really* think action-at-a-distance reverse composition is a good idea?
Seems like a maintenance nightmare to me.

Damian

Reply via email to