May 12, 2021
On Wednesday, 12 May 2021 at 11:41:20 UTC, Andrei Alexandrescu wrote:
> On 5/11/21 9:46 PM, deadalnix wrote:
>> This whole model in C++ is unsound. It's easy to show. In you above example, the this pointer, typed as Widget*, points to an instance of a subclass of Widget. If you were to assign a Widget to that pointer (which you can do, this is a pointer to a mutable widget), then any references to that widget using a subtype of Widget is now invalid.
>
> All of this is bizarrely incorrect. Care to elaborate?

Consider the following: https://godbolt.org/z/8vzx9W56a

This is a clear demonstration that C++'s type system is unsound here.

It is unsound because it has the property that you mentioned in your post: the pointer is monomorphic and the value this pointers points to is polymorphic. This is simply unsound, you cannot separate the two (unless you make everything immutable).

The pointer and the value must come together as a bundle, and that whole bundle (which is a value type containing a reference) is itself what is polymorphic. That's what Java/C#/D do and this is right.

May 12, 2021
On Wednesday, May 12, 2021 5:52:28 AM MDT Andrei Alexandrescu via Digitalmars- d wrote:
> On 5/11/21 10:36 PM, Meta wrote:
> > Last I checked, strings are reference types, in the same way that Java objects are reference types.
>
> Just by means of clarification, that's not true because the length is stored with the pointer. This occasionally trips folks starting with D.

To be more precise, a dynamic array in D is essentially

struct Array(T)
{
    size_t length;
    T* ptr;
}

So, the length is stored directly in the struct, and the data is referenced via the pointer stored in the struct. As such, we often refer to a D dynamic array as a pseudo-reference type. Either way, while the way it's put together has some very useful properties (like making it so that multiple dynamic arrays can be slices of the same data), there's no question that it can be confusing at first. And of course, that extends to strings, since D strings are dynamic arrays.

- Jonathan M Davis



May 12, 2021
On 5/12/21 6:36 AM, Jonathan M Davis wrote:
> Having isSomeString accept types that implicitly converted to string would
> be a disaster.

Sadly that's exactly what StringTypeOf does: https://run.dlang.io/is/8xqPKr

We should eliminate all uses of StringTypeOf from phobos.
May 12, 2021
On Wednesday, 12 May 2021 at 12:11:22 UTC, deadalnix wrote:
> This is a clear demonstration that C++'s type system is unsound here.

In fairness all generic low level programming languages that are practical to use have somewhat unsound type systems. Only high level languages can be fully sound (detect invalid programs at runtime and abort). C++ was forced into this mold by C though...

(You can have heavily constrained low level languages that are sound)

> It is unsound because it has the property that you mentioned in your post: the pointer is monomorphic and the value this

What does monomorphic mean in this context? Why would not this hold:

*Singleton <: *Anything

I find the discussion very confusing at this point.

May 12, 2021
On 5/11/21 10:39 PM, Timon Gehr wrote:
> Deadalnix is saying that there is a subtyping relationship for rvalues, while you are pointing out that there is no subtyping relationship for lvalues. I think those are both correct.

Well put. Rvalues can afford the luxury to change representation (e.g. from byte to int of float to double) because they're only used once. So a passable polymorphism scheme can be implemented via coercion.

> (Type theory has no notion of lvalues or rvalues, so those would indeed have to be interpreted as different types.)

