www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.announce - tardy v0.0.1 - Runtime polymorphism without inheritance

reply Atila Neves <atila.neves gmail.com> writes:
https://code.dlang.org/packages/tardy
https://github.com/atilaneves/tardy

Tardy lets users have their cake and eat it too by not making 
them have to use classes for runtime polymorphism. No inheritance 
anywhere to be found, which means structs, ints, and arrays can 
be used with dynamic dispatch (classes as well). It uses the GC 
by default but users can specify their own allocator type for 
flexibility, including a built-in "small buffer optimisation" 
allocator. Sample usage code:

import tardy;

interface ITransformer {
     int transform(int)  safe pure const;
}
alias Transformer = Polymorphic!ITransformer;

int xform(Transformer t) {
     return t.transform(3);
}

struct Adder {
     int i;
     int transform(int j)  safe pure const { return i + j; }
}

struct Plus1 {
     int transform(int i)  safe pure const { return i + 1; }
}

unittest {
     assert(xform(Transformer(Adder(2))) == 5);
     assert(xform(Transformer(Adder(3))) == 6);

     assert(xform(Transformer(Plus1())) == 4);
}
Jun 13
next sibling parent reply jmh530 <john.michael.hall gmail.com> writes:
On Saturday, 13 June 2020 at 15:11:49 UTC, Atila Neves wrote:
 https://code.dlang.org/packages/tardy
 https://github.com/atilaneves/tardy

 [snip]
This is pretty cool. Thanks for sharing. I have a few questions/comments: 1) It might make a good blog post at some point to discuss this and the performance (perhaps with reference to the earlier open methods blog post). 2) It looks like Polymorphic can only take one interface. Would you consider adding the ability to have more than one? I suppose a work-around would be to combine all the interfaces you would want to use into a new interface before combining. 3) Do arrays work, as in Transformer(Adder(2), Plus1()) or [Transformer(Adder(2)), Transformer(Plus1())]
Jun 13
parent reply Atila Neves <atila.neves gmail.com> writes:
On Saturday, 13 June 2020 at 16:15:49 UTC, jmh530 wrote:
 On Saturday, 13 June 2020 at 15:11:49 UTC, Atila Neves wrote:
 https://code.dlang.org/packages/tardy
 https://github.com/atilaneves/tardy

 [snip]
This is pretty cool. Thanks for sharing. I have a few questions/comments: 1) It might make a good blog post at some point to discuss this and the performance (perhaps with reference to the earlier open methods blog post).
I actually need to benchmark it though!
 2) It looks like Polymorphic can only take one interface. Would 
 you consider adding the ability to have more than one? I 
 suppose a work-around would be to combine all the interfaces 
 you would want to use into a new interface before combining.
I... completely forgot about that, thanks for bringing it up.
 3) Do arrays work, as in Transformer(Adder(2), Plus1()) or 
 [Transformer(Adder(2)), Transformer(Plus1())]
Yep: https://github.com/atilaneves/tardy/blob/d5f1102a6a791e77e0a27ee1a7920166fba8fcc8/tests/ut/polymorphic.d#L222
Jun 15
parent jmh530 <john.michael.hall gmail.com> writes:
On Monday, 15 June 2020 at 14:12:17 UTC, Atila Neves wrote:
 [snip]

 Yep:

 https://github.com/atilaneves/tardy/blob/d5f1102a6a791e77e0a27ee1a7920166fba8fcc8/tests/ut/polymorphic.d#L222
Thanks, I missed that.
Jun 15
prev sibling next sibling parent Sebastiaan Koppe <mail skoppe.eu> writes:
On Saturday, 13 June 2020 at 15:11:49 UTC, Atila Neves wrote:
 Tardy lets users have their cake and eat it too by not making 
 them have to use classes for runtime polymorphism.
