May 12, 2021
On Wednesday, 12 May 2021 at 01:58:39 UTC, 12345swordy wrote:
> No, classes are reference types, structs are values types in c#.
>
> -Alex

No, both are value type, but in the case of the class, the value contains a reference to the payload that you describe in the class's body. Consider:

class A {}
A a = new A();

void foo(A ainfoo) {
    ainfooo = new A();
}

foo(a);

Was "a" modified here? No it wasn't.
May 12, 2021
On Wednesday, 12 May 2021 at 02:07:14 UTC, deadalnix wrote:
> On Wednesday, 12 May 2021 at 01:58:39 UTC, 12345swordy wrote:
>> No, classes are reference types, structs are values types in c#.
>>
>> -Alex
>
> No, both are value type,
Wrong.

https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/keywords/reference-types

> but in the case of the class, the value contains a reference to the payload that you describe in the class's body. Consider:
>
> class A {}
> A a = new A();
>
> void foo(A ainfoo) {
>     ainfooo = new A();
> }
>
> foo(a);
>
> Was "a" modified here?
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
May 12, 2021
On Wednesday, 12 May 2021 at 02:21:06 UTC, 12345swordy wrote:
> On Wednesday, 12 May 2021 at 02:07:14 UTC, deadalnix wrote:
>> On Wednesday, 12 May 2021 at 01:58:39 UTC, 12345swordy wrote:
>>> No, classes are reference types, structs are values types in c#.
>>>
>>> -Alex
>>
>> No, both are value type,
> Wrong.
>
> https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/keywords/reference-types
>
>> but in the case of the class, the value contains a reference to the payload that you describe in the class's body. Consider:
>>
>> class A {}
>> A a = new A();
>>
>> void foo(A ainfoo) {
>>     ainfooo = new A();
>> }
>>
>> foo(a);
>>
>> Was "a" modified here?
> 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

In layman terms, just because I can replace the item in the box with the exact same box, it does not mean the box hasn't been modified.

- Alex
May 12, 2021
On Wednesday, 12 May 2021 at 02:22:52 UTC, 12345swordy wrote:
> On Wednesday, 12 May 2021 at 02:21:06 UTC, 12345swordy wrote:
>> On Wednesday, 12 May 2021 at 02:07:14 UTC, deadalnix wrote:
>>> On Wednesday, 12 May 2021 at 01:58:39 UTC, 12345swordy wrote:
>>>> No, classes are reference types, structs are values types in c#.
>>>>
>>>> -Alex
>>>
>>> No, both are value type,
>> Wrong.
>>
>> https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/keywords/reference-types
>>
>>> but in the case of the class, the value contains a reference to the payload that you describe in the class's body. Consider:
>>>
>>> class A {}
>>> A a = new A();
>>>
>>> void foo(A ainfoo) {
>>>     ainfooo = new A();
>>> }
>>>
>>> foo(a);
>>>
>>> Was "a" modified here?
>> 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
>
> In layman terms, just because I can replace the item in the box with the exact same box, it does not mean the box hasn't been modified.
>
> - Alex
Woops, meant to say "with the exact same item."

-Alex

May 12, 2021
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. For instance by opening https://replit.com/languages/csharp and running the following code in there:

using System;

class A {
  int i;
  public A(int i_) { i = i_; }
  public int getI() { return i; }
}

class Program {
  static void Main(string[] args) {
    A a = new A(15);
    Console.WriteLine(a.getI());
    foo(a);
    Console.WriteLine(a.getI());
  }

  static void foo(A ainfoo) {
    ainfoo = new A(23);
    Console.WriteLine(ainfoo.getI());
  }
}

