May 12, 2021
On Tuesday, 11 May 2021 at 21:36:46 UTC, Andrei Alexandrescu wrote:
> Values are monomorphic. Years ago I found a bug in a large C++ system that went like this:
>
> class Widget : BaseWidget {
>     ...
>     Widget* clone() {
>         assert(typeid(this) == typeid(Widget*));
>         return new Widget(*this);
>     }
> };
>
> The assert was a _monomorphism test_, i.e. it made sure that the current object is actually a Widget and not something derived from it, who forgot to override clone() once again.

I don't understand what you mean by pointers being monomorphic.

this will always have the type of the class it was defined in. So the assert will always hold.

How is this surprising???

What is more dangerous is that if you forget to add a virtual member then *this will also always hold as being a Widget.

That is the result of C++ being a low-level language. No sensible high level language would allow such semantics.



May 12, 2021
On Wednesday, 12 May 2021 at 14:49:35 UTC, NonNull wrote:
> On Friday, 7 May 2021 at 20:53:08 UTC, Andrei Alexandrescu
>> Yah, ranges are a generalization of arrays. It would be odd if the generalization of arrays didn't work when tried with arrays.
>
> No. Ranges are not a generalization of arrays unless you ignore the most important feature of the notion of a Range. An array is a sequence of things in space: a spatial container (all values stored) that happens to be a sequence. A Range is a sequence of things in time. (Purist definition, often true in practice.)

Ranges are a generalization of arrays (or slices, if you prefer) in the same way that iterators are a generalization of pointers. In both cases, certain features of the specialized version are ignored or left out in the generalized version. As you've correctly pointed out, one of those ignored features is the array's layout in memory. A range *may* store all of its elements in memory, or it may not; as users of the range API, we are not suppose to know or care.
May 12, 2021
On Wednesday, 12 May 2021 at 13:39:50 UTC, Andrei Alexandrescu wrote:
> One matter is to distinguish what can be done from what D has already done and cannot change. For example, I tried some code just now and was... surprised.
>
> Meta mentioned that increment works with enums, and lo and behold it does:
>
> void main() {
>     import std;
>     enum X : int { x = 10, y = 20 }
>     X x;
>     writeln(x);
>     ++x;
>     writeln(x);
> }
>
> That prints "x" and then "cast(X)11". Meaning you can easily write a program that takes you outside enumerated values without a cast, which somewhat dilutes the value of "final switch" and the general notion that enumerated types are a small closed set. Arguably ++ should not be allowed on enumerated values.
>
> Surprises go on:
>
> void main() {
>     import std;
>     enum X : string { x = "Hello, world!", y = "xyz" }
>     X x;
>     writeln(x);
>     x = x[1 .. $];
>     writeln(x);
> }
>
> That prints:
>
> x
> cast(X)ello, world!
>
> which showcases, as a little distraction, a bug in the formatting of enums - the string should be quoted properly.
>
> But the larger point is that enum types derived from string actually allow, again, stepping outside their universe with ease.
>

I've raised these problem on a regular basis for years now.

This is obviously another instance where things are unsound, and needs to be fixed.

Last time I we had a discussion on the matter, it went in a loop that is best summarized as this:
enum E : int { A, B, C }

while (true) {
  Me: A | B ought to be an int, not an E.
  W&A: But you need it to be an enum, so that you can do things like combining flags and stay. As in:
    enum Mode { Read, Write }
    openFile(file, Mode.Read | Mode.Write);
  Me: Wl then, you can't have final switch, because you don't have the guarantee it rely on.
  W&A: final switch is very much needed, from X, Y Z reason.
}

This is extremely tiresome and kinda looks like the current discussion (or another one would be the in contract needing to be statically bound, where Timon and Myself had to fish for Bertrand Meyer because nothing short of an argument from authority could do the trick).

So if we get nothing else out of that discussion, fixing enum so that they don't go out of the allowed set of value would be nice. It's just unfortunate that it takes literally 5 years+ to get to a point where this is even acknowledged as being an issue.

I hope we can somehow shorten that process, because it's not workable as it is. You have people around like Timon and myself who have an eye for this. It's free brainpower you are leaving not leveraging.
May 12, 2021
On Wednesday, 12 May 2021 at 14:35:27 UTC, 12345swordy wrote:
> Replacing the item in the box with the different yet exact same item, doesn't mean that you didn't modify the box. Again, print the object memory address, and you will see what I am talking about.
>
> -Alex

I legitimately can't tell if you are an idiot or a troll.
May 12, 2021
On Wednesday, 12 May 2021 at 14:52:42 UTC, Ola Fosheim Grøstad wrote:
> On Tuesday, 11 May 2021 at 21:36:46 UTC, Andrei Alexandrescu wrote:
>> Values are monomorphic. Years ago I found a bug in a large C++ system that went like this:
>>
>> class Widget : BaseWidget {
>>     ...
>>     Widget* clone() {
>>         assert(typeid(this) == typeid(Widget*));
>>         return new Widget(*this);
>>     }
>> };
>>
>> The assert was a _monomorphism test_, i.e. it made sure that the current object is actually a Widget and not something derived from it, who forgot to override clone() once again.
>
> I don't understand what you mean by pointers being monomorphic.
>

Ok, consider the following.

class A {};
class B: public A {};

