June 16, 2020
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.

June 16, 2020
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;
}




June 16, 2020
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?
June 17, 2020
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.


June 17, 2020
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? :)

June 17, 2020
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 ;)
June 17, 2020
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.
June 17, 2020
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.

June 17, 2020
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.
June 17, 2020
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.