May 12, 2021
On Tuesday, 11 May 2021 at 21:36:46 UTC, Andrei Alexandrescu wrote:
>> I apologize for injecting myself into this conversation, but with all due respect, what the hell are you talking about? Everything Deadalnix is saying makes perfect sense - it's basic type theory, and yet you're accusing him of moving goalposts and making up definitions, etc. The problem is that `isSomeString` doesn't respect the LSP and the template constraints on the relevant stdlib functions for enums are a hack to work around that. End of story. if `isSomeString` was defined sensibly, these template constraint hacks would not have to exist.
>> 
>> All the bluster about `popFront` on enum strings, etc. is completely irrelevant, and is a red herring anyway (as was already explained).
>> 
>> I'm sorry for being so blunt, but this conversation is painful to read.
>
> Being blunt is totally cool, but that doesn't make you right.
>
> There's no true subtyping or polymorphism with value semantics. This has been common knowledge in C++ - inheriting a value type is an antipattern for many reasons, and conversion operators are to be used carefully (and not as a substitute to subtyping) for many other reasons.
>
> With value types, it's all static typing, no polymorphism, no LSP beyond what's called ad-hoc polymorphism in the classic Caderlli et al paper (http://poincare.matf.bg.ac.rs/~smalkov/files/old/fp.r344.2016/public/predavanja/FP.cas.2016.07%20-%20p471-cardelli.pdf).

Of course, but I thought the conversation was about strings, not value types. Last I checked, strings are reference types, in the same way that Java objects are reference types.

> What can be aimed for with values is called "parametric polymorphism" (which is NOT subtyping) by the same paper:

The nice thing about D's template constraints, though, is that it allows us to impose subtype polymorphism on a parametrically polymorphic function.

> "Parametric polymorphism is obtained when a function works uniformly on a range of types; these types normally exhibit some common structure."
>
> That works if and only if you can reasonably supplant the same primitives across said range of types. With enums that's onerous; as soon as you "derive" an enum from int you figure that ++x can't reasonably be implemented. Same goes for enum strings - you can't implement the expected string primitives so substitutability is out the window.

++x still fulfills the contract that the derived enum has inherited from `int`: `++: int -> int`. It easily passes the substitutability test. Likewise, enums with a base type of string fulfill all the same contracts that `string` does.

Nowhere in the contract of the string type does it specify that `s[1..$]` returns a value of the same type as `s`, just of type `string`, which a string enum does.

> Values are monomorphic.

Are you saying that all values are monomorphic, or that _value types_ 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

Just so we're clear, my previous post was not trying to insinuate that I am an expert in type theory and you are just too ignorant to understand the arguments presented. I don't claim to be anything close to an expert and only know the basics, and you're the one with the doctorate here.

> 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.)

You just said a paragraph back that values are monomorphic. So are pointed-to values monomorphic or polymorphic? This isn't a gotcha; I'm just confused about which you meant.

I think the point you are trying to make with this story is that an operation on an enum that returns the base type will lead to confusing/wrong behaviour and allowing it for template functions which are meant to take strings would be bad design, just like it was with Widget.clone(). Is that right?
May 12, 2021
On 11.05.21 23:36, Andrei Alexandrescu wrote:
> On 5/11/21 2:37 PM, Meta wrote:
>> On Tuesday, 11 May 2021 at 16:44:03 UTC, Andrei Alexandrescu wrote:
>>>> Again with moving the goalposts.
>>>
>>> To clarify: you can't make up your own definitions as you go so as to support the point you're making at the moment. You can't go "oh, call it something else than a type, my point stays". No. Your point doesn't stay.
>>>
>>> By the same token you can't make up your own definition of what subtyping is and isn't. Value types and reference types are well-trodden ground. You can't just claim new terminology and then prove your own point by using it.
>>
>> I apologize for injecting myself into this conversation, but with all due respect, what the hell are you talking about? Everything Deadalnix is saying makes perfect sense - it's basic type theory, and yet you're accusing him of moving goalposts and making up definitions, etc. The problem is that `isSomeString` doesn't respect the LSP and the template constraints on the relevant stdlib functions for enums are a hack to work around that. End of story. if `isSomeString` was defined sensibly, these template constraint hacks would not have to exist.
>>
>> All the bluster about `popFront` on enum strings, etc. is completely irrelevant, and is a red herring anyway (as was already explained).
>>
>> I'm sorry for being so blunt, but this conversation is painful to read.
> 
> Being blunt is totally cool, but that doesn't make you right.
> ...

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. (Type theory has no notion of lvalues or rvalues, so those would indeed have to be interpreted as different types.)

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.

> 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.

> 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));
> ...


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.

May 12, 2021
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
May 11, 2021
On 5/11/2021 7:04 PM, deadalnix wrote:
> 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)[]

That's what I said.
May 12, 2021
On Wednesday, 12 May 2021 at 02:56:49 UTC, Walter Bright wrote:
> On 5/11/2021 7:04 PM, deadalnix wrote:
>> 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)[]
>
> That's what I said.

🍿