September 27, 2018
On Thursday, 27 September 2018 at 08:19:41 UTC, Jonathan M Davis wrote:
> On Thursday, September 27, 2018 1:41:23 AM MDT Chad Joan via Digitalmars-d- learn wrote:
>> On Thursday, 27 September 2018 at 05:12:06 UTC, Jonathan M Davis
>
>> This is also reminding me of how it's always bugged me that there isn't a way to operator overload opEquals with a static method (or even a free function?), given that it would allow the class/struct implementer to guard against (or even interact intelligently with) null values:
>
> That's not really an issue with D. With classes, when you have a == b, it
> doesn't lower to a.opEquals(b). Rather, it lowers to opEquals(a, b), and the
> free function opEquals is defined as
>
> bool opEquals(Object lhs, Object rhs)
> {
>     // If aliased to the same object or both null => equal
>     if (lhs is rhs) return true;
>
>     // If either is null => non-equal
>     if (lhs is null || rhs is null) return false;
>
>     // If same exact type => one call to method opEquals
>     if (typeid(lhs) is typeid(rhs) ||
>         !__ctfe && typeid(lhs).opEquals(typeid(rhs)))
>             /* CTFE doesn't like typeid much. 'is' works, but opEquals
> doesn't
>             (issue 7147). But CTFE also guarantees that equal TypeInfos are
>             always identical. So, no opEquals needed during CTFE. */
>     {
>         return lhs.opEquals(rhs);
>     }
>
>     // General case => symmetric calls to method opEquals
>     return lhs.opEquals(rhs) && rhs.opEquals(lhs);
> }
>
> /************************
> * Returns true if lhs and rhs are equal.
> */
> bool opEquals(const Object lhs, const Object rhs)
> {
>     // A hack for the moment.
>     return opEquals(cast()lhs, cast()rhs);
> }
>
> So, it already takes care of checking for null or even if the two references point to the same object. For structs, a == b, does lower to a.opEquals(b), but for better or worse, structs are designed so that their init values need to be valid, or you're going to have problems in general. Trying to work around that is fighting a losing battle.
>

The spec seems to have the homogeneous cases covered: classes with classes or structs with structs.  What I'm more worried about is stuff like when you have a class compared to a struct or builtin type, or maybe a struct compared to a builtin type (especially more complicated builtin types like arrays!).  The homogeneous cases are important for making a type consistent with itself, but the other cases are important for integrating a type with everything else in the ecosystem.

Notably, "alias this" is awesome and has more or less solved that for me in the pedestrian cases I tend to encounter.  I can write a struct and alias this to some reference variable that will be representative of my struct's "nullness" or other states of existence.

But I wouldn't be surprised if there are corner-cases I haven't encountered yet (actually I think I just remembered that this bit me a little bit once or twice) where having a single alias-this isn't sufficient to cover all of the possible things my struct/class could be compared to (ex: if the type's null-state corresponded to int.max for ints and float.nan for floats, and you can't just use opEquals, such as when the type is a class and could be precisely null).

>> Wouldn't it be helpful to have a root class type just to have a "Top" type at runtime, even if it had no members?  Ex: so you could do things like make an array ProtoObject[] foo; that can contain any runtime polymorphic variables.
>
> Maybe? It's not something that I've personally found to be particularly
> useful. Once you can templatize code, the need to have a common base class
> gets pretty hard to argue for, but I don't know that it's non-existent.
> Also, for better or worse, you can already get it with void* - and cover
> more types no less (albeit less @safely). But from what I understand of what
> Andrei is intending, ProtoObject will end up being the new root class for
> all D classos, so having ProtoObject[] would work for all extern(D) classes.
> Of course, that still potentially leaves exern(C++) classes and interfaces
> (which could be extern(C++) or COM even right now and aren't derived from
> Object). So, things are already a bit weird with classes when you start
> interacting with other languages through D. is(T : Object) and
> is(T == class) do _not_ mean quite the same thing even though you'd think
> that they would. And I haven't done enough with extern(C++) or COM in D to
> claim to understand all of the subtleties. If you're not messing with them
> directly or writing generic code that's going to mess with them, it doesn't
> really matter though.
>
> -Jonathan M Davis

Gotcha.  Quite a rabbit hole :)
September 27, 2018
On Wednesday, 26 September 2018 at 20:41:38 UTC, Chad Joan wrote:

> class Root(T)
> {
> 	T x;
> }
>
> class Extended(T) : Root!T
> {
> 	T y;
> }
>

Sorry for a technical aside, but would this be something for you?
https://forum.dlang.org/post/vtaxcxpufrovwfrkbyye@forum.dlang.org