A *a = new B();

tyepid(a) is A*. In C++, a is monomorphic.
typeid(*a) is B. In C++, *a is polymorphic.


May 12, 2021
On Wednesday, 12 May 2021 at 15:31:29 UTC, deadalnix wrote:
> On Wednesday, 12 May 2021 at 14:35:27 UTC, 12345swordy wrote:
>> Replacing the item in the box with the different yet exact same item, doesn't mean that you didn't modify the box. Again, print the object memory address, and you will see what I am talking about.
>>
>> -Alex
>
> I legitimately can't tell if you are an idiot or a troll.

What kind of idiot that ignores official documentation provided by Microsoft that clearly states that classes are reference types not value types!? Your coding examples does NOT DISPROVE THIS NOTATION WHATSOEVER!!!!

-Alex
May 12, 2021
On Wednesday, 12 May 2021 at 15:08:46 UTC, Paul Backus wrote:
> On Wednesday, 12 May 2021 at 14:49:35 UTC, NonNull wrote:
>> No. Ranges are not a generalization of arrays unless you ignore the most important feature of the notion of a Range. An array is a sequence of things in space: a spatial container (all values stored) that happens to be a sequence. A Range is a sequence of things in time. (Purist definition, often true in practice.)
>
> Ranges are a generalization of arrays (or slices, if you prefer) in the same way that iterators are a generalization of pointers. In both cases, certain features of the specialized version are ignored or left out in the generalized version. As you've correctly pointed out, one of those ignored features is the array's layout in memory. A range *may* store all of its elements in memory, or it may not; as users of the range API, we are not suppose to know or care.

This is the standard pattern of the interpretation of the meaning of Range. It is more concrete. I want the idea of range to escape its historical semantic origins.

I am suggesting a different and cleaner interpretation of that meaning. One that draws a deliberate line between space and time as a means of motivating language design.

Instead of regarding the psychological process of regarding a spatial data structure as a range as being the psychological process of simply ignoring other non-range features and just using range operations, I am suggesting a semantic hard line be drawn between the two. A range could be obtained by exploding a spatial data structure (array say) and regarded as a distinct entity. Concretely the latent temporal sequence of things taken from the spatial data structure (the derived range) could be regarded as semantically quite different and separate from that data structure.

While some may consider this a distinction without a difference, it does nevertheless change how one might relate a range to a spatial data structure in a programming language.

My view leads to an explicit explode operation of some kind on all occasions, whereas yours can munge together range stuff with other operations on spatial data structures, so that your spatial structure IS a range and abstraction is avoided. Moving away from the historical semantics to the semantics I suggest above and having that guide language design separates those concerns.

The idea of /explode/ is a nice intuitive fundamental concept that is concealed and entangled in D right now. Things could be less baroque. Specifically, arrays would then be treated the same way as any other spatial data structure. They would not be ranges.


May 12, 2021
On Wednesday, 12 May 2021 at 15:35:26 UTC, deadalnix wrote:
> Ok, consider the following.
>
> class A {};
> class B: public A {};
>
> A *a = new B();
>
> tyepid(a) is A*. In C++, a is monomorphic.
> typeid(*a) is B. In C++, *a is polymorphic.

Sadly, IIRC typeid(*a) is A, because A does not contain a virtual member...

typeid(a) is A*, because that is the type of the pointer. However, the relationship between B* and A* is polymorphic, because you can use B* in the context where you expect A*? E.g. you can call a function that expects paramater A* with a pointer B*. So that makes the relationship polymorphic?

I have to admit I never use the terminology monomorphic and polymorphic, so my understanding could be wrong. If so, I am probably not alone in the thread, so for the sake of other readers, maybe someone can provide a definition for monomorphic?


May 12, 2021
On Wednesday, 12 May 2021 at 16:38:10 UTC, Ola Fosheim Grøstad wrote:
> typeid(a) is A*, because that is the type of the pointer. However, the relationship between B* and A* is polymorphic, because you can use B* in the context where you expect A*? E.g. you can call a function that expects paramater A* with a pointer B*. So that makes the relationship polymorphic?


To be more precise. B* is a subtype of A* if you can use B* in contexts where A* is expected, which is polymorphic in nature.

More interestingly, pure OO-languages like Beta provide type-variables. C++/D lack those. So in such languages you can bind new types to type-variables and therefore change the typing of elements of arrays and such in subclasses.

(Which leads to other challenges, all languages seem to have some kind of challenge associated with them once they allow polymorphisms)

May 12, 2021
On Wednesday, 12 May 2021 at 16:38:10 UTC, Ola Fosheim Grøstad wrote:
> I have to admit I never use the terminology monomorphic and polymorphic, so my understanding could be wrong. If so, I am probably not alone in the thread, so for the sake of other readers, maybe someone can provide a definition for monomorphic?

It's quite simple.

*a is polymorphic, because it it an object of type A as far as the user of *a is concerned, but it is actually an object of type B (or any other subtype of a).

a itself isn't polymorphic, because it is a pointer to an A no matter what. It is not a pointer to a B that is observed as if it was a pointer to an A. There is nothing more in it to be discovered at run time, it's just a pointer.

Even if you do

B *b = ...;
A *a = b;

Then you have not an instance of polymorphism, simply that you had a pointer to a B, and now you also have a pointer to an A.