digitalmars.D - Why D needs implicit instantion or an autotype keyword
- "Matthew" <admin stlsoft.dot.dot.dot.dot.org> Jul 18 2004
- "Ivan Senji" <ivan.senji public.srce.hr> Jul 18 2004
- Matthias Becker <Matthias_member pathlink.com> Jul 18 2004
- "Matthew Wilson" <admin.hat stlsoft.dot.org> Jul 18 2004
- "Bent Rasmussen" <exo bent-rasmussen.info> Jul 18 2004
- Norbert Nemec <Norbert.Nemec gmx.de> Jul 18 2004
- "Bent Rasmussen" <exo bent-rasmussen.info> Jul 18 2004
- Norbert Nemec <Norbert.Nemec gmx.de> Jul 19 2004
- "Bent Rasmussen" <exo bent-rasmussen.info> Jul 20 2004
- Andy Friesen <andy ikagames.com> Jul 20 2004
- Andy Friesen <andy ikagames.com> Jul 18 2004
- "Carlos Santander B." <carlos8294 msn.com> Jul 18 2004
- "Bent Rasmussen" <exo bent-rasmussen.info> Jul 18 2004
- "Carlos Santander B." <carlos8294 msn.com> Jul 18 2004
- Norbert Nemec <Norbert.Nemec gmx.de> Jul 19 2004
- Russ Lewis <spamhole-2001-07-16 deming-os.org> Jul 19 2004
- Andy Friesen <andy ikagames.com> Jul 19 2004
- Matthias Becker <Matthias_member pathlink.com> Jul 20 2004
- nail <nail_member pathlink.com> Jul 20 2004
- Sean Kelly <sean f4.ca> Jul 20 2004
- Matthias Becker <Matthias_member pathlink.com> Jul 21 2004
- Sean Kelly <sean f4.ca> Jul 21 2004
- Russ Lewis <spamhole-2001-07-16 deming-os.org> Jul 20 2004
- nail <nail_member pathlink.com> Jul 20 2004
- Matthias Becker <Matthias_member pathlink.com> Jul 21 2004
- nail <nail_member pathlink.com> Jul 21 2004
- Matthias Becker <Matthias_member pathlink.com> Jul 22 2004
- Sean Kelly <sean f4.ca> Jul 22 2004
- Matthias Becker <Matthias_member pathlink.com> Jul 23 2004
- Sha Chancellor <schancel pacific.net> Jul 23 2004
- Sean Kelly <sean f4.ca> Jul 23 2004
- Sha Chancellor <schancel pacific.net> Jul 23 2004
- Sean Kelly <sean f4.ca> Jul 23 2004
- Sha Chancellor <schancel pacific.net> Jul 23 2004
- Sean Kelly <sean f4.ca> Jul 23 2004
- Andy Friesen <andy ikagames.com> Jul 21 2004
- "Bent Rasmussen" <exo bent-rasmussen.info> Jul 28 2004
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 compile
pretty confident that it can be achieved soon without much hassles. This
works reasonably neatly because all <range> objects (those returned by transformations on containers or other <ranges>) support foreach. So far
There'll also be support for getting a specific item out of
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
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
List of Person container: List!(Person) results of select() (with delegate IsMan): MatchedNotionalRange!(_ListUtil!(Person).ListRange,
delegate(in Person value), Person)) Now that's just the one level. Imagine what it looks like when we're
another filter on top of the results of select()! In C++ one can obviate such things by writing working template functions.
C++ supports implicit instantiation, the worker function can deduce such horrendous mouthfuls for us. Of course, this is not always appropriate,
does help a lot. Since D is not going to have implicit instantiation, I suggest that we
have the autotype keyword, which will be akin to the semantics proposed
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
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
that if you know what type it should be, then you don't need autotype.
axiomatic, 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...Thoughts, everyone? Walter?!?
Well, I proposed this some time ago, got only good response, but Walter
answer. If you look at Walters style you can understand this. He declares all
on top of his functions, like Pascal coders do or C programmers before
Anyway, I don't like the name autotype. In C++0x auto is used so they
to introduce a new keyword. But auto is already used in D (well it is in
too, but with another meaning, that can't conflict in the situations,
need the new auto). Other languages use let, var or val for this. Why don't we use one of
words?
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, and
assume 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 in
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...
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:B.t.w: autotypes for local variables have proven rather convenient in
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...
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");
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.
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:B.t.w: autotypes for local variables have proven rather convenient in
Satheras 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");
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.
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; -- andy
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:It even looks (to my feeble brain, at least) easy to implement: autotype r = expr; // equivalent to: typeof(expr) r = expr; -- andy
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.
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!) -- andy
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.
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!)
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 Becker
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...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.
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. Sean
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: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?
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...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?
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...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?
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
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.
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.
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=gplain
Jul 23 2004
In article <cdpp68$1a61$1 digitaldaemon.com>, Sean Kelly <sean f4.ca> wrote: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.go ogle.com&output=gplain Sean
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.
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...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
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?
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...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
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.
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...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. :)
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
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
have the autotype keyword, which will be akin to the semantics proposed
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:
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









"Ivan Senji" <ivan.senji public.srce.hr> 