I mean... In either case, there is something curious in the Extended/Root usage, as they both are bound to the same type. And if so, you could get rid of the templatization in the Extended class, either by templating Root on the Extended or use the T of Root in Extended.
September 27, 2018
On Thursday, September 27, 2018 2:44:25 AM MDT Chad Joan via Digitalmars-d- learn wrote:
> The spec seems to have the homogeneous cases covered: classes with classes or structs with structs.  What I'm more worried about is stuff like when you have a class compared to a struct or builtin type, or maybe a struct compared to a builtin type (especially more complicated builtin types like arrays!).  The homogeneous cases are important for making a type consistent with itself, but the other cases are important for integrating a type with everything else in the ecosystem.
>
> Notably, "alias this" is awesome and has more or less solved that for me in the pedestrian cases I tend to encounter.  I can write a struct and alias this to some reference variable that will be representative of my struct's "nullness" or other states of existence.
>
> But I wouldn't be surprised if there are corner-cases I haven't encountered yet (actually I think I just remembered that this bit me a little bit once or twice) where having a single alias-this isn't sufficient to cover all of the possible things my struct/class could be compared to (ex: if the type's null-state corresponded to int.max for ints and float.nan for floats, and you can't just use opEquals, such as when the type is a class and could be precisely null).

For two types to be compared, they must be the the same type - or they must be implicitly convertible to the same type, in which case, they're converted to that type and then compared. So, as far as D's design of comparison goes, there is no need worry about comparing differing types. At most, you need to worry about what implicit type conversions exist, and D isn't big on implicit type conversions, because they tend to cause subtle bugs. So, while they definitely affect comparison, they don't affect anywhere near as much as they would in a language like C++. In general, you're not going to get very far if you're trying to make it possible to compare a user-defined type against other types in D without explicitly converting it first.

- Jonathan M Davis



September 27, 2018
On Thursday, 27 September 2018 at 05:18:14 UTC, Chad Joan wrote:
> How does this work?
>
> The language reference states that typeid(Type) returns "an instance of class TypeInfo corresponding to Type".
> (https://dlang.org/spec/expression.html#typeid_expressions)
>
> But then the TypeInfo class doesn't seem to have a .create() method, or at least not one in the documentation:
> https://dlang.org/phobos/object.html#.TypeInfo


I forgot about the create method, lol, that is what you want.

But typeid(obj) - pass it an existing object of the type btw - when obj is a class will return TypeInfo_Class - a subclass of TypeInfo.

It is *that* which has the create method.
https://dlang.org/phobos/object.html#.TypeInfo_Class.create

(or less horrible docs
http://dpldocs.info/experimental-docs/object.TypeInfo_Class.html
)
September 27, 2018
On Thursday, 27 September 2018 at 05:04:09 UTC, Chad Joan wrote:
> The tree nodes are potentially very diverse, but the tree structure itself will be very homogeneous.

The virtual method can still handle that case!

But, if your child array of expressions is already accessible through the base interface, I think now your code is going to look something more like:

Expression copy(Expression e) {
   // I kinda hate the cast, but create is too generic lol
   // and we already know so it ok here
   Expression n = cast(Expression) typeid(e).create();

   foreach(child; e.children)
      n.children ~= copy(child);

   return n;
}

that's not too bad :)


> I'm having trouble looking this up.  Could you link me to the docs for this?

Well, the create method is actually better anyway (and actually documented! I just forgot about it in that first post) but you can see more of the guts here http://dpldocs.info/experimental-docs/object.TypeInfo_Class.html


> As above, I think this might be a very clean and effective solution for a different class of use-cases :)  I'll keep it in mind though.

Yeah. And I did make one mistake: the tupleof assignment trick wouldn't work well for references, so lol it isn't much of a deep copy. You'd want to deep copy any arrays too probably.

