December 06
On Thursday, 5 December 2024 at 21:58:14 UTC, Richard (Rikki) Andrew Cattermole wrote:
> If you are forcing users of a parent class to add the call explicitly into the parent, its a major pain that other languages simply do not have. Not to mention easily forgotten.
>
> This is what my attribute that I said I'm thinking about ``@reinterptAsChild`` would solve.

I'm confused for example. What is the problem you're mentioning here?

If it is inheritance, then you either predefine the inheritance structure through annotations just like Jakson lib in java https://www.javadoc.io/doc/com.fasterxml.jackson.core/jackson-annotations/latest/com/fasterxml/jackson/annotation/JsonTypeInfo.html, or use runtime reflection to check what child it is and use respective serializer for it that is prerecorded.


December 06
On 06/12/2024 1:47 PM, Alexandru Ermicioi wrote:
> On Thursday, 5 December 2024 at 21:58:14 UTC, Richard (Rikki) Andrew Cattermole wrote:
>> If you are forcing users of a parent class to add the call explicitly into the parent, its a major pain that other languages simply do not have. Not to mention easily forgotten.
>>
>> This is what my attribute that I said I'm thinking about ``@reinterptAsChild`` would solve.
> 
> I'm confused for example. What is the problem you're mentioning here?
> 
> If it is inheritance, then you either predefine the inheritance structure through annotations just like Jakson lib in java https:// www.javadoc.io/doc/com.fasterxml.jackson.core/jackson-annotations/ latest/com/fasterxml/jackson/annotation/JsonTypeInfo.html, or use runtime reflection to check what child it is and use respective serializer for it that is prerecorded.

We do not have runtime reflection. We cannot copy languages that do, including for your example that isn't an alternative to runtime reflection.

```d
interface ISerialize {
	void serialize();
}

class SerializableRoot {
	void serialize() {
		// serialize here
	}
}

class MyType : SerializableRoot {
	@thing int field;

	...
}
```

Some how SerializableRoot.serialize has to be able to see the child MyType class. We have no good way of handling that currently.

The attribute allows the serialize method to think that its in MyType instead.

December 06
On Friday, 6 December 2024 at 01:18:03 UTC, Richard (Rikki) Andrew Cattermole wrote:
> Some how SerializableRoot.serialize has to be able to see the child MyType class. We have no good way of handling that currently.

There is the curious recurring template pattern of course.
December 06
On 6/12/24 2:18, Richard (Rikki) Andrew Cattermole wrote:
> We do not have runtime reflection. We cannot copy languages that do, including for your example that isn't an alternative to runtime reflection.
> 
> ```d
> interface ISerialize {
>      void serialize();
> }
> 
> class SerializableRoot {
>      void serialize() {
>          // serialize here
>      }
> }
> 
> class MyType : SerializableRoot {
>      @thing int field;
> 
>      ...
> }
> ```
> 
> Some how SerializableRoot.serialize has to be able to see the child MyType class. We have no good way of handling that currently.

```d
import std;

interface ISerialize {
    void serialize();
}

struct thing{ }

class SerializableRoot {
    void serialize(this T)() {
        // serialize here, possibly using __traits to introspect on T
        writeln("Serializing ", typeid(T));
    }
}

class MyType : SerializableRoot {
    @thing int field;
}

void main() {
    MyType c = new MyType;
    c.serialize;
}
```


December 06
On 06/12/2024 8:59 PM, Arafel wrote:
> On 6/12/24 2:18, Richard (Rikki) Andrew Cattermole wrote:
>> We do not have runtime reflection. We cannot copy languages that do, including for your example that isn't an alternative to runtime reflection.
>>
>> ```d
>> interface ISerialize {
>>      void serialize();
>> }
>>
>> class SerializableRoot {
>>      void serialize() {
>>          // serialize here
>>      }
>> }
>>
>> class MyType : SerializableRoot {
>>      @thing int field;
>>
>>      ...
>> }
>> ```
>>
>> Some how SerializableRoot.serialize has to be able to see the child MyType class. We have no good way of handling that currently.
> 
> ```d
> import std;
> 
> interface ISerialize {
>      void serialize();
> }
> 
> struct thing{ }
> 
> class SerializableRoot {
>      void serialize(this T)() {
>          // serialize here, possibly using __traits to introspect on T
>          writeln("Serializing ", typeid(T));
>      }
> }
> 
> class MyType : SerializableRoot {
>      @thing int field;
> }
> 
> void main() {
>      MyType c = new MyType;
>      c.serialize;
> }
> ```

I'm fully aware of template this parameters, I was trying to get them to work 10 years ago.

They have problems that prevent their usage.

1. They are templates, so not in vtable
2. They require the this pointer to be typed to the child (extremely easy for this to not be the case, see 1)
3. Because they are not virtual, you cannot up cast (see 2)
4. Cannot be over ridden for any manual behavior

All and all, they become very complex for common usage, quite fragile if you don't use them right, not the kind of thing you recommend to people who are use to runtime reflection.