This is one of those things that is so obvious in hindsight. Genius.
Jun 13
prev sibling next sibling parent reply Paul Backus <snarwin gmail.com> writes:
On Saturday, 13 June 2020 at 15:11:49 UTC, Atila Neves wrote:
 https://code.dlang.org/packages/tardy
 https://github.com/atilaneves/tardy
Cool stuff! What's the reasoning behind implementing your own vtables instead of using D's built-in object system? Don't want to be stuck inheriting from Object?
Jun 13
next sibling parent Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
On 6/13/20 2:39 PM, Paul Backus wrote:
 On Saturday, 13 June 2020 at 15:11:49 UTC, Atila Neves wrote:
 https://code.dlang.org/packages/tardy
 https://github.com/atilaneves/tardy
Cool stuff! What's the reasoning behind implementing your own vtables instead of using D's built-in object system? Don't want to be stuck inheriting from Object?
FWIW that could be avoided with extern(C++) classes.
Jun 13
prev sibling parent Atila Neves <atila.neves gmail.com> writes:
On Saturday, 13 June 2020 at 18:39:14 UTC, Paul Backus wrote:
 On Saturday, 13 June 2020 at 15:11:49 UTC, Atila Neves wrote:
 https://code.dlang.org/packages/tardy
 https://github.com/atilaneves/tardy
Cool stuff! What's the reasoning behind implementing your own vtables instead of using D's built-in object system? Don't want to be stuck inheriting from Object?
That's one reason, but even more importantly this gives the implementation more freedom and potentially more options for client code. I haven't added it yet (mostly due to laziness) but I considered compile-time policies to inline the vtable in the object, or to determine ordering of the vtable and the implementation (might have performance considerations), and a few other things. Also: more fun to implement, and shows that it can be done as a library.
Jun 15
prev sibling next sibling parent Dukc <ajieskola gmail.com> writes:
On Saturday, 13 June 2020 at 15:11:49 UTC, Atila Neves wrote:
 https://code.dlang.org/packages/tardy
 https://github.com/atilaneves/tardy
If this is what you say, it could be used for object-oriented programing with types that are not designed as objects. Not only that, in principle the design should work even at a bare-metal platform as I understand it. I know that practical corner cases would prevent doing the latter without extra work, but impressive nonetheless.
Jun 14
prev sibling next sibling parent reply 12345swordy <alexanderheistermann gmail.com> writes:
On Saturday, 13 June 2020 at 15:11:49 UTC, Atila Neves wrote:
 https://code.dlang.org/packages/tardy
 https://github.com/atilaneves/tardy

 [...]
Wouldn't a top type be a better way to achieve this? -Alex
Jun 15
next sibling parent reply Stefan Koch <uplink.coder googlemail.com> writes:
On Monday, 15 June 2020 at 20:47:16 UTC, 12345swordy wrote:
 On Saturday, 13 June 2020 at 15:11:49 UTC, Atila Neves wrote:
 https://code.dlang.org/packages/tardy
 https://github.com/atilaneves/tardy

 [...]
