From: Robert A Duff on
Marshall <marshall.spight(a)gmail.com> writes:

> On Nov 12, 7:12 am, "Dmitry A. Kazakov" <mail...(a)dmitry-kazakov.de>
> wrote:
>>
>> The problem arises from uncertainty about the freezing points of the type
>> definitions. A freezing point is the point where the new type becomes first
>> usable.
>
> Hmmm. This "freezing point" is an interesting concept.

I invented the term "freezing point" that Dmitry is referring to,
as part of the design of Ada 95. And it still exists now in Ada 2005.

I'm quite surprised to see this obscure term popping up in a fairly
language-independent discussion! ;-)

It's not really a very interesting concept. The freezing rules of Ada
are used to avoid certain obscure anomalies that would otherwise occur.
For example, trying to create an object of type T before the size
of T is known. Or trying to define the size of T to be twice the size
of T. (Ada allows dynamically sized types.)

Ada 83 had a similar concept called "forcing occurrences". I changed
that to "freezing point", and changed the rules around somewhat,
to make them more friendly.

> You make explicit what I am used to being implicit. But
> now that you've made it explicit, I have to ask: what
> good is this concept?

Not much good.

>...Why should we have it?

We should not have it.

>...So far
> the only thing we've said about it explicitly is that it
> causes this problem. So why not ditch the concept?

Indeed -- in a from-scratch language design, I would ditch the freezing
rules, and deal with those obscure anomalies in a different way.

- Bob
From: Robert A Duff on
Joachim Durchholz <jo(a)durchholz.org> writes:

> Not having a freezing point means the code remains extensible.
> Viz. it remains possible to add subclasses with redefinitions.

Actually, that's backwards. In Ada, you are allowed to derive new types
(i.e. "add subclasses") only after the freezing point of the parent
type(s). The idea is that when the type is known to be frozen, the
layout of its fields, and its size, and its alignment, and whether it
conforms to foreign-language conventions, and so forth are known -- so
after that point you can do things like create objects of the type,
and derive new types from it.

I really don't think this Ada-specific concept needs to be foisted on
other languages! It's really a mistake in language-design terms, and
most Ada programmers can ignore it most of the time.

- Bob
From: Robert A Duff on
Joachim Durchholz <jo(a)durchholz.org> writes:

> Marshall schrieb:
>> Very interesting. Can you expand on the difficulties that multiple
>> dispatch brings to the question of composable modules?
>
> Assume you have a multi-dispatching function
>
> f (A, B)
>
> Now the author of class A creates a subclass A', and the associated function
>
> f (A', B)
>
> Likewise for the author of B:
>
> f (A, B')
>
> Problems arise if the two authors don't know about each other (or can't
> or don't want to collaborate). Then, combining classes A' and B' in one
> system will lack a definition for
>
> f (A', B')

I agree that multiple dispatch tends to damage modularity.
I think one could come up with a coherent system by restricting
A and B to be in the same module, and require that inheriting
from them happen in lock step. So A' and B' are necessarily
connected in some way (not created by two independent authors).

I admit that I have not worked out the details of this idea.
It certainly requires that "module" not be equated with "type/class",
which is true in Ada.

CLOS supports multiple dispatch, but I can't imagine how the programmer
can possibly know which methods happen at run time, and in which order,
when you do the equivalent of "f (A', B')".

- Bob
From: S Perryman on
Robert A Duff wrote:

> Joachim Durchholz <jo(a)durchholz.org> writes:

>>Problems arise if the two authors don't know about each other (or can't
>>or don't want to collaborate). Then, combining classes A' and B' in one
>>system will lack a definition for

>> f (A', B')

> I agree that multiple dispatch tends to damage modularity.
> I think one could come up with a coherent system by restricting
> A and B to be in the same module, and require that inheriting
> from them happen in lock step. So A' and B' are necessarily
> connected in some way (not created by two independent authors).

AFAIK, there are prog langs with type checking that ensure for multiple
dispatch, appropriate impls exist for those f/A/B instances in code that
are in separate modules (I think Isaac Gouy posted an example to
comp.object a couple of years ago) . I cannot recall whether the check
was static, or (as is usual) deferred to runtime.

The difficulty will be for systems supporting concepts such as "separate
compilation" (like Ada etc) . Prog langs like CLOS which support multiple
dispatch are more 'environment' based, where the whole code base is held
within the environment, which makes checking for the existence of impls
much easier.


> I admit that I have not worked out the details of this idea.
> It certainly requires that "module" not be equated with "type/class",
> which is true in Ada.

> CLOS supports multiple dispatch, but I can't imagine how the programmer
> can possibly know which methods happen at run time, and in which order,
> when you do the equivalent of "f (A', B')".

Std CLOS is left-to-right evaluation. The most basic scheme would be :

Find all appropriate f based on A' (L1) , using the CLOS precedence
rules for inheritance etc. Do the same for B' , based on L1.
You will have a candidate f, or not (error) .


Regards,
Steven Perryman
From: Joachim Durchholz on
Robert A Duff schrieb:
> I agree that multiple dispatch tends to damage modularity.
> I think one could come up with a coherent system by restricting
> A and B to be in the same module, and require that inheriting
> from them happen in lock step. So A' and B' are necessarily
> connected in some way (not created by two independent authors).

I had the same idea :-)

However, this is a strong restriction on where you can subclass and
where you can't. If you wish to write a subclassed version of
f (A, B, C, ...)
and f, A, and B are declared in the same module, you can have
f (A', B', C, ...)
but not
f (A, B, C', ...)

In other words, when writing a new type, you have to foresee in what
functions there might be a need to write a subclass, in combinations
with what other types you're working on.

Plus you can't write an easily-subclassable add-on to an existing type.

At that point of my considerations, I sighed and shelved the idea...

> I admit that I have not worked out the details of this idea.
> It certainly requires that "module" not be equated with "type/class",
> which is true in Ada.
>
> CLOS supports multiple dispatch, but I can't imagine how the programmer
> can possibly know which methods happen at run time, and in which order,
> when you do the equivalent of "f (A', B')".

IIRC it's even worse, since CLOS allows programmers to use their own
dispatch mechanism. Plugging obscure mis-dispatch bugs with an even more
obscure mechanism, with the end result that you have to hunt the entire
code for the dispatch mechanism du jour just to see what function really
gets called - that cries out for even more obscure bugs.

Well, to be fair, it's a bad solution to a problem that doesn't seem to
have any good solutions.

My personal reaction to the situation has been a move off the OO world.
Functional languages don't use mutation and make it easy to use
higher-order functions, which means that 99% of all reasons to write a
redefinition in the first place simply vanish.
The remaining 1% is mostly optimization. That's a nonmodular activity
anyway, so having a declaration that says "for f(A',B,C',...), use f'3
in module blah", possibly conflicting with other declarations, and the
compiler is free to resolve in any way it sees fit - I think that would
be enough.

Regards,
Jo