Frank Heckenbach wrote:
Waldek Hebisch wrote:
I wrote that I _want_ to change the rules. Two variants of Integer _are_ the same type, so current overloading really breaks normal rules.
A nice example how strange current rules are:
As I wrote, I don't need to be convinced how strange current rules are.
BTW, it looks that current rules are a quick hack:
Yes, they are! Haven't I mentioned this often enough yet (in this and other threads)?
I think there's something strange about this discussion, so let me recapitulate:
There's no question that we need better rules in the long run. But it's also clear, as previous discussions have shown, that there are some controversial issues that need to be resolved, and then it will be some work to implement better rules.
OTOH, Adriaan's problem is not one of strange rules (AFAIK), but a simple performance problem that can probably be solved, or at least alleviated, without long discussions (or could have ... ;-), and with less implementation work.
That's why I initially asked whther you are interested in the general solution of this issue (and have the time and willingness to resolve the issues in discussions, and then do the implementation, I should have added)? As you replied: "ATM I would prefer to think more before implementing general solution.", I took this for a no, so I assumed we were talking about the quick fix only.
There is Grand Design, involving function overloading and objects. It will take more then two weeks to implement. Also Grand Design depends on (or interacts with) currently unimplemented features. And of course, there are issues to be resoved before Grand Design can be called a design. For me Grand Design = general solution.
To make my answer clearer: I do not accept your dichotmy between general solution and a quick fix. Or, putting it differntly, for me quick fix is making implemented things work correctly and barfing on unimplemented stuff.
Anyway, I see 3 options now:
- Do a quick fix (you or me), without changing the rules. (And do the whole thing sometime later.)
My point is: the current rules are so strage that improving speed without changing them requires some real work. Less then I thought, but still comparable to doing things correctly. In particular, one have to seach for operators in a very specific order.
I guess that you realy mean:
1'. Change the rules and hope that nobody will notice. I admit that in my proposal there is a chance to invalidate some real programs.
- Do a quick fix, with changing the rules. I explained in my previous mail why I dislike this. (Of course, you may disagree, but I don't think you've said that/why you think that changing the rules a little now, and more later, would be useful.)
The idea was: accept uncontroversial and easy to handle cases and reject everthing else. In other words, I want a quick fix which fits into general solution.
Why changing rules now:
1) someone may be tempted to take advantage of current behaviour, removing strangeness quickly reduces risk of breaking such programs 2) the later changes should be compatible (do not change meaning of existing programs)
- Do the whole thing, resolve controversies etc. (you -- I don't have time for it anytime soon), of course with changing the rules.
Which one are we talking about?
I certainly do not have time to do now what I consider a general solution. But I want a fix which is part of general solution, which means that we need to agree on some parts of general solution. Or we may conclude that general solution is too controversial to try to implement part of it.
I have actually tried to implement what I proposed in the first post, and I get it to pass the test suite. But now I see problems with scope/modules:
1) my hack will break down when exporting operators acting on builtin types 2) it has similar strange behaviour when types names are shadowed in inner scopes
I must admit that I do not know how to correctly handle the following program:
program foo4; type t1 = string(20); operator f (x, y : t1) = c : integer; begin writeln('outer f'); c := 1; end; procedure p(n : integer); type t1 = string(n); var c1, c2 : t1; operator f (x, y: t1) = c : integer; begin writeln('second f'); c := 1; end; procedure q; type t1 = string(20); var c1, c2 : t1; begin writeln(c1 f c2); end; begin q; end; begin p(5); p(20); end .
ATM we just ICE, so we can forbid operators on dynamic types. If we make it legal, for n = 5 procedure q should see outer declaration, but for n = 20 the inner declaration should shadow the outer one, which means dispatch at runtime.
Now talking about the whole thing (which I'd actually rather not do now, unless you really want to implement it soon):
we need a piece of information which uniqely identifies a type and is propagated via interfaces. Name of main variant is first approximation, but some types it is not defined.
(And different types can have the same name, say in different modules.)
Or just going to inner scope ...
OTOH function arguments always have names and look nicer in error messages.
Indeed, for error messages we should make some effort to get reasonable names, but it may not have to be perfect. The identification will *then* surely be by types, not by names. That's always been clear to me, and I don't think I've ever said otherwise, neither in previous threads, so why are we discussing this again and again?
I was thinking of storing a list of operators defined for a given symbol in a OPERATOR_DECL because (a) we'll probably need this anyway for the real solution and (b) this part should be uncontroversial (the question will be how we use this list later). The main implementation issues about this way are GPI handling (but shouldn't be too hard), and possibly scoping (that's why I started the other thread).
Well, we need a map: operator x type x type -> function. We may have a global list of all tuples, we can attach list to operator, we can attach list to types, we can use a hash table ... Whatever representation we choose we need to add/remove imported operators as needed.
Then we'd need to know the exact types we search for, so if (later) we want to implement closest-match rules etc., we'd need to normalize types in a way that guarantees that all possibly matching types will be found. Sure, it's possible, but probably not the least-effort solution. (Yes, I know, more effort can be justified by performance issues, but compared to the current way with c n^2 and a large c, maybe anything else will just be fast enough for any reasonable code ...)
I thought about OPERATOR_DECL because we have this as a placeholder anyway (which currently doesn't have much other purpose), and we often look up the operator name before anyway, so it would reduce the search space by the number of operator symbols (not important for a hash table, but for other implementations). And it could take care of scoping with little effort -- if we decide we want that.
Apropos remove imported operators: I suppose you refer to local imports. So we have scoping issues regardless of whether we restrict operator declarations to the global level, don't we?
Yes, local imports. We could forbid importing operators in local scopes, but such restriction looks to kludgy for me (still, may be good for a quick fix). So we probably should handle scope.
Concerning OPERATOR_DECL: I agree that attaching list of operators to OPERATOR_DECL may give easy and reasonably fast solution. But I am affraid that for scoping we need a separate list anyway. We also need to attach/ detach operators on import, so I am not sure how much such list buys us.