Wouldn't a top type be a better way to achieve this? -Alex
the Talias in type functions is a top type. (If I do understand correctly that it is something it's another word for an Any type.) it's a pain in the ass, to work with if you are not dynamically typed language.
Jun 15
parent reply 12345swordy <alexanderheistermann gmail.com> writes:
On Monday, 15 June 2020 at 20:51:38 UTC, Stefan Koch wrote:
 On Monday, 15 June 2020 at 20:47:16 UTC, 12345swordy wrote:
 On Saturday, 13 June 2020 at 15:11:49 UTC, Atila Neves wrote:
 https://code.dlang.org/packages/tardy
 https://github.com/atilaneves/tardy

 [...]
Wouldn't a top type be a better way to achieve this? -Alex
the Talias in type functions is a top type. (If I do understand correctly that it is something it's another word for an Any type.) it's a pain in the ass, to work with if you are not dynamically typed language.
Speaking of type functions, have you been working on a dip for that? I am quite curious to see what it looks like currently. -Alex
Jun 15
parent Stefan Koch <uplink.coder googlemail.com> writes:
On Monday, 15 June 2020 at 20:54:27 UTC, 12345swordy wrote:
 On Monday, 15 June 2020 at 20:51:38 UTC, Stefan Koch wrote:
 On Monday, 15 June 2020 at 20:47:16 UTC, 12345swordy wrote:
 On Saturday, 13 June 2020 at 15:11:49 UTC, Atila Neves wrote:
 https://code.dlang.org/packages/tardy
 https://github.com/atilaneves/tardy

 [...]
Wouldn't a top type be a better way to achieve this? -Alex
the Talias in type functions is a top type. (If I do understand correctly that it is something it's another word for an Any type.) it's a pain in the ass, to work with if you are not dynamically typed language.
Speaking of type functions, have you been working on a dip for that? I am quite curious to see what it looks like currently. -Alex
Oh no, I haven't. And I am not really a visionary. I am trying to solve a few concrete problems. To language addition is a means to an end, for a DIP which is is supposed to consider the language at large, I would need help. Furthermore, I have to see how the implementation plays out. It would be unwise to spec something that I couldn't implement.
Jun 15
prev sibling parent Atila Neves <atila.neves gmail.com> writes:
On Monday, 15 June 2020 at 20:47:16 UTC, 12345swordy wrote:
 On Saturday, 13 June 2020 at 15:11:49 UTC, Atila Neves wrote:
 https://code.dlang.org/packages/tardy
 https://github.com/atilaneves/tardy

 [...]
Wouldn't a top type be a better way to achieve this? -Alex
How?
Jun 16
prev sibling next sibling parent reply Petar Kirov [ZombineDev] <petar.p.kirov gmail.com> writes:
On Saturday, 13 June 2020 at 15:11:49 UTC, Atila Neves wrote:
 https://code.dlang.org/packages/tardy
 https://github.com/atilaneves/tardy
Looks interesting, nice work! How does it compare to: https://dlang.org/phobos/std_experimental_typecons#.wrap ? In the more longer-term, is the goal of the project to implement a Typescript / Go interfaces like structural type system in user space? Also how would it compare to Rust traits? I guess the main difference, would be that Rust enforces a nominal type system like approach, where 2 differently named traits that otherwise define the same interface are not considered interchangeable.
Jun 15
parent reply Atila Neves <atila.neves gmail.com> writes:
On Tuesday, 16 June 2020 at 03:56:52 UTC, Petar Kirov 
[ZombineDev] wrote:
 On Saturday, 13 June 2020 at 15:11:49 UTC, Atila Neves wrote:
 https://code.dlang.org/packages/tardy
 https://github.com/atilaneves/tardy
Looks interesting, nice work! How does it compare to: https://dlang.org/phobos/std_experimental_typecons#.wrap ?
For starters, that uses a class and inheritance internally and therefore has all the drawbacks of that approach as laid out in tardy's README.md. Then there's the lack of allocator support.
 In the more longer-term, is the goal of the project to 
 implement a Typescript / Go interfaces like structural type 
 system in user space?
Yes. Other than allowing multiple interfaces, I think it's already implemented.
 Also how would it compare to Rust traits?
Rust's traits are usually used like D's template contraints and Haskell's type classes. The only way they're relevant here are trait objects: https://doc.rust-lang.org/reference/types/trait-object.html The main difference is that tardy is supposed to give the user choices over how the dispatch is actually implemented. Allocators alone are huge.
 I guess the main difference, would be that Rust enforces a 
 nominal type system like approach, where 2 differently named 
 traits that otherwise define the same interface are not 
 considered interchangeable.
Yes, that's also a difference.
Jun 16
next sibling parent Petar Kirov [ZombineDev] <petar.p.kirov gmail.com> writes:
On Tuesday, 16 June 2020 at 09:15:10 UTC, Atila Neves wrote:
 On Tuesday, 16 June 2020 at 03:56:52 UTC, Petar Kirov 
 [ZombineDev] wrote:
 On Saturday, 13 June 2020 at 15:11:49 UTC, Atila Neves wrote:
 https://code.dlang.org/packages/tardy
 https://github.com/atilaneves/tardy
Looks interesting, nice work! How does it compare to: https://dlang.org/phobos/std_experimental_typecons#.wrap ?
For starters, that uses a class and inheritance internally and therefore has all the drawbacks of that approach as laid out in tardy's README.md. Then there's the lack of allocator support.
 In the more longer-term, is the goal of the project to 
 implement a Typescript / Go interfaces like structural type 
 system in user space?
Yes. Other than allowing multiple interfaces, I think it's already implemented.
Cool!
 Also how would it compare to Rust traits?
Rust's traits are usually used like D's template contraints and Haskell's type classes. The only way they're relevant here are trait objects:
Yes I meant trait objects actually.
 https://doc.rust-lang.org/reference/types/trait-object.html

 The main difference is that tardy is supposed to give the user 
 choices over how the dispatch is actually implemented. 
 Allocators alone are huge.
Interesting!
 I guess the main difference, would be that Rust enforces a 
 nominal type system like approach, where 2 differently named 
 traits that otherwise define the same interface are not 
 considered interchangeable.
Yes, that's also a difference.
Jun 16
prev sibling parent reply jmh530 <john.michael.hall gmail.com> writes:
On Tuesday, 16 June 2020 at 09:15:10 UTC, Atila Neves wrote:
 [snip]
 In the more longer-term, is the goal of the project to 
 implement a Typescript / Go interfaces like structural type 
 system in user space?
Yes. Other than allowing multiple interfaces, I think it's already implemented.
I'm not familiar with what Typescript does, but doesn't Go allow interfaces to be implemented by free-standing functions? That is a little bit more similar to open methods. This requires the type inherit from the interface and implement member functions.
 Also how would it compare to Rust traits?
Rust's traits are usually used like D's template contraints and Haskell's type classes. The only way they're relevant here are trait objects: https://doc.rust-lang.org/reference/types/trait-object.html
[snip] Similar to above, aren't Rusts's trait objects defined using separate impl blocks, rather than as member functions. --- I'm not that knowledgeable of Boost, but I see some similarities with Boost's type_erasure library. However, one main difference is that it is implemented with concepts, rather than the equivalent of interfaces. I would guess using interfaces has some benefits in terms of implementation since you know exactly what functions need to be called. Something like models is very flexible, but that might be a downside.
Jun 16
parent reply Atila Neves <atila.neves gmail.com> writes:
On Tuesday, 16 June 2020 at 11:24:05 UTC, jmh530 wrote:
 On Tuesday, 16 June 2020 at 09:15:10 UTC, Atila Neves wrote:
 [snip]
 In the more longer-term, is the goal of the project to 
 implement a Typescript / Go interfaces like structural type 
 system in user space?
Yes. Other than allowing multiple interfaces, I think it's already implemented.
I'm not familiar with what Typescript does, but doesn't Go allow interfaces to be implemented by free-standing functions?
So does tardy.
 That is a little bit more similar to open methods. This 
 requires the type inherit from the interface and implement 
 member functions.
There is no inheritance anywhere, otherwise that'd defeat the point of the library in the first place. I used interfaces because they exist and intuively make sense, and support classes because why not. Otherwise it could be just structs and other values with candidate UFCS functions.
 I'm not that knowledgeable of Boost, but I see some 
 similarities with Boost's type_erasure library. However, one 
 main difference is that it is implemented with concepts, rather 
 than the equivalent of interfaces. I would guess using 
 interfaces has some benefits in terms of implementation since 
 you know exactly what functions need to be called. Something 
 like  models is very flexible, but that might be a downside.
Using an interface means using familiar syntax with the added benefits of being able to write a signature like this: int* fun(int*, int) safe nogc pure return scope const; And then have all of those attributes and `this` modifiers used in the vtable function pointer declarations. Overloading and UFCS get dealt with naturally by usual language rules.
Jun 16
parent reply jmh530 <john.michael.hall gmail.com> writes:
On Tuesday, 16 June 2020 at 11:31:14 UTC, Atila Neves wrote:
 On Tuesday, 16 June 2020 at 11:24:05 UTC, jmh530 wrote:
 On Tuesday, 16 June 2020 at 09:15:10 UTC, Atila Neves wrote:
 [snip]
 In the more longer-term, is the goal of the project to 
 implement a Typescript / Go interfaces like structural type 
 system in user space?
Yes. Other than allowing multiple interfaces, I think it's already implemented.
I'm not familiar with what Typescript does, but doesn't Go allow interfaces to be implemented by free-standing functions?
So does tardy.
Sorry, I had not realized that. I took Go's interface example and converted it to D. Would this work with tardy? ``` import tardy; interface IGeometry { double area() safe pure const; double perim() safe pure const; } alias Geometry = Polymorphic!IGeometry; struct Rect { double width, height; } struct Circle { double radius; } double area(Rect r) { return r.width * r.height } double perim(Rect r) { return 2 * r.width + 2 * r.height } double area(Circle c) { import std.math: PI; return PI * c.radius * c.radius } double perim(Circle c) { import std.math: PI; return 2 * PI * c.radius } void measure(IGeometry g) { import std.stdio: writeln; writeln(g); writeln(g.area); writeln(g.perim); } void main() { auto r = Rect(3.0, 4.0); auto c = Circle(5.0); r.Geometry.measure; c.Geometry.measure; } ```
 That is a little bit more similar to open methods. This 
 requires the type inherit from the interface and implement 
 member functions.
There is no inheritance anywhere, otherwise that'd defeat the point of the library in the first place. I used interfaces because they exist and intuively make sense, and support classes because why not. Otherwise it could be just structs and other values with candidate UFCS functions.
Sorry, that was me being hasty. I was just concerned about the member functions part of it, which you said above is not a concern.
Jun 16
next sibling parent jmh530 <john.michael.hall gmail.com> writes:
On Tuesday, 16 June 2020 at 12:30:24 UTC, jmh530 wrote:
 [snip]
 
 double area(Rect r) {
     return r.width * r.height
 }

 double perim(Rect r) {
     return  2 * r.width + 2 * r.height
 }

 double area(Circle c) {
     import std.math: PI;
     return PI * c.radius * c.radius
 }

 double perim(Circle c) {
     import std.math: PI;
     return 2 * PI * c.radius
 }
These should be safe pure.
Jun 16
prev sibling parent reply Atila Neves <atila.neves gmail.com> writes:
On Tuesday, 16 June 2020 at 12:30:24 UTC, jmh530 wrote:
 On Tuesday, 16 June 2020 at 11:31:14 UTC, Atila Neves wrote:
 On Tuesday, 16 June 2020 at 11:24:05 UTC, jmh530 wrote:
 On Tuesday, 16 June 2020 at 09:15:10 UTC, Atila Neves wrote:
 [snip]
 In the more longer-term, is the goal of the project to 
 implement a Typescript / Go interfaces like structural type 
 system in user space?
Yes. Other than allowing multiple interfaces, I think it's already implemented.
I'm not familiar with what Typescript does, but doesn't Go allow interfaces to be implemented by free-standing functions?
So does tardy.
Sorry, I had not realized that. I took Go's interface example and converted it to D. Would this work with tardy?
<snip> With a few changes, yes (added missing semicolons, changed IGeometry to Geometry in `measure`, passed the current module so tardy can find the UFCS functions, added ` safe pure` to the UFCS functions: import tardy; interface IGeometry { double area() safe pure const; double perim() safe pure const; } alias Geometry = Polymorphic!IGeometry; struct Rect { double width, height; } struct Circle { double radius; } double area(Rect r) safe pure { return r.width * r.height; } double perim(Rect r) safe pure { return 2 * r.width + 2 * r.height; } double area(Circle c) safe pure { import std.math: PI; return PI * c.radius * c.radius; } double perim(Circle c) safe pure { import std.math: PI; return 2 * PI * c.radius; } void measure(Geometry g) { import std.stdio: writeln; writeln(g); writeln(g.area); writeln(g.perim); } void main() { auto r = Rect(3.0, 4.0); auto c = Circle(5.0); Geometry.create!__MODULE__(r).measure; Geometry.create!__MODULE__(c).measure; }
Jun 16
next sibling parent reply jmh530 <john.michael.hall gmail.com> writes:
On Tuesday, 16 June 2020 at 13:31:49 UTC, Atila Neves wrote:
 [snip]
Pretty cool, thanks for the fixups. It may make for a good documentation example, in that it may help make clear that you need to pass the module in somehow when dealing with non-member functions (AFAICT). You could include a comment about how it would be different if it were split into separate files. I have a few more questions... In your original example, is there any issue if we then make transform a templated non-member function that is generic over types like Adder (maybe there is an enum hasAddableMember that is true for Adder and false for Plus1)? Am I right that the shared static constructor in vtable means that there is one vtable for all the instances? Are there any technical issues preventing Polymorphism from accepting a class in addition to an interface?
Jun 16
parent reply Atila Neves <atila.neves gmail.com> writes:
On Tuesday, 16 June 2020 at 15:50:07 UTC, jmh530 wrote:
 On Tuesday, 16 June 2020 at 13:31:49 UTC, Atila Neves wrote:
 [snip]
Pretty cool, thanks for the fixups. It may make for a good documentation example, in that it may help make clear that you need to pass the module in somehow when dealing with non-member functions (AFAICT). You could include a comment about how it would be different if it were split into separate files.
Good point.
 In your original example, is there any issue if we then make 
 transform a templated non-member function that is generic over 
 types like Adder (maybe there is an enum hasAddableMember that 
 is true for Adder and false for Plus1)?
I was going to say "it should work" but instead I wrote the code and... it does. It all falls out of how UFCS works and usual language rules. https://github.com/atilaneves/tardy/blob/feb26282608081098134c8846be87d398772ccb0/tests/ut/polymorphic.d#L102 https://github.com/atilaneves/tardy/blob/feb26282608081098134c8846be87d398772ccb0/tests/modules/ufcs/template_.d
 Am I right that the shared static constructor in vtable means 
 that there is one vtable for all the instances?
All instances of the same concrete type share the same vtable pointer, yes. Originally I built the vtable on the spot with `new` and assigned to each function pointer but then I realised that was a waste of time and allocations - the vtable is unique per concrete type.
 Are there any technical issues preventing Polymorphism from 
 accepting a class in addition to an interface?
None. It could even accept a struct, really.
Jun 17
parent reply jmh530 <john.michael.hall gmail.com> writes:
On Wednesday, 17 June 2020 at 10:04:59 UTC, Atila Neves wrote:
 [snip]

 I was going to say "it should work" but instead I wrote the 
 code and... it does. It all falls out of how UFCS works and 
 usual language rules.

 https://github.com/atilaneves/tardy/blob/feb26282608081098134c8846be87d398772ccb0/tests/ut/polymorphic.d#L102
 https://github.com/atilaneves/tardy/blob/feb26282608081098134c8846be87d398772ccb0/tests/modules/ufcs/template_.d
Cool.
 Am I right that the shared static constructor in vtable means 
 that there is one vtable for all the instances?
All instances of the same concrete type share the same vtable pointer, yes. Originally I built the vtable on the spot with `new` and assigned to each function pointer but then I realised that was a waste of time and allocations - the vtable is unique per concrete type.
 Are there any technical issues preventing Polymorphism from 
 accepting a class in addition to an interface?
None. It could even accept a struct, really.
If I'm understanding you correctly, you could modify Polymorphic (and a similar change to VirtualTable) to struct Polymorphic(Interface, InstanceAllocator = DefaultAllocator) if(is(Interface == interface) || is(Interface == class) || is(Interface == struct)) and the current functionality would still work. However, compared to normal inheritance, it would be missing the "base" class's member variables that don't exist in the "derived" one. You also couldn't call the "base" class member functions. Polymorphic is assuming the member functions are all implemented in the instance passed to it, correct? I suppose I was more asking if there were any issues preventing the ability to get these things to work. In other words, Polymorphism works with interfaces now and the behavior is like inheriting from an interface. Could it work for classes (and by implication structs), such that the behavior is like inheriting from classes, and not just treating the class like an interface. In the case of structs, which may be a little simpler to implement than classes, something as simple as below might be able to get this functionality working. static if (Interface == struct) { Interface base; alias base this; } so that member variables and functions missing from the instance can get forwarded to the alias this. Though this wouldn't handle super constructors.
Jun 17
parent reply Atila Neves <atila.neves gmail.com> writes:
On Wednesday, 17 June 2020 at 11:31:09 UTC, jmh530 wrote:
 On Wednesday, 17 June 2020 at 10:04:59 UTC, Atila Neves wrote:
[...]
Cool.
 [...]
If I'm understanding you correctly, you could modify Polymorphic (and a similar change to VirtualTable) to struct Polymorphic(Interface, InstanceAllocator = DefaultAllocator) if(is(Interface == interface) || is(Interface == class) || is(Interface == struct)) and the current functionality would still work. However, compared to normal inheritance, it would be missing the "base" class's member variables that don't exist in the "derived" one. You also couldn't call the "base" class member functions. Polymorphic is assuming the member functions are all implemented in the instance passed to it, correct?
I think these questions are good motivators for making it interface-only since then I don't have to check for data definitions.
Jun 17
parent jmh530 <john.michael.hall gmail.com> writes:
On Wednesday, 17 June 2020 at 11:46:12 UTC, Atila Neves wrote:
 [snip]

 I think these questions are good motivators for making it 
 interface-only since then I don't have to check for data 
 definitions.
Makes sense.
Jun 17
prev sibling parent reply Paul Backus <snarwin gmail.com> writes:
On Tuesday, 16 June 2020 at 13:31:49 UTC, Atila Neves wrote:
 <snip>

 With a few changes, yes (added missing semicolons, changed 
 IGeometry to Geometry in `measure`, passed the current module 
 so tardy can find the UFCS functions, added ` safe pure` to the 
 UFCS functions:
[...]
 void main() {
     auto r = Rect(3.0, 4.0);
     auto c = Circle(5.0);

     Geometry.create!__MODULE__(r).measure;
     Geometry.create!__MODULE__(c).measure;
 }
IMO this can be done more elegantly by separating out the code that looks up methods in the current module from the code that does the actual type erasure. A while ago, I collaborated briefly with Adam Kowalski from the Dlang discord server on some code to emulate C++-style argument-dependent lookup in D. Using that code, your example above would be written: Geometry.create(r.extended).measure; Geometry.create(c.extended).measure; Here's a gist with the code, along with a small example: https://gist.github.com/pbackus/0a70419eb8bece52f3a08edfe7b6019b If anyone thinks it's worthwhile, I can toss this up on Dub. Personally, I've never had much use for it in my own projects.
Jun 17
next sibling parent jmh530 <john.michael.hall gmail.com> writes:
On Wednesday, 17 June 2020 at 16:01:29 UTC, Paul Backus wrote:
 [snip]

 IMO this can be done more elegantly by separating out the code 
 that looks up methods in the current module from the code that 
 does the actual type erasure.

 A while ago, I collaborated briefly with Adam Kowalski from the 
 Dlang discord server on some code to emulate C++-style 
 argument-dependent lookup in D. Using that code, your example 
 above would be written:

     Geometry.create(r.extended).measure;
     Geometry.create(c.extended).measure;

 Here's a gist with the code, along with a small example:

 https://gist.github.com/pbackus/0a70419eb8bece52f3a08edfe7b6019b

 If anyone thinks it's worthwhile, I can toss this up on Dub. 
 Personally, I've never had much use for it in my own projects.
The syntax there looks nice (wouldn't hurt to make it even nicer!). Your moduleOf (and associated functions) looks like it shares similar functionality as some of what is in vtableImpl in poly.d (particularly importMixin).
Jun 17
prev sibling next sibling parent Sebastiaan Koppe <mail skoppe.eu> writes:
On Wednesday, 17 June 2020 at 16:01:29 UTC, Paul Backus wrote:
 A while ago, I collaborated briefly with Adam Kowalski from the 
 Dlang discord server on some code to emulate C++-style 
 argument-dependent lookup in D. Using that code, your example 
 above would be written:

     Geometry.create(r.extended).measure;
     Geometry.create(c.extended).measure;

 Here's a gist with the code, along with a small example:

 https://gist.github.com/pbackus/0a70419eb8bece52f3a08edfe7b6019b

 If anyone thinks it's worthwhile, I can toss this up on Dub. 
 Personally, I've never had much use for it in my own projects.
Yeah I can see a use for this. If have had to refactor things before because I was hitting this restriction.
Jun 17
prev sibling parent Atila Neves <atila.neves gmail.com> writes:
On Wednesday, 17 June 2020 at 16:01:29 UTC, Paul Backus wrote:
 On Tuesday, 16 June 2020 at 13:31:49 UTC, Atila Neves wrote:
 <snip>

 With a few changes, yes (added missing semicolons, changed 
 IGeometry to Geometry in `measure`, passed the current module 
 so tardy can find the UFCS functions, added ` safe pure` to 
 the UFCS functions:
[...]
 void main() {
     auto r = Rect(3.0, 4.0);
     auto c = Circle(5.0);

     Geometry.create!__MODULE__(r).measure;
     Geometry.create!__MODULE__(c).measure;
 }
IMO this can be done more elegantly by separating out the code that looks up methods in the current module from the code that does the actual type erasure. A while ago, I collaborated briefly with Adam Kowalski from the Dlang discord server on some code to emulate C++-style argument-dependent lookup in D. Using that code, your example above would be written: Geometry.create(r.extended).measure; Geometry.create(c.extended).measure;
Interesting. The issue I see here is you might not be able to control which module has the extension methods. I guess you could alias them in the module you want to use but that seems clumsy. In any case, this is trivial: template extended(string mod = __MODULE__) { auto extended(A...)(auto ref A args) { return create!mod(args); } }
Jun 19
prev sibling parent reply Stanislav Blinov <stanislav.blinov gmail.com> writes:
On Saturday, 13 June 2020 at 15:11:49 UTC, Atila Neves wrote:

 Tardy lets users have their cake and eat it too by not making 
 them have to use classes for runtime polymorphism.
I've got to ask though. Why "tardy"? Search engines be damned? :)
Jun 17
parent reply Atila Neves <atila.neves gmail.com> writes:
On Wednesday, 17 June 2020 at 10:43:35 UTC, Stanislav Blinov 
wrote:
 On Saturday, 13 June 2020 at 15:11:49 UTC, Atila Neves wrote:

 Tardy lets users have their cake and eat it too by not making 
 them have to use classes for runtime polymorphism.
I've got to ask though. Why "tardy"? Search engines be damned? :)
Late binding ;)
Jun 17
parent Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
On 6/17/20 7:19 AM, Atila Neves wrote:
 On Wednesday, 17 June 2020 at 10:43:35 UTC, Stanislav Blinov wrote:
 On Saturday, 13 June 2020 at 15:11:49 UTC, Atila Neves wrote:

 Tardy lets users have their cake and eat it too by not making them 
 have to use classes for runtime polymorphism.
I've got to ask though. Why "tardy"? Search engines be damned? :)
Late binding ;)
Love the name.
Jun 18