digitalmars.D - Why D needs implicit instantion or an autotype keyword
- Matthew (49/49) Jul 18 2004 Ok, some of you may have followed a recent post describing composition o...
- Ivan Senji (18/67) Jul 18 2004 this, I'm
- Matthias Becker (11/12) Jul 18 2004 Well, I proposed this some time ago, got only good response, but Walter ...
- Matthew Wilson (11/22) Jul 18 2004 didn't
- Bent Rasmussen (2/4) Jul 18 2004 I like "val", it reminds me of ML.
- Norbert Nemec (17/86) Jul 18 2004 You are talking about two connected but distinct issues here: implicit
- Bent Rasmussen (8/11) Jul 18 2004 It sure would be sweet to have both in 2.0. I like it even before I've t...
- Norbert Nemec (10/22) Jul 19 2004 I think, the feature only makes sense for variables that are immediately
- Bent Rasmussen (11/16) Jul 20 2004 I don't find it that confusing*, in fact an error message would clear up...
- Andy Friesen (11/41) Jul 20 2004 Right: "If it's easy to explain, it might be a good idea." (quoth the
- Andy Friesen (6/12) Jul 18 2004 It even looks (to my feeble brain, at least) easy to implement:
-
Carlos Santander B.
(27/27)
Jul 18 2004
"Andy Friesen"
escribió en el mensaje - Bent Rasmussen (10/14) Jul 18 2004 I can't imagine. To test this write
-
Carlos Santander B.
(20/20)
Jul 18 2004
"Bent Rasmussen"
escribió en el mensaje - Norbert Nemec (10/38) Jul 19 2004 In general, the compiler can always determine the type of an exception
- Russ Lewis (4/11) Jul 19 2004 I agree that this would be a good and useful feature for 2.0. In the
- Andy Friesen (5/19) Jul 19 2004 Maybe, but what I was trying to imply was that it's so easy to implement...
- Matthias Becker (6/13) Jul 20 2004 Hmm, I proposed it about a half year ago and we still don't have it.
- nail (23/23) Jul 20 2004 What about multimethods? It would be fine if I can do following:
- Sean Kelly (2/2) Jul 20 2004 Why the new keyword? Doesn't D handle overload resolution just fine wit...
- Matthias Becker (3/4) Jul 21 2004 Nope. There is no overload resolution at runtime. And that's what multim...
- Sean Kelly (5/10) Jul 21 2004 Please excuse me while I kick myself :) Thanks for the clarification.
- Russ Lewis (3/36) Jul 20 2004 I assume that you are aware how you can hand-code a multimethod?
- nail (17/54) Jul 20 2004 Hand-coded multimethods are posible. But I have to use bulky auxiliary
- Matthias Becker (12/35) Jul 21 2004 I proposed something similar a few days ago. My version didn't need a ne...
- nail (8/52) Jul 21 2004 I see nothing confusing in the examle above. If you mean that handle(x),...
- Matthias Becker (42/49) Jul 22 2004 MULTImethod. This means that you have methods whith is bound to multiple...
- Sean Kelly (6/31) Jul 22 2004 Good proposal. This may need to wait for better RTTI but it has the
- Matthias Becker (2/7) Jul 23 2004 Yes, that's mine, too.
- Sha Chancellor (10/49) Jul 23 2004 I'm a little confused:
- Sean Kelly (28/32) Jul 23 2004 The examples were a little bit simplistic. Assume I have a factory clas...
- Sha Chancellor (4/50) Jul 23 2004 Rather than adding syntax, should the compiler just "take care of it"?
- Sean Kelly (10/12) Jul 23 2004 It's mostly a matter of efficiency. Normal overload resolution is handl...
- Sha Chancellor (7/27) Jul 23 2004 I wasn't implying that it should be purely dynamic, I seriously dislike
- Sean Kelly (18/23) Jul 23 2004 If you can suggest a method then by all means do so. It might not be to...
- Andy Friesen (7/38) Jul 21 2004 Dynamic dispatch can be implemented by setting up an associative array
- Bent Rasmussen (6/10) Jul 28 2004 While searching for threads about implicit template instantiation I trip...
Ok, some of you may have followed a recent post describing composition of filtering operations on DTL containers, e.g. foreach(X x; cont.select!(SomePredicate).collect(SomeTxFunction).select(SomeDelegate)) { . . . } Although the compiler currently eats my machine when trying to compile this, I'm pretty confident that it can be achieved soon without much hassles. This all works reasonably neatly because all <range> objects (those returned by transformations on containers or other <ranges>) support foreach. So far so good. There'll also be support for getting a specific item out of container/range, via methods like max(), min_if(), find_first(), as in: Person m = cont.select!(SomePredicate).collect(SomeTxFunction).max(SomeDelegate) That's also quite straightforward. However, here's the rub. If you want to get hold of a range and save it for later processing, you need to know its type. The problem is that its type can be something incredibly complex. Consider the following fictional case, where we're filtering the contents of a List of Person container: List!(Person) results of select() (with delegate IsMan): MatchedNotionalRange!(_ListUtil!(Person).ListRange, DelegatePredicate!(bool delegate(in Person value), Person)) Now that's just the one level. Imagine what it looks like when we're composing another filter on top of the results of select()! In C++ one can obviate such things by writing working template functions. Since C++ supports implicit instantiation, the worker function can deduce such horrendous mouthfuls for us. Of course, this is not always appropriate, but it does help a lot. Since D is not going to have implicit instantiation, I suggest that we *must* have the autotype keyword, which will be akin to the semantics proposed for auto in C++ 0.x. That is to say, the type is deduced, at compile time - it's no variant type! - from the initialising expression, so: bool IsOdd(Number n); Number Square(Number n); autotype r = cont.select!(IsOdd).collect(Square) . . . for(; r.open; r.advance()) { writeln("Number: ", r.current); } AFAIUI, there's no major conceptual difficulty in doing this, since the compiler already knows the type of the initialising expression. The only area of uncertainty is that we might lose the polymorphic nature of the type, but I'd say that if you know what type it should be, then you don't need autotype. Kind of axiomatic, but maybe I've missed something Thoughts, everyone? Walter?!?
Jul 18 2004
"Matthew" <admin stlsoft.dot.dot.dot.dot.org> wrote in message news:cddhd8$275p$2 digitaldaemon.com...Ok, some of you may have followed a recent post describing composition of filtering operations on DTL containers, e.g. foreach(X x; cont.select!(SomePredicate).collect(SomeTxFunction).select(SomeDelegate)) { . . . } Although the compiler currently eats my machine when trying to compilethis, I'mpretty confident that it can be achieved soon without much hassles. Thisallworks reasonably neatly because all <range> objects (those returned by transformations on containers or other <ranges>) support foreach. So farso good.There'll also be support for getting a specific item out ofcontainer/range, viamethods like max(), min_if(), find_first(), as in: Person m = cont.select!(SomePredicate).collect(SomeTxFunction).max(SomeDelegate) That's also quite straightforward. However, here's the rub. If you want to get hold of a range and save itfor laterprocessing, you need to know its type. The problem is that its type can be something incredibly complex. Consider the following fictional case, where we're filtering the contentsof aList of Person container: List!(Person) results of select() (with delegate IsMan): MatchedNotionalRange!(_ListUtil!(Person).ListRange,DelegatePredicate!(booldelegate(in Person value), Person)) Now that's just the one level. Imagine what it looks like when we'recomposinganother filter on top of the results of select()! In C++ one can obviate such things by writing working template functions.SinceC++ supports implicit instantiation, the worker function can deduce such horrendous mouthfuls for us. Of course, this is not always appropriate,but itdoes help a lot. Since D is not going to have implicit instantiation, I suggest that we*must*have the autotype keyword, which will be akin to the semantics proposedfor autoin C++ 0.x. That is to say, the type is deduced, at compile time - it's no variant type! - from the initialising expression, so: bool IsOdd(Number n); Number Square(Number n); autotype r = cont.select!(IsOdd).collect(Square) . . . for(; r.open; r.advance()) { writeln("Number: ", r.current); } AFAIUI, there's no major conceptual difficulty in doing this, since thecompileralready knows the type of the initialising expression. The only area of uncertainty is that we might lose the polymorphic nature of the type, butI'd saythat if you know what type it should be, then you don't need autotype.Kind ofaxiomatic, but maybe I've missed something Thoughts, everyone? Walter?!?I like it! I also liked that proposal for C++, so why not for D? :)
Jul 18 2004
Thoughts, everyone? Walter?!?Well, I proposed this some time ago, got only good response, but Walter didn't answer. If you look at Walters style you can understand this. He declares all variables on top of his functions, like Pascal coders do or C programmers before 1999. Anyway, I don't like the name autotype. In C++0x auto is used so they don't have to introduce a new keyword. But auto is already used in D (well it is in C++, too, but with another meaning, that can't conflict in the situations, where you need the new auto). Other languages use let, var or val for this. Why don't we use one of these words? -- Matthias Becker
Jul 18 2004
"Matthias Becker" <Matthias_member pathlink.com> wrote in message news:cddv6v$2bi8$1 digitaldaemon.com...didn'tThoughts, everyone? Walter?!?Well, I proposed this some time ago, got only good response, but Walteranswer. If you look at Walters style you can understand this. He declares allvariableson top of his functions, like Pascal coders do or C programmers before1999.Anyway, I don't like the name autotype. In C++0x auto is used so theydon't haveto introduce a new keyword. But auto is already used in D (well it is inC++,too, but with another meaning, that can't conflict in the situations,where youneed the new auto). Other languages use let, var or val for this. Why don't we use one ofthesewords?I don't really care about the name, although I don't like let and val, and I assume var will be reserved for a built-in variant type in the future.
Jul 18 2004
I don't really care about the name, although I don't like let and val, andIassume var will be reserved for a built-in variant type in the future.I like "val", it reminds me of ML.
Jul 18 2004
You are talking about two connected but distinct issues here: implicit instantiation and automatic type deduction for local variables. The first is dearly needed for template metaprogramming, and especially for expression templates. It is far more than just a convenience issue. A nested expression of moderate complexity, will usually have a type of nested templates of the same depth. Furthermore: the exact type of the expression may not even be easy to deduct, but it may follow from some lengthy compile-time computation. With types of local variables, the situation is similar. Of course, you could in principle always deduct them and give them explicitly, but for a non-trivial meta-program, this deduction may be arbitrarily complicated. The compiler, of course, can always deduct it (that's why it is called compile-time-computation). B.t.w: autotypes for local variables have proven rather convenient in Sather as well. There is no meta-programming possibility, so it really is just a matter of convenience, but still, you quickly get addicted to it... Matthew wrote:Ok, some of you may have followed a recent post describing composition of filtering operations on DTL containers, e.g. foreach(X x; cont.select!(SomePredicate).collect(SomeTxFunction).select(SomeDelegate)) { . . . } Although the compiler currently eats my machine when trying to compile this, I'm pretty confident that it can be achieved soon without much hassles. This all works reasonably neatly because all <range> objects (those returned by transformations on containers or other <ranges>) support foreach. So far so good. There'll also be support for getting a specific item out of container/range, via methods like max(), min_if(), find_first(), as in: Person m = cont.select!(SomePredicate).collect(SomeTxFunction).max(SomeDelegate) That's also quite straightforward. However, here's the rub. If you want to get hold of a range and save it for later processing, you need to know its type. The problem is that its type can be something incredibly complex. Consider the following fictional case, where we're filtering the contents of a List of Person container: List!(Person) results of select() (with delegate IsMan): MatchedNotionalRange!(_ListUtil!(Person).ListRange, DelegatePredicate!(bool delegate(in Person value), Person)) Now that's just the one level. Imagine what it looks like when we're composing another filter on top of the results of select()! In C++ one can obviate such things by writing working template functions. Since C++ supports implicit instantiation, the worker function can deduce such horrendous mouthfuls for us. Of course, this is not always appropriate, but it does help a lot. Since D is not going to have implicit instantiation, I suggest that we *must* have the autotype keyword, which will be akin to the semantics proposed for auto in C++ 0.x. That is to say, the type is deduced, at compile time - it's no variant type! - from the initialising expression, so: bool IsOdd(Number n); Number Square(Number n); autotype r = cont.select!(IsOdd).collect(Square) . . . for(; r.open; r.advance()) { writeln("Number: ", r.current); } AFAIUI, there's no major conceptual difficulty in doing this, since the compiler already knows the type of the initialising expression. The only area of uncertainty is that we might lose the polymorphic nature of the type, but I'd say that if you know what type it should be, then you don't need autotype. Kind of axiomatic, but maybe I've missed something Thoughts, everyone? Walter?!?
Jul 18 2004
B.t.w: autotypes for local variables have proven rather convenient inSatheras well. There is no meta-programming possibility, so it really is just a matter of convenience, but still, you quickly get addicted to it...It sure would be sweet to have both in 2.0. I like it even before I've tried it. :-) val t, i; ... t = s[5 .. 25]; i = index(s,"abc"); begone bloat
Jul 18 2004
Bent Rasmussen wrote:I think, the feature only makes sense for variables that are immediately initialized: val t = s[5 .. 25]; val i = index(s,"abc"); since the initialization is needed to determine the type. Of course, it would be possible to allow predeclaring such variables, but then you would have to assure that the variable is initialized at exactly one point, and it would seem strange that the first assignment to the variable has a special meaning compared to the others.B.t.w: autotypes for local variables have proven rather convenient inSatheras well. There is no meta-programming possibility, so it really is just a matter of convenience, but still, you quickly get addicted to it...It sure would be sweet to have both in 2.0. I like it even before I've tried it. :-) val t, i; ... t = s[5 .. 25]; i = index(s,"abc");
Jul 19 2004
since the initialization is needed to determine the type. Of course, it would be possible to allow predeclaring such variables, but then you would have to assure that the variable is initialized at exactly one point, and it would seem strange that the first assignment to the variable has a special meaning compared to the others.I don't find it that confusing*, in fact an error message would clear up any confusion quite easily: "(23): attempt to redefine type of 't' from prior definition (15)" or something like that. It is a choice to be made by the programmer. Its one more "use it if you like it, don't if you don't" kind of thing. But of course if it turns out confusing to too many then its probably not a good idea. It would be interesting to have an editor with sufficient compiler interaction to annotate the types of values as you assign expressions to them. * But then there's no experience with it to speak of.
Jul 20 2004
Norbert Nemec wrote:Bent Rasmussen wrote:Right: "If it's easy to explain, it might be a good idea." (quoth the zen of Python) - Can be used for local variables and constants only. - Must be initialized in its declaration. The attribute's type is statically determined by the initializer. - Otherwise not special in any way. (no implicit constness, runtime variant behaviour or anything like that) Also, this would offer a simple way to get an integer whose size is whatever the host CPU likes best: var x = 0; -- andyI think, the feature only makes sense for variables that are immediately initialized: val t = s[5 .. 25]; val i = index(s,"abc"); since the initialization is needed to determine the type. Of course, it would be possible to allow predeclaring such variables, but then you would have to assure that the variable is initialized at exactly one point, and it would seem strange that the first assignment to the variable has a special meaning compared to the others.B.t.w: autotypes for local variables have proven rather convenient inSatheras well. There is no meta-programming possibility, so it really is just a matter of convenience, but still, you quickly get addicted to it...It sure would be sweet to have both in 2.0. I like it even before I've tried it. :-) val t, i; ... t = s[5 .. 25]; i = index(s,"abc");
Jul 20 2004
Matthew wrote:Since D is not going to have implicit instantiation, I suggest that we *must* have the autotype keyword, which will be akin to the semantics proposed for auto in C++ 0.x. That is to say, the type is deduced, at compile time - it's no variant type! - from the initialising expression, so: Thoughts, everyone? Walter?!?It even looks (to my feeble brain, at least) easy to implement: autotype r = expr; // equivalent to: typeof(expr) r = expr; -- andy
Jul 18 2004
"Andy Friesen" <andy ikagames.com> escribió en el mensaje news:cde51k$2dpc$1 digitaldaemon.com | Matthew wrote: || Since D is not going to have implicit instantiation, I suggest that we *must* || have the autotype keyword, which will be akin to the semantics proposed for auto || in C++ 0.x. That is to say, the type is deduced, at compile time - it's no || variant type! - from the initialising expression, so: || || Thoughts, everyone? Walter?!? | I don't really care about the name, but I do like the idea. | It even looks (to my feeble brain, at least) easy to implement: | | autotype r = expr; | // equivalent to: | typeof(expr) r = expr; | | -- andy What if "expr" is really complex (like that really long expression that Matthew posted before) and the compiler can't directly determine what it's type is? Wouldn't it mean evaluating it twice? Of course, if it just can't happen, then just tell me so, I just wanna be sure. ----------------------- Carlos Santander Bernal
Jul 18 2004
What if "expr" is really complex (like that really long expression that Matthew posted before) and the compiler can't directly determine what it's type is? Wouldn't it mean evaluating it twice? Of course, if it just can't happen, then just tell me so, I just wanna be sure.I can't imagine. To test this write type value = expr; where typeof(expr) is incompatible with type, and expr is an arbitrarily complex expression. It should be clear that since the compiler can catch type mismatches between the type of value and the type of expr, it must know the type of expr and hence be able to deduce the type of a val/autotype. I can imagine implicit template instantiation complex to implement though. But then either it can deduce the types or it can't, in which case compilation fails.
Jul 18 2004
"Bent Rasmussen" <exo bent-rasmussen.info> escribió en el mensaje news:cdf91l$2sjf$1 digitaldaemon.com | I can't imagine. To test this write | | type value = expr; | | where typeof(expr) is incompatible with type, and expr is an arbitrarily | complex expression. | | It should be clear that since the compiler can catch type mismatches between | the type of value and the type of expr, it must know the type of expr and | hence be able to deduce the type of a val/autotype. | | I can imagine implicit template instantiation complex to implement though. | But then either it can deduce the types or it can't, in which case | compilation fails. Yes, I think that makes sense. ----------------------- Carlos Santander Bernal
Jul 18 2004
Carlos Santander B. wrote:"Andy Friesen" <andy ikagames.com> escribió en el mensaje news:cde51k$2dpc$1 digitaldaemon.com | Matthew wrote: || Since D is not going to have implicit instantiation, I suggest that we *must* || have the autotype keyword, which will be akin to the semantics proposed for auto || in C++ 0.x. That is to say, the type is deduced, at compile time - it's no || variant type! - from the initialising expression, so: || || Thoughts, everyone? Walter?!? | I don't really care about the name, but I do like the idea. | It even looks (to my feeble brain, at least) easy to implement: | | autotype r = expr; | // equivalent to: | typeof(expr) r = expr; | | -- andy What if "expr" is really complex (like that really long expression that Matthew posted before) and the compiler can't directly determine what it's type is? Wouldn't it mean evaluating it twice? Of course, if it just can't happen, then just tell me so, I just wanna be sure.In general, the compiler can always determine the type of an exception without knowledge of its usage. There are just two exceptions: * function pointers/delegates to overloaded functions. The syntax here is not sufficient to determine which of the functions is meant, so the compiler has to look at the variable that it is assigned to. * struct/array initializers. Even though these look like plain assignments at first sight, they are not. In both cases, type deduction would not work. In all other cases, it is no problem.
Jul 19 2004
I agree that this would be a good and useful feature for 2.0. In the meantime, it would be a very easy thing to implement with a preprocessing tool. Andy Friesen wrote:It even looks (to my feeble brain, at least) easy to implement: autotype r = expr; // equivalent to: typeof(expr) r = expr; -- andy
Jul 19 2004
Russ Lewis wrote:Andy Friesen wrote:Maybe, but what I was trying to imply was that it's so easy to implement that it could realistically be added before 1.0. (the weight of the world hasn't crushed my hopes yet!) -- andyIt even looks (to my feeble brain, at least) easy to implement: autotype r = expr; // equivalent to: typeof(expr) r = expr; -- andyI agree that this would be a good and useful feature for 2.0. In the meantime, it would be a very easy thing to implement with a preprocessing tool.
Jul 19 2004
Hmm, I proposed it about a half year ago and we still don't have it. Anyway, are we going to allow something like this: void foo (autotype bar) {...} So we would have the missed implicitly instantiated functions. -- Matthias BeckerI agree that this would be a good and useful feature for 2.0. In the meantime, it would be a very easy thing to implement with a preprocessing tool.Maybe, but what I was trying to imply was that it's so easy to implement that it could realistically be added before 1.0. (the weight of the world hasn't crushed my hopes yet!)
Jul 20 2004
What about multimethods? It would be fine if I can do following: class Figure { ... } class Rect : Figure { ... } class Circle: Figure { ... } class Triangle: Figure { ... } float IntersectionSquare(Rect r, Circle c) // (1) { ... } float IntersectionSquare(Circle r, Circle c) // (2) { ... } float IntersectionSquare(Rect r, Figure f) // (3) { ... } multimethod float IntersectionSquare(Figure a, Figure b); void main() { Figure r = new Rect; Figure c = new Circle; Figure t = new Triangle; float sq; sq = IntersectionSquare(r, c); // (1) called sq = IntersectionSquare(c, c); // (2) called sq = IntersectionSquare(r, t); // (3) called } Any facts against?
Jul 20 2004
Why the new keyword? Doesn't D handle overload resolution just fine without it? Sean
Jul 20 2004
In article <cdjekg$1ki0$1 digitaldaemon.com>, Sean Kelly says...Why the new keyword? Doesn't D handle overload resolution just fine without it?Nope. There is no overload resolution at runtime. And that's what multimethods are all about.
Jul 21 2004
In article <cdlc1g$2g7g$1 digitaldaemon.com>, Matthias Becker says...In article <cdjekg$1ki0$1 digitaldaemon.com>, Sean Kelly says...Please excuse me while I kick myself :) Thanks for the clarification. BTW, I agree that multimethods would be a great feature to have, provided the syntax were fitting. I don't like the idea of a new keyword in this case. SeanWhy the new keyword? Doesn't D handle overload resolution just fine without it?Nope. There is no overload resolution at runtime. And that's what multimethods are all about.
Jul 21 2004
I assume that you are aware how you can hand-code a multimethod? I agree that this would be very cool to be implemented automatically. nail wrote:What about multimethods? It would be fine if I can do following: class Figure { ... } class Rect : Figure { ... } class Circle: Figure { ... } class Triangle: Figure { ... } float IntersectionSquare(Rect r, Circle c) // (1) { ... } float IntersectionSquare(Circle r, Circle c) // (2) { ... } float IntersectionSquare(Rect r, Figure f) // (3) { ... } multimethod float IntersectionSquare(Figure a, Figure b); void main() { Figure r = new Rect; Figure c = new Circle; Figure t = new Triangle; float sq; sq = IntersectionSquare(r, c); // (1) called sq = IntersectionSquare(c, c); // (2) called sq = IntersectionSquare(r, t); // (3) called } Any facts against?
Jul 20 2004
In article <cdkc7v$2217$1 digitaldaemon.com>, Russ Lewis says...I assume that you are aware how you can hand-code a multimethod? I agree that this would be very cool to be implemented automatically. nail wrote:Hand-coded multimethods are posible. But I have to use bulky auxiliary constructions a-la dispatcher. This makes code less readable and more comlicated. Integrated feature will resolve this problem in elegant way. Sean Kelly <sean f4.ca> says...What about multimethods? It would be fine if I can do following: class Figure { ... } class Rect : Figure { ... } class Circle: Figure { ... } class Triangle: Figure { ... } float IntersectionSquare(Rect r, Circle c) // (1) { ... } float IntersectionSquare(Circle r, Circle c) // (2) { ... } float IntersectionSquare(Rect r, Figure f) // (3) { ... } multimethod float IntersectionSquare(Figure a, Figure b); void main() { Figure r = new Rect; Figure c = new Circle; Figure t = new Triangle; float sq; sq = IntersectionSquare(r, c); // (1) called sq = IntersectionSquare(c, c); // (2) called sq = IntersectionSquare(r, t); // (3) called } Any facts against?Why the new keyword? Doesn't D handle overload resolution just fine without it?No, it would not. If I have: class A {} class B : A {} void foo(A a1, A a2) { std.c.printf("A!"); } void foo(B b1, B b2) { std.c.printf("B!"); } void main() { A x = new B; A y = new B; foo(x, y); } Program will print "A!" that is not what I want.
Jul 20 2004
What about multimethods? It would be fine if I can do following: class Figure { ... } class Rect : Figure { ... } class Circle: Figure { ... } class Triangle: Figure { ... } float IntersectionSquare(Rect r, Circle c) // (1) { ... } float IntersectionSquare(Circle r, Circle c) // (2) { ... } float IntersectionSquare(Rect r, Figure f) // (3) { ... } multimethod float IntersectionSquare(Figure a, Figure b); void main() { Figure r = new Rect; Figure c = new Circle; Figure t = new Triangle; float sq; sq = IntersectionSquare(r, c); // (1) called sq = IntersectionSquare(c, c); // (2) called sq = IntersectionSquare(r, t); // (3) called } Any facts against?I proposed something similar a few days ago. My version didn't need a new keyword and was more general. This ability isn't neccessarilly restricted to multimethods. You could use it for any kind of runtime dispatching. To use your notation: float handle(HandleDerived1 foo) { ... } float handle(HandleDerived2 foo) { ... } multimethod float handle(HandleBase foo); You see, the name multimethod is very confusing here. -- Matthias Becker
Jul 21 2004
In article <cdlc89$2gai$1 digitaldaemon.com>, Matthias Becker says...I see nothing confusing in the examle above. If you mean that handle(x), where x is realy HandleBase is undefined then exception must be thrown. Or maybe more acceptable way - throw compile-time error if such method for base class is undefined. I don't impose this kind of notation. I simply said that multimethods are powerful things and having them integrated in language would be very and very nicely.What about multimethods? It would be fine if I can do following: class Figure { ... } class Rect : Figure { ... } class Circle: Figure { ... } class Triangle: Figure { ... } float IntersectionSquare(Rect r, Circle c) // (1) { ... } float IntersectionSquare(Circle r, Circle c) // (2) { ... } float IntersectionSquare(Rect r, Figure f) // (3) { ... } multimethod float IntersectionSquare(Figure a, Figure b); void main() { Figure r = new Rect; Figure c = new Circle; Figure t = new Triangle; float sq; sq = IntersectionSquare(r, c); // (1) called sq = IntersectionSquare(c, c); // (2) called sq = IntersectionSquare(r, t); // (3) called } Any facts against?I proposed something similar a few days ago. My version didn't need a new keyword and was more general. This ability isn't neccessarilly restricted to multimethods. You could use it for any kind of runtime dispatching. To use your notation: float handle(HandleDerived1 foo) { ... } float handle(HandleDerived2 foo) { ... } multimethod float handle(HandleBase foo); You see, the name multimethod is very confusing here. -- Matthias Becker
Jul 21 2004
I see nothing confusing in the examle above. If you mean that handle(x), where x is realy HandleBase is undefined then exception must be thrown. Or maybe more acceptable way - throw compile-time error if such method for base class is undefined. I don't impose this kind of notation. I simply said that multimethods are powerful things and having them integrated in language would be very and very nicely.MULTImethod. This means that you have methods whith is bound to multiple objects instead of only one. In my example I used it on only one object, so the name multimethod is very confusing. Have you read my proposal? // this is a fallback function bool intersect(Shape foo, Shape bar) {...} // Intersection of Triangle and Square bool intersect(Shape Triangle foo, Shape Square bar) {...} // Intersection of Square and Triangle bool intersect(Shape Square foo, Shape Triangle bar) {...} // Intersection of two Squares bool intersect(Shape Square foo, Shape Square bar) {...} You have one compiletimetype (here Shape) and one runtimetype (Here Triangle and Square). You can do things you can't with your version: class Base1{} class Base2{} class Derived1FromBase1 : Base1 {} class Derived2FromBase1 : Base1 {} class Derived1FromBase2 : Base2 {} class Derived2FromBase2 : Base2 {} void func (Base1 Derived1FromBase1 x) {...} void func (Base1 Derived2FromBase1 x) {...} void func (Base2 Derived1FromBase2 x) {...} void func (Base2 Derived2FromBase2 x) {...} So you can do: Base1 b1 = new Derived2FromBase1(); func (b1); Base2 b2 = new Derived1FromBase2(); func (b2); I don't need a new keyword and my version is more flexible. My version allows a fallback-function i none of the derived forms fits, ... . I don't want to sound arogant. -- Matthias Becker
Jul 22 2004
Matthias Becker wrote:MULTImethod. This means that you have methods whith is bound to multiple objects instead of only one. In my example I used it on only one object, so the name multimethod is very confusing. Have you read my proposal? // this is a fallback function bool intersect(Shape foo, Shape bar) {...} // Intersection of Triangle and Square bool intersect(Shape Triangle foo, Shape Square bar) {...} // Intersection of Square and Triangle bool intersect(Shape Square foo, Shape Triangle bar) {...} // Intersection of two Squares bool intersect(Shape Square foo, Shape Square bar) {...} You have one compiletimetype (here Shape) and one runtimetype (Here Triangle and Square).Good proposal. This may need to wait for better RTTI but it has the best syntax I've seen. And oddly, a very similar proposal popped up today on comp.std.c++: http://groups.google.com/groups?selm=194c1211.0407220349.3bededd3%40posting.google.com&output=gplain Sean
Jul 22 2004
Good proposal. This may need to wait for better RTTI but it has the best syntax I've seen.Thanks.And oddly, a very similar proposal popped up today on comp.std.c++: http://groups.google.com/groups?selm=194c1211.0407220349.3bededd3%40posting.google.com&output=gplainYes, that's mine, too.
Jul 23 2004
In article <cdpp68$1a61$1 digitaldaemon.com>, Sean Kelly <sean f4.ca> wrote:Matthias Becker wrote:I'm a little confused: Is this different from function overloading in C? If not, can't the compile time type be deduced from the child class? As in, why do I need Shape Triangle. Triangle seems to imply Shape to me. On the other hand, is this trying to allow you to bind one function to several child types of a class, without accepting them all? That would be something interesting, although I've never ran into a need for such a feature.MULTImethod. This means that you have methods whith is bound to multiple objects instead of only one. In my example I used it on only one object, so the name multimethod is very confusing. Have you read my proposal? // this is a fallback function bool intersect(Shape foo, Shape bar) {...} // Intersection of Triangle and Square bool intersect(Shape Triangle foo, Shape Square bar) {...} // Intersection of Square and Triangle bool intersect(Shape Square foo, Shape Triangle bar) {...} // Intersection of two Squares bool intersect(Shape Square foo, Shape Square bar) {...} You have one compiletimetype (here Shape) and one runtimetype (Here Triangle and Square).Good proposal. This may need to wait for better RTTI but it has the best syntax I've seen. And oddly, a very similar proposal popped up today on comp.std.c++: http://groups.google.com/groups?selm=194c1211.0407220349.3bededd3%40posting.go ogle.com&output=gplain Sean
Jul 23 2004
In article <schancel-C035F9.10362623072004 digitalmars.com>, Sha Chancellor says...I'm a little confused: Is this different from function overloading in C? If not, can't the compile time type be deduced from the child class? As in, why do I need Shape Triangle. Triangle seems to imply Shape to me.The examples were a little bit simplistic. Assume I have a factory class that returns different objects based on user input: class Base {} class Derived1 : Base {} class Derived2 : Bse {} void multimethod( Base b ) {} void multimethod( Derived1 b ) {} void multimethod( Derived2 b ) {} Base build( char[] type ) { if( type == "Derived1" ) return new Derived1(); if( type == "Derived2" ) return new Derived2(); } Base b = build( "Derived1" ); multimethod( b ); In D and C++ land, multimethod(base) will be called because that is the type of the variable b. The compiler doesn't try and determine the type of the underlying instance. I saw a good example of multimethods I think on Gamasutra.Com where the programmer had optimized collision detection functions for different pairs of shapes. Because the shapes were all created dynamically at runtime it wasn't possible to rely on standard overload resolution to find the right function. And if all else fails there's always multimethod(base) to fall back on. This type of thing can be accomplished manually but the implementations tend to be complex and brittle. Having language support makes life much easier in both respects. Sean
Jul 23 2004
In article <cdrjj5$28sq$1 digitaldaemon.com>, Sean Kelly <sean f4.ca> wrote:In article <schancel-C035F9.10362623072004 digitalmars.com>, Sha Chancellor says...Rather than adding syntax, should the compiler just "take care of it"? Is there a reason why it can't, or is adding syntax just easier?I'm a little confused: Is this different from function overloading in C? If not, can't the compile time type be deduced from the child class? As in, why do I need Shape Triangle. Triangle seems to imply Shape to me.The examples were a little bit simplistic. Assume I have a factory class that returns different objects based on user input: class Base {} class Derived1 : Base {} class Derived2 : Bse {} void multimethod( Base b ) {} void multimethod( Derived1 b ) {} void multimethod( Derived2 b ) {} Base build( char[] type ) { if( type == "Derived1" ) return new Derived1(); if( type == "Derived2" ) return new Derived2(); } Base b = build( "Derived1" ); multimethod( b ); In D and C++ land, multimethod(base) will be called because that is the type of the variable b. The compiler doesn't try and determine the type of the underlying instance. I saw a good example of multimethods I think on Gamasutra.Com where the programmer had optimized collision detection functions for different pairs of shapes. Because the shapes were all created dynamically at runtime it wasn't possible to rely on standard overload resolution to find the right function. And if all else fails there's always multimethod(base) to fall back on. This type of thing can be accomplished manually but the implementations tend to be complex and brittle. Having language support makes life much easier in both respects. Sean
Jul 23 2004
In article <schancel-C9539F.11483623072004 digitalmars.com>, Sha Chancellor says...Rather than adding syntax, should the compiler just "take care of it"? Is there a reason why it can't, or is adding syntax just easier?It's mostly a matter of efficiency. Normal overload resolution is handled at compile-time, while multimethod determination is handled at run-time. If I know that only 2% of the functions I call are for dynamic objects I don't necessarily want to pay the performance cost of having all function calls resolved at run-time. This is a side-effect of D being a weird mutant conglomeration of a few different programming styles, much like C++. For a purely dynamic language you might want to look into Smalltalk. Sean
Jul 23 2004
In article <cdroqu$2fnt$1 digitaldaemon.com>, Sean Kelly <sean f4.ca> wrote:In article <schancel-C9539F.11483623072004 digitalmars.com>, Sha Chancellor says...I wasn't implying that it should be purely dynamic, I seriously dislike Obj-C and similar languages. I was implying that the compiler might be able to make the determination based on existing information rather than having to be explicitly told. Although I can imagine adding syntax makes it easier.Rather than adding syntax, should the compiler just "take care of it"? Is there a reason why it can't, or is adding syntax just easier?It's mostly a matter of efficiency. Normal overload resolution is handled at compile-time, while multimethod determination is handled at run-time. If I know that only 2% of the functions I call are for dynamic objects I don't necessarily want to pay the performance cost of having all function calls resolved at run-time. This is a side-effect of D being a weird mutant conglomeration of a few different programming styles, much like C++. For a purely dynamic language you might want to look into Smalltalk. Sean
Jul 23 2004
In article <schancel-FF3823.14215323072004 digitalmars.com>, Sha Chancellor says...I wasn't implying that it should be purely dynamic, I seriously dislike Obj-C and similar languages. I was implying that the compiler might be able to make the determination based on existing information rather than having to be explicitly told. Although I can imagine adding syntax makes it easier.If you can suggest a method then by all means do so. It might not be too hard to do in C++ -- dynamically bind for data allocated on the heap and statically bind otherwise -- but in D there isn't such a clear distinction. The only other method I can think of would be to either do something special in the function declaration or define a new statement wrapper: class A {} class B : A {} void func( A a ) {} void func( B b ) {} A a = new B(); func( a ); // calls func(A) dynamic { func( a ); // calls func(B) if it exists, otherwise func(A) } Sean
Jul 23 2004
nail wrote:What about multimethods? It would be fine if I can do following: class Figure { ... } class Rect : Figure { ... } class Circle: Figure { ... } class Triangle: Figure { ... } float IntersectionSquare(Rect r, Circle c) // (1) { ... } float IntersectionSquare(Circle r, Circle c) // (2) { ... } float IntersectionSquare(Rect r, Figure f) // (3) { ... } multimethod float IntersectionSquare(Figure a, Figure b); void main() { Figure r = new Rect; Figure c = new Circle; Figure t = new Triangle; float sq; sq = IntersectionSquare(r, c); // (1) called sq = IntersectionSquare(c, c); // (2) called sq = IntersectionSquare(r, t); // (3) called } Any facts against?Dynamic dispatch can be implemented by setting up an associative array that maps ClassInfo to a delegate, and writing a method that indexes the classinfo of its argument and calls. The trick, then, becomes working out some automatic (or nearly automatic) way of filling up that associative array. :) -- andy
Jul 21 2004
In article <cdm3hu$2qqb$1 digitaldaemon.com>, Andy Friesen says...Actually, I started work on a solution for this the beginning of the thread. The result is almost exactly what you propose here Andy. ( well, mostly ) ;) Take a look at the example in main, it pretty much handles the proposed case menioned at the beginning of the thread. Personally, I hate template-heavy solutions but that was the only way I could figure out how to capture the call signatures of each delegate. The only place type safety breaks is in the return value, which is broken at the moment. Basically the interesting stuff is all in the Delegate class templates. Each Delegate takes a return type and n types that correspond to the parameters of a function. The template constructor requires a valid delegate that matches this signature. The class itself exposes several methods for calling the underlying method, as well as validating the types of the passed parameters in a strict or relaxed fashion. The MultimethodDelegate itself maintains a list of abstract delegates for call dispatching. When a call is made via opCall(...) it looks for the first best match and forwards the arguments to that delegate. Another drawback is that the delegate templates are specialized by the number of parameters and the return type. So Delegate0 has no args, Delegate1 has one, Delegate2 has two args... the rest (Delegate3 on up) are left as an exercise to the developer. As far as I know, this is an age-old limitation to Templates that comes from D's C++ heritage. As a bonus, I threw in some extra helper templates and a MulticastDelegate that I was finally able to finish thanks to the ideas put forward here in the NG. If you have any fixes/changes/suggestions, please post them back here so we can all benefit. :) - Pragma begin 0644 multimethod.d M=F%L*7L-" D)<F5T=7)N(&-A<W0H4E0I=F%L.PT*"7T-"GT-" T*=&5M<&QA M87)T3G5L;"A25#IV;VED*7L-" E25"!3;6%R=$YU;&PH*7L +RID;R!N;W1H M"G1E;7!L871E(%9E<FEF>4%R9TUA=&-H*%0Q*7L-" EB:70 5F5R:69Y07)G M36%T8V H=F]I9"H 87)G+%1Y<&5);F9O('1Y<&4I>PT*"0ER971U<FXH='EP M:60J(&%R9RQ4>7!E26YF;R!T>7!E*7L-" D):68H87)G(#T](&YU;&PI(')E M"7)E='5R;BAO("$](&YU;&P )B8 ;RYC;&%S<VEN9F\ /3T]("AC87-T*%1Y M='5R;BAT>7!E(#T M<FEF>4%R9TUA=&-H4F5L87AE9"AV;VED*B!A<F<L5'EP94EN9F\ ='EP92E[ M"0ER971U<FXH9F%L<V4I.PT*"7T-"GT-" T*86)S=')A8W0 8VQA<W, 1&5L M96=A=&5"87-E>PT*"51Y<&5);F9O(&=E=%)E='5R;E1Y<&4H*3L-" E4>7!E M26YF;UM=(&=E=%-I9VYA='5R92 I.PT*"79O:60J(&1O0V%L;"AV;VED*EM= M(&%R9W,I.PT*"6)I="!V97)I9GE!<F=U;65N=',H=F]I9"I;72!A<F=S+"!4 M960H=F]I9"I;72!A<F=S+"!4>7!E26YF;UM=('1Y<&5S*3L-"GT-" T*8VQA M<W, 1&5L96=A=&4P*%)4*2`Z($1E;&5G871E0F%S97L-" E25"!D96QE9V%T M92 I(&UO9&5L.PT*"7-T871I8R!C;VYS="!4>7!E26YF;UM=('-I9VYA='5R M92`](%M=.PT*"0T*"51Y<&5);F9O(&=E=%)E='5R;E1Y<&4H*7L-" D)<F5T M=&AI<RA25"!D96QE9V%T92 I(&UO9&5L*7L-" D)=&AI<RYM;V1E;"`](&UO M"0T*"0T*"79O:60J(&1O0V%L;"AV;VED*EM=(&%R9W,I>PT*"0ER971U<FX M9W5M96YT<RAV;VED*EM=(&%R9W,L(%1Y<&5);F9O6UT ='EP97,I>PT*"0ER M<F=U;65N='-296QA>&5D*'9O:60J6UT 87)G<RP 5'EP94EN9F];72!T>7!E M8VQA<W, 1&5L96=A=&4Q*%)4+%0Q*2`Z($1E;&5G871E0F%S97L-" E25"!D M6UT <VEG;F%T=7)E(#T M0V%L;"A25"!D96QE9V%T92A4,2D 9"Q4,2!O;F4I>PT*"0D)<F5T=7)N(&-A M<W0H=F]I9"HI(&0H;VYE*3L-" D)?0T*"7T-" D-" ET96UP;&%T92!6;VED M4'1R0V%L;"A25#IV;VED+%0Q*7L-" D)=F]I9"H 5F]I9%!T<D-A;&PH4E0 M9W,I>PT*"0ER971U<FX 5F]I9%!T<D-A;&PA*%)4+%0Q*2AM;V1E;"QC87-T M<F5T=7)N*&UO9&5L*&]N92DI.PT*"7T-" D-" EB:70 =F5R:69Y07)G=6UE M;B -" D)"6%R9W,N;&5N9W1H(#T M<FEF>4%R9W5M96YT<U)E;&%X960H=F]I9"I;72!A<F=S+"!4>7!E26YF;UM= M('1Y<&5S*7L-" D)<F5T=7)N*`T*"0D)87)G<RYL96YG=& /3T ,2`F) T* M"0D)5F5R:69Y07)G36%T8VA296QA>&5D(2A4,2DH87)G<ULP72QT>7!E<ULP M72D-" D)*3L-" E]"0D-"GT-" T*8VQA<W, 1&5L96=A=&4R*%)4+%0Q+%0R M*2`Z($1E;&5G871E0F%S97L-" E25"!D96QE9V%T92A4,2Q4,BD ;6]D96P[ M"7T-" T*"79O:60J(&1O0V%L;"AV;VED*EM=(&%R9W,I>PT*"0ER971U<FX M5F]I9%!T<D-A;&PA*%)4+%0Q+%0R*2AM;V1E;"QC87-T*%0Q*6%R9W-;,%TL M8V%S="A4,BEA<F=S6S%=*3L-" E]"0T*"0T*"5)4(&]P0V%L;"A4,2!O;F4L M8FET('9E<FEF>4%R9W5M96YT<RAV;VED*EM=(&%R9W,L(%1Y<&5);F9O6UT M;&5N9W1H(#T M87-S($UU;'1I8V%S=$1E;&5G871E*$14.B!$96QE9V%T94)A<V4I>PT*"414 M6UT 9&5L96=A=&5,:7-T.PT*"0T*"4UU;'1I8V%S=$1E;&5G871E(&]P061D M<W-I9VXH375L=&EC87-T1&5L96=A=&4 ;60I>PT*"0ED96QE9V%T94QI<W0 M(%]A<F=P='(L5'EP94EN9F];72!?87)G=6UE;G1S*7L-" D)=F]I9"I;72!A M"0EA<F=S('X]("IC87-T*'9O:60J*BE?87)G<'1R.PT*"0D)7V%R9W!T<B`K M?0D-"GT-" T*8VQA<W, 375L=&EM971H;V1$96QE9V%T97L-" E$96QE9V%T M92!O<$%D9$%S<VEG;BA$96QE9V%T94)A<V4 9&5L*7L-" D)9&5L96=A=&5, M:7-T('X](&1E;#L-" D)<F5T=7)N*'1H:7,I.PT*"7T-" D-" E-=6QT:6UE M=&AO9$1E;&5G871E(&]P061D07-S:6=N*$UU;'1I;65T:&]D1&5L96=A=&4 M;60I>PT*"0ED96QE9V%T94QI<W0 ?CT ;60N9&5L96=A=&5,:7-T.PT*"0ER M9"!V;VED*B!D;T-A;&Q);G1E<FYA;"AV;VED*B!?87)G<'1R+%1Y<&5);F9O M>7!E26YF;R!A<F<[(%]A<F=U;65N=',I>PT*"0D)87)G<R!^/2`J8V%S="AV M;VED*BHI7V%R9W!T<CL-" D)"5]A<F=P='( *ST 87)G+G1S:7IE.PT*"0E] M*7L-" D)"6EF*&1B+G9E<FEF>4%R9W5M96YT<RAA<F=S+%]A<F=U;65N=',I M(&UA:6XH*7L-" EC;&%S<R!&:6=U<F4 >R` ?0T*"6-L87-S(%)E8W0 .B!& M<G-E8W1I;VY3<75A<F4H0VER8VQE('(L($-I<F-L92!C*2`O+R`H,BD-" D) M<F4H4F5C="!R+"!4<FEA;F=L92!T*2`O+R`H,RD-" D)>R`-" D)"7!R:6YT M971H;V1$96QE9V%T92 I.PT*"51E<W1E<B!T97-T97( /2!N97< 5&5S=&5R M97)S96-T:6]N4W%U87)E*3L-" EM;60 *ST ;F5W($1E;&5G871E,B$H9FQO M870L4F5C="Q4<FEA;F=L92DH)G1E<W1E<BY);G1E<G-E8W1I;VY3<75A<F4I A+&,I.PT*"6UM9"AC+&,I.PT*"6UM9"AR+'0I.PT*?0T* ` endAny facts against?Dynamic dispatch can be implemented by setting up an associative array that maps ClassInfo to a delegate, and writing a method that indexes the classinfo of its argument and calls. The trick, then, becomes working out some automatic (or nearly automatic) way of filling up that associative array. :)
Jul 21 2004
Alternate link: http://www.djsolaries.com/pragma/misc/multimethod.d
Jul 21 2004
Since D is not going to have implicit instantiation, I suggest that we*must*have the autotype keyword, which will be akin to the semantics proposedfor autoin C++ 0.x. That is to say, the type is deduced, at compile time - it's no variant type! - from the initialising expression, so:While searching for threads about implicit template instantiation I tripped over this: "Since D is not going to have implicit instantiation". Is that "not" as in "not ever" or as in "not in a long time"? -- Bent Rasmussen
Jul 28 2004