Hmmm... haven't looked in a while, but don't some of Java formalizations account for int, double etc. being values and consequently rvalues when passed around? (Though they can't be passed by reference so a formalization could get away with assuming int is a reference, e.g. ++x means "rebind reference x to a new reference to the value x + 1").

> I fail to see why the semantics of lvalues should have any bearing on format strings even though I understand why most of Phobos might want to assume isSomeString talks about lvalues of the type.

It doesn't, the format string is just a symptom. The problem is that we change (already did, and massively... >100 instances of StringTypeOf) the standard library to accommodate what I think is an unproductive form of genericity.

>> There's no true subtyping or polymorphism with value semantics.
>> ...
> 
> There's certainly subtyping. The point about "polymorphism" (in type theory, polymorphism typically refers to parametric polymorphism, but I guess you mean existential types), is a bit more tricky. I guess the point is that a language that wants `f(σ) ⊆ ∃τ. f(τ)` to hold without any runtime semantics can't support data types whose values do not embed runtime type info. However, it can certainly support value types, even value types that are stored without indirections.

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.

This cramps my style somewhat because during the whole discussion I assume that doesn't work, or at least shouldn't. I guess an argument could be built that its semantics is what it is.

Anyway, the other side of the argument that got ignored is the alias this thing:

void main() {
    import std;
    static struct X {
        string fun();
        alias fun this;
    }
    X x;
    x = x[1 .. $];
}

This doesn't compile; the slice does, but the assignment doesn't. Which means there are differences in what would be expected of a string (or, as it turns out, an enum string) and what would be expected of a type that converts to string by means of alias this.
May 12, 2021
On Wednesday, 12 May 2021 at 13:35:11 UTC, Ola Fosheim Grøstad wrote:
> *Singleton <: *Anything

Typo :-D, I meant pointer-to-Singeltong is subtype of pointer-to-Anything:

Singleton* <: Anything*



May 12, 2021
On Wednesday, 12 May 2021 at 11:45:52 UTC, deadalnix wrote:
> On Wednesday, 12 May 2021 at 02:41:31 UTC, 12345swordy wrote:
>> On Wednesday, 12 May 2021 at 02:30:50 UTC, deadalnix wrote:
>>> On Wednesday, 12 May 2021 at 02:21:06 UTC, 12345swordy wrote:
>>>> Yes. A is being replace with the new instance of A that happens to have the same value here. There is no guarantee that they will share the same address.
>>>>
>>>> - Alex
>>>
>>> You might want to reconsider how sure of yourself you are.
>> The code you posted, do not support your claim what so ever. When I am talk about address I am literally talking about virtual memory address here, such as 0x40000 or something similar to that. You do not know what the actual virtual memory address of variable of 'a' for class 'b', as the GC takes it care of it for you.
>> So when A is being replace with the new instance of A that happens to have the same value that is being replace, the virtual memory that A holds from the function parameter currently holds will change.
>>
>> -Alex
>
> Before posting that email was the best time to run the code, look at the output and deduce what it means.
Like I said before, it does not support your claims, whatsoever. I wrote plenty of c# code before, and the code you posted does not support your claims whatsoever.

Classes are reference types not value types, end of discussion. Go to the official C# github and argue against the designers of the language if you don't believe me.


> In any case, I will disengage from that subthread with you, because it has reached its conclusion, and the point has been demonstrably made with actual code.
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



May 12, 2021
On Wednesday, 12 May 2021 at 02:39:23 UTC, Timon Gehr wrote:
>>          assert(typeid(*this) == typeid(Widget));
>> ...
>
>
> That's a C++ quirk. Not much to do with type theory. In fact, C++ may not be a great example for illustration, as its type system is unsound.

It isn't a quirk. To get dynamic lookup you need to add a virtual member.

class A {
public:
    virtual void nothing(){}
    void test(){
        std::cout << typeid(*this).name() << std::endl;
        std::cout << typeid(A).name() << std::endl;
    }
};
class B : public A {
};


void test_typeinfo(){
    B b{};
    b.test();
}

May 12, 2021
On 5/12/21 8:11 AM, deadalnix wrote:
> On Wednesday, 12 May 2021 at 11:41:20 UTC, Andrei Alexandrescu wrote:
>> On 5/11/21 9:46 PM, deadalnix wrote:
>>> This whole model in C++ is unsound. It's easy to show. In you above example, the this pointer, typed as Widget*, points to an instance of a subclass of Widget. If you were to assign a Widget to that pointer (which you can do, this is a pointer to a mutable widget), then any references to that widget using a subtype of Widget is now invalid.
>>
>> All of this is bizarrely incorrect. Care to elaborate?
> 
> Consider the following: https://godbolt.org/z/8vzx9W56a
> 
> This is a clear demonstration that C++'s type system is unsound here.
> 
> It is unsound because it has the property that you mentioned in your post: the pointer is monomorphic and the value this pointers points to is polymorphic. This is simply unsound, you cannot separate the two (unless you make everything immutable).
> 
> The pointer and the value must come together as a bundle, and that whole bundle (which is a value type containing a reference) is itself what is polymorphic. That's what Java/C#/D do and this is right.

Ah, now we're at slicing. Love these forum discussions!
May 12, 2021
On Friday, 7 May 2021 at 20:53:08 UTC, Andrei Alexandrescu wrote:
> On 5/7/21 2:44 PM, Steven Schveighoffer wrote:
>> On 5/7/21 2:17 PM, Adam D. Ruppe wrote:
>>> I think it was actually a mistake for Phobos to UFCS shoe-horn in range functions on arrays too - this includes strings as well as int[] and such as well.
>> 
>> The most common range BY FAR in all of D code is an array.
>> 
>> The end result of something like you allude to would result in nearly all of phobos NOT working with arrays.
>
> 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.)

A spatial container can be /exploded/ into a sequence in time. And a sequence in time can be /accreted/ into a spatial container (whether it has sequence or not).

Explode is a natural idea and could be defined for any spatial container, producing a Range from a spatial container, and specifically from an array.

Making a distinction of spatial and temporal makes sense.