May 11, 2021
On 5/11/21 3:43 PM, Jon Degenhardt wrote:
> 
>      enum { f4 = "%d" }
>      writefln!f4(4);
> 
>      enum : string { f5 = "%d" }
>      writefln!f5(5);
> 
>      enum X { f6 = "%d" }
>      writefln!(X.f6)(6);   // Compilation error
> 
>      enum Y : string { f7 = "%d" }
>      writefln!(Y.f7)(7);   // Compilation error

Thanks. I agree it's confusing. The mystery gets elucidated with some ease if we write the types involved: f4 and f5 have type string, f6 has type X, and f7 have type Y.

It's unpleasant that `enum : string { f5 = "%d" }` is really the same as `enum f5 = "%d"`. I expected that some anonymous enum type would be generated.
May 11, 2021
On 5/11/21 7:00 PM, Andrei Alexandrescu wrote:
> On 5/11/21 3:43 PM, Jon Degenhardt wrote:
>>
>>      enum { f4 = "%d" }
>>      writefln!f4(4);
>>
>>      enum : string { f5 = "%d" }
>>      writefln!f5(5);
>>
>>      enum X { f6 = "%d" }
>>      writefln!(X.f6)(6);   // Compilation error
>>
>>      enum Y : string { f7 = "%d" }
>>      writefln!(Y.f7)(7);   // Compilation error
> 
> Thanks. I agree it's confusing. The mystery gets elucidated with some ease if we write the types involved: f4 and f5 have type string, f6 has type X, and f7 have type Y.
> 
> It's unpleasant that `enum : string { f5 = "%d" }` is really the same as `enum f5 = "%d"`. I expected that some anonymous enum type would be generated.

Another unpleasant issue:

    enum Y : string { f7 = "%d" }
    writeln(typeof(Y.f7.representation).stringof);

prints immutable(ubyte)[], not immutable(char)[]. So not even Y.f7.representation is usable. Sigh.
May 11, 2021
On 5/11/2021 12:56 PM, deadalnix wrote:
> The fact is that you can't get rid of 1. and support OOP, because polymorphism is a key ingredient of OOP.

Converting a derived class reference to a base class reference is an "implicitly convert" operation, not a special-kind-of conversion.
May 11, 2021
On 5/11/2021 4:00 PM, Andrei Alexandrescu wrote:
> It's unpleasant that `enum : string { f5 = "%d" }` is really the same as `enum f5 = "%d"`. I expected that some anonymous enum type would be generated.

That came about due to the decision to overload enum to create manifest constants. This way, a block of manifest constants can be created.
May 11, 2021
On 5/11/2021 5:04 PM, Andrei Alexandrescu wrote:
> Another unpleasant issue:
> 
>      enum Y : string { f7 = "%d" }
>      writeln(typeof(Y.f7.representation).stringof);
> 
> prints immutable(ubyte)[], not immutable(char)[]. So not even Y.f7.representation is usable. Sigh.

The representation of a named enum is its base type.

The representation of a string type is immutable(ubyte)[].

It's consistent.
May 12, 2021
On 11.05.21 16:38, Paul Backus wrote:
> allocating from the global (!) heap, which arguably *should* be impure

I think this is confusing different levels of abstraction. What should be impure is accessing memory addresses as integers.
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.
>
> The problem was the code was doing exactly what it shouldn't have, yet the assert was puzzlingly passing. Since everyone here is great at teaching basic type theory, it's an obvious problem - the fix is:
>
>         assert(typeid(*this) == typeid(Widget));
>
> Then the assertion started failing as expected. Following that, I've used that example for years in teaching and to invariably there are eyes going wide when they hear that C++ pointers are monomorphic, it's the pointed-to values that are polymorphic, and that's an essential distinction. (In D, just like in Java, classes take care of that indirection automatically, which can get some confused.)

While this is indeed very interesting, this is missing the larger point.

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.

There is no such thing as a monomorphic pointer to a polymorphic type in any sound type system. That cannot be made to work. It is unsound. This is why in Java or C#, the type represent both the pointer and the pointed data, as a package, being half a value, half a reference type in the process. This is unavoidable, you can't unbundle it or everything breaks down.