December 06
On 6/12/24 9:05, Richard (Rikki) Andrew Cattermole wrote:
> I'm fully aware of template this parameters, I was trying to get them to work 10 years ago.
> 
> They have problems that prevent their usage.
> 
> 1. They are templates, so not in vtable
> 2. They require the this pointer to be typed to the child (extremely easy for this to not be the case, see 1)
> 3. Because they are not virtual, you cannot up cast (see 2)
> 4. Cannot be over ridden for any manual behavior
> 
> All and all, they become very complex for common usage, quite fragile if you don't use them right, not the kind of thing you recommend to people who are use to runtime reflection.

That's true, I found a workaround using constructors and registering classes there, but currently it only works with one level of inheritance (without user intervention, the whole purpose of it):

```d
import std;

class SerializationRoot {
    static void function()[TypeInfo_Class] serializationMethods;
    // We would need a `shared static` constructor that would be always called
    /* shared static */ this(this T)() {
        auto myType = typeid(T);
        if (myType !in serializationMethods) {
            writeln("Registering type ", myType);
            serializationMethods[myType] = (&T.serializeDefaultImpl!T).funcptr;
        }
    }
    void serialize() {
        auto myType = typeid(this);
        assert(myType in serializationMethods, "The class was not registered for default serialization.");
        void delegate() dlg;
        dlg.funcptr = serializationMethods[myType];
        dlg.ptr = cast (void *) this;
        dlg();
        stdout.flush;
    }
    void serializeDefaultImpl(T)() {
        T realThis = cast(T) this;
        writeln("Default serialization for ", typeid(realThis));
    }
}

class MyClass : SerializationRoot { }

class MyOtherClass : SerializationRoot {
    this(int) { }
    override void serialize() {
        writeln("This class does something special, but still uses the default mechanism.");
        super.serialize;
    }
}

class MySpecialClass : SerializationRoot {
    override void serialize() {
        writeln("This class disregards everything takes everything into its own hands.");
    }
}

class MyProblematicClass : MyClass { }

void main() {
    SerializationRoot myClass = new MyClass;
    SerializationRoot myOtherClass = new MyOtherClass(1);
    SerializationRoot mySpecialClass = new MySpecialClass();
    SerializationRoot myProblematicClass = new MyProblematicClass();
    myClass.serialize;
    myOtherClass.serialize;
    mySpecialClass.serialize;
    myProblematicClass.serialize; // BOOM!
}
```

But it should work as expect if we had templated-this (shared) static constructors. My reading is that they are actually not forbidden in the spec ("member functions" should include static ones too) [1], and there is already an open bug for that [2] (with some duplicates).

Unfortunately I don't think it'll happen anytime soon, and I don't have the skills to have a go at it myself.

[1]: https://dlang.org/spec/template.html#template_this_parameter
[2]: https://issues.dlang.org/show_bug.cgi?id=10488

December 06
On 06/12/2024 10:46 PM, Arafel wrote:
> On 6/12/24 9:05, Richard (Rikki) Andrew Cattermole wrote:
>> I'm fully aware of template this parameters, I was trying to get them to work 10 years ago.
>>
>> They have problems that prevent their usage.
>>
>> 1. They are templates, so not in vtable
>> 2. They require the this pointer to be typed to the child (extremely easy for this to not be the case, see 1)
>> 3. Because they are not virtual, you cannot up cast (see 2)
>> 4. Cannot be over ridden for any manual behavior
>>
>> All and all, they become very complex for common usage, quite fragile if you don't use them right, not the kind of thing you recommend to people who are use to runtime reflection.
> 
> That's true, I found a workaround using constructors and registering classes there, but currently it only works with one level of inheritance (without user intervention, the whole purpose of it):

Yes, once you have a level in the hierarchy that adds a constructor, you basically lose the ability to call that constructor with the template this parameter.

Which means the user derived type has to be final.

A struct starts to look inviting with all these issues present in classes lol

December 06
On 6/12/24 11:18, Richard (Rikki) Andrew Cattermole wrote:
> Yes, once you have a level in the hierarchy that adds a constructor, you basically lose the ability to call that constructor with the template this parameter.

Shared static constructors with a template this parameter would be the solution (just promoting them in case somebody feels like implementing them ;-) )
December 06
On 06/12/2024 11:38 PM, Arafel wrote:
> On 6/12/24 11:18, Richard (Rikki) Andrew Cattermole wrote:
>> Yes, once you have a level in the hierarchy that adds a constructor, you basically lose the ability to call that constructor with the template this parameter.
> 
> Shared static constructors with a template this parameter would be the solution (just promoting them in case somebody feels like implementing them ;-) )

For deserialization sure.

For serialization they cannot be overridden.

In both cases you have to use a single global registration (which may not be a down side).

So another case of it works for somethings, but not others.

December 11
On 12/3/2024 6:51 PM, Timon Gehr wrote:
> I dislike function-local import semantics quite a bit as they do not follow the well-thought-out overloading rules that apply to other imports.

I don't know if anyone remembers, but there was a huge debate about this in the early days. I had implemented local import rules just like all the other rules, but everyone else deemed that unintuitive, and so we now have a difficult to explain mechanism that is intuitive.