But sounds like you are already doing that, yay.
September 27, 2018
On 9/27/18 1:18 AM, Chad Joan wrote:
> On Wednesday, 26 September 2018 at 21:25:07 UTC, Steven Schveighoffer wrote:
>> ...
>> Object.factory is a really old poorly supported type of reflection. I would not depend on it for anything.
>>
> 
> Roger that.  Will avoid :)
> 
>> You are better off using your own registration system.
>>
>> As far as choosing the design for your problem, you can use:
>>
>> auto obj = typeid(obj).create();
>>
>> which is going to work better, and doesn't require a linear search through all modules/classes like Object.factory.
>>
> 
> How does this work?
> 
> The language reference states that typeid(Type) returns "an instance of class TypeInfo corresponding to Type".
> (https://dlang.org/spec/expression.html#typeid_expressions)
> 
> But then the TypeInfo class doesn't seem to have a .create() method, or at least not one in the documentation:
> https://dlang.org/phobos/object.html#.TypeInfo

typeid sometimes gives you a more derived type than TypeInfo. Including for classes and structs.

In the past, .classinfo gave you a different thing than typeid(obj), but now it is the same thing:


    auto obj = new Object;
    // classinfo and typeid are the same object
    assert(obj.classinfo is typeid(obj));
    // and the same type
    static assert(is(typeof(obj.classinfo) == typeof(typeid(obj))));

I wouldn't use classinfo any more, I generally use typeid.

-Steve
September 27, 2018
On 9/27/18 10:20 AM, Steven Schveighoffer wrote:
> typeid sometimes gives you a more derived type than TypeInfo. Including for classes and structs.
> 
> In the past, .classinfo gave you a different thing than typeid(obj), but now it is the same thing:
> 
> 
>      auto obj = new Object;
>      // classinfo and typeid are the same object
>      assert(obj.classinfo is typeid(obj));
>      // and the same type
>      static assert(is(typeof(obj.classinfo) == typeof(typeid(obj))));
> 
> I wouldn't use classinfo any more, I generally use typeid.

I should add that typeid does give you the derived TypeInfo_Class, not the concrete one.

that is:

Object obj; // = null

//typeid(obj); // segfault, can't dereference null pointer

class C {}

obj = new C;

static assert(is(typeof(obj) == Object));

writeln(typeid(obj)); // C, not Object

So it really is a drop-in replacement for classinfo. I think classinfo is still there to avoid breaking existing code that uses it.

-Steve
September 27, 2018
On Thursday, 27 September 2018 at 08:56:22 UTC, Alex wrote:
> On Wednesday, 26 September 2018 at 20:41:38 UTC, Chad Joan wrote:
>
>> class Root(T)
>> {
>> 	T x;
>> }
>>
>> class Extended(T) : Root!T
>> {
>> 	T y;
>> }
>>
>
> Sorry for a technical aside, but would this be something for you?
> https://forum.dlang.org/post/vtaxcxpufrovwfrkbyye@forum.dlang.org
>
> I mean... In either case, there is something curious in the Extended/Root usage, as they both are bound to the same type. And if so, you could get rid of the templatization in the Extended class, either by templating Root on the Extended or use the T of Root in Extended.

Perhaps it's half some form of unintended neural network fabrication that happened as I wrote the example and half that the original code isn't that well thought out yet.  The original code looks more like this:

template Nodes(T)
{
	class Root
	{
		T x;
	}

	class Extended : Root
	{
		T y;
	}
}

I will probably end up going with the latter suggestion and have Extended use the Root's T.  That would probably make sense for what I'm doing.  In my case, the T allows the caller to configure what kind of output the thing provides... IIRC (it's been a while since I touched this code o.O).

Thanks for pointing that out.
September 27, 2018
On Thursday, 27 September 2018 at 18:37:27 UTC, Chad Joan wrote:
>
> I will probably end up going with the latter suggestion and have Extended use the Root's T.  That would probably make sense for what I'm doing.  In my case, the T allows the caller to configure what kind of output the thing provides... IIRC (it's been a while since I touched this code o.O).
>
> Thanks for pointing that out.

Wait, actually the T is the element type of the input range, like "char" if the parser is to parse UTF8 strings.  I'd already figured that out too.  (*゚ー゚)ゞ
September 27, 2018
On Thursday, 27 September 2018 at 13:16:58 UTC, Adam D. Ruppe wrote:
> On Thursday, 27 September 2018 at 05:18:14 UTC, Chad Joan wrote:
>> How does this work?
>>
>> The language reference states that typeid(Type) returns "an instance of class TypeInfo corresponding to Type".
>> (https://dlang.org/spec/expression.html#typeid_expressions)
>>
>> But then the TypeInfo class doesn't seem to have a .create() method, or at least not one in the documentation:
>> https://dlang.org/phobos/object.html#.TypeInfo
>
>
> I forgot about the create method, lol, that is what you want.
>
> But typeid(obj) - pass it an existing object of the type btw - when obj is a class will return TypeInfo_Class - a subclass of TypeInfo.
>
> It is *that* which has the create method.
> https://dlang.org/phobos/object.html#.TypeInfo_Class.create
>
> (or less horrible docs
> http://dpldocs.info/experimental-docs/object.TypeInfo_Class.html
> )

Yep, that solves the mystery!  Thanks!