So why is there an indirection in there? Simply because you cannot know the layout of the object at compile time when you are doing runtime polymorphism. But even then, you could decide to make it behave as a value type with eager deep copy or copy on write and that would work too, and it would still be polymorphic.

D is correct to use Java and C# 's model. C++'s is unsound.

But we get back to square one: this has nothing to do with the indirection. In fact, in the Java/C# model, class types are value type, which hold a reference to a payload. And the whole typing and subtyping business happen on these value types.
May 12, 2021
On Wednesday, 12 May 2021 at 00:22:56 UTC, Walter Bright wrote:
> On 5/11/2021 12:56 PM, deadalnix wrote:
>> The fact is that you can't get rid of 1. and support OOP, because polymorphism is a key ingredient of OOP.
>
> Converting a derived class reference to a base class reference is an "implicitly convert" operation, not a special-kind-of conversion.

That is trivially demonstrably false. Consider:

class A {}
class B : A {}

B function() implicitly converts to A function()

But

byte function() doesn't implicitly converts to int function()

Clear, the implicit conversion from byte to int is of different nature than the one from B to A, and one doesn't have to dig very deep to find these differences.

Now, mind you, this is not a problem. At all. After all, B is a subtype of A, while byte is not a subtype of int. There are different kind of implicit conversions. This is pefectly sound and required if D wants to have implicit conversion of things which aren't subtypes of each others. There are no ways around it.

Let's just not pretend it's the same, because this from these erroneous assumptions that bad design grows.

May 12, 2021
On Wednesday, 12 May 2021 at 01:46:25 UTC, deadalnix 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.
>>
>> The problem was the code was doing exactly what it shouldn't have, yet the assert was puzzlingly passing. Since everyone here is great at teaching basic type theory, it's an obvious problem - the fix is:
>>
>>         assert(typeid(*this) == typeid(Widget));
>>
>> Then the assertion started failing as expected. Following that, I've used that example for years in teaching and to invariably there are eyes going wide when they hear that C++ pointers are monomorphic, it's the pointed-to values that are polymorphic, and that's an essential distinction. (In D, just like in Java, classes take care of that indirection automatically, which can get some confused.)
>
> While this is indeed very interesting, this is missing the larger point.
>
> 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.
>
> There is no such thing as a monomorphic pointer to a polymorphic type in any sound type system. That cannot be made to work. It is unsound. This is why in Java or C#, the type represent both the pointer and the pointed data, as a package, being half a value, half a reference type in the process. This is unavoidable, you can't unbundle it or everything breaks down.
>
> So why is there an indirection in there? Simply because you cannot know the layout of the object at compile time when you are doing runtime polymorphism. But even then, you could decide to make it behave as a value type with eager deep copy or copy on write and that would work too, and it would still be polymorphic.
>
> D is correct to use Java and C# 's model. C++'s is unsound.
>
> But we get back to square one: this has nothing to do with the indirection. In fact, in the Java/C# model, class types are value type, which hold a reference to a payload. And the whole typing and subtyping business happen on these value types.

No, classes are reference types, structs are values types in c#.

-Alex
May 12, 2021
On Wednesday, 12 May 2021 at 01:06:29 UTC, Walter Bright wrote:
> On 5/11/2021 5:04 PM, Andrei Alexandrescu wrote:
>> Another unpleasant issue:
>> 
>>      enum Y : string { f7 = "%d" }
>>      writeln(typeof(Y.f7.representation).stringof);
>> 
>> prints immutable(ubyte)[], not immutable(char)[]. So not even Y.f7.representation is usable. Sigh.
>
> The representation of a named enum is its base type.
>
> The representation of a string type is immutable(ubyte)[].
>
> It's consistent.

Y.f7 is of type Y. It's representation is string, not immutable(ubyte)[]

typeof(Y.f7.representation) ought to be string.
typeof(Y.f7.representation.representation) ought to be immutable(ubyte)[]

Unless I'm missing something, that wold b the consistent behavior. Unless representation is supposed to recurse up to the bottom turtle?