July 24, 2009
Rainer Deyke wrote:
> Andrei Alexandrescu wrote:
>> Rainer Deyke wrote:
>>> 1. C++'s object model, complete with polymorphic value types,
>> What designs make good use of polymorphic value types?
> 
> In a way, that's a loaded question, since C++ classes don't stay
> polymorphic when passed around by value.  Actual polymorphic value types
> that stay polymorphic when copied would be much more useful.  However,
> even the limited polymorphic value types of C++ are useful.

I'll try to briefly reply to this (I've been offline since yesterday's storm when a thunder fried the cable modem).

> Let's see:
> 
>   - Implementation inheritance of value types, even without actual
> polymorphism, is useful.  See, for example, the curiously recurring
> template pattern (and all of its uses).
> 
>     And, yes, you can achieve similar results in D by using template
> mixins, but this is a specialized mechanism where C++ manages to do with
> the same generalized mechanism used for polymorphism and interface
> inheritance.
> 
>     I use inheritance of value types all the time.

Well I use it sometimes too, but it's so fraught with peril that virtually all coding standards I've ever seen, all books and magazine articles on the topic, all pundits, say one thing: don't. Better use composition.

>   - Polymorphic references to value types are often useful.  D has
> references to value types (in the form of 'ref' parameters and pointers)
> but these are not polymorphic.  As an example, I would name the standard
> C++ iostream.
> 
>     They're value types (non-copyable for the most part, but stored
> directly instead of as pointers, with RAII), but they're often passed
> around as polymorphic references.
> 
>     I use polymorphic references to value types occasionally.

I don't know what polymorphic references to value types mean, and I don't think iostreams are representative for the matter, or a good design in general.

>   - C++ makes a clear distinction between a reference and the thing
> being referenced.  Even if value types and polymorphism were mutually
> exclusive, this distinction would be useful.  All types are consistently
> treated as value types, even those types that reference another object.

This is incorrect. C++ references are not value types. The design of references in C++ is particularly poor.

>   I *like* having to write 'gc_ptr<Object> p;' instead of 'Object p;'.
> I *like* having to write 'p->f();' instead of 'p.f();'.  It keeps my
> code clearly and more explicit.

I understand, but that's a judgment call (e.g. explicit calls instead of operator overloading). Other people prefer the opposite.

>   - C++ does not have separate rules for value types and reference
> types.  All types are implicitly value types; values of all types can be
> placed on the heap.  This simplifies the language by having just one set
> of rules instead of separate rules for classes and structs.  Again, this
> unification would be useful even if it was an error to declare a
> variable of a polymorphic type as a direct variable.

Again, C++ has references which kind of introduce separate and very different rules.

>>> deterministic destructors,  arbitrary copy constructors, and optional
>>> lack of default constructor.
>> Struct have that except for default constructor. In D, currently default
>> constructors cannot execute code. This is a limitation that we might
>> need to address, although it has some advantages.
> 
> There are things that copy constructors can do that the post-blit
> operator can't do.

Yes, mostly wrong things. I think it would be a huge loss if D copied C++'s model.

> Also, unless I am mistaken, D can move value types
> around in memory at will, which also invalidates designs that are useful
> in C++.

C++ copy+destroy sequence has been an absolute pest for years. Rvalue references fix that by adding another layer of complexity. I'm very happy D didn't inherit that.


Andrei
July 24, 2009
Walter Bright wrote:

> Michiel Helvensteijn wrote:
>> That's just a few reasons right there. D's properties lack elegance and they lack potential.
> 
> Let's start with:
> 
> 1. What is a property?

A property contains information describing some object, which may or may not
be mutable.

> 2. How is that different from, say, a pure function?

A pure function is a computation without side effects:
- does not have to relate to any one object
- a mutable counterpart may not make any sense
- it cannot have any side effects while that may make sense even for a
getter (lazy load for example)
- it does not have to *describe* an object in the sense that a property does

The mechanism may be exactly the same, but a property gives a big hint to the programmer (and tools) as to what kind of thing you are dealing with.

Extremely dumb example, all very much the same going from less to more nice:

color(corvette, Color.Red);
assert(color(corvette) == Color.Red);

corvette.color(Color.Red);
assert(corvette.color() == Color.Red)

corvette.color = Color.Red;
assert(corvette.color == Color.Red);


July 24, 2009
Walter Bright wrote:

>> That's just a few reasons right there. D's properties lack elegance and they lack potential.
> 
> Let's start with:
> 1. What is a property?
> 2. How is that different from, say, a pure function?

I agree with Bill's reply to this one.

Again it appears you're avoiding those specific issues I mentioned. Quite blatantly, I might add. Many people have been asking you the same thing. Do you not have a answer?

-- 
Michiel Helvensteijn

July 24, 2009
Bill Baxter wrote:
> On Fri, Jul 24, 2009 at 2:19 PM, Andrei
> Alexandrescu<SeeWebsiteForEmail@erdani.org> wrote:
>> Jarrett Billingsley wrote:
>>> On Thu, Jul 23, 2009 at 5:56 PM, Michiel
>>> Helvensteijn<m.helvensteijn.remove@gmail.com> wrote:
>>>> Eldar Insafutdinov wrote:
>>>>
>>>>> from your post:
>>>>>
>>>>> property int length {
>>>>>    get() { return this.len; }
>>>>>    set(newLen) { this.len = newLen; }
>>>>>    void opIncrement() { this.len++; }
>>>>> }
>>>>>
>>>>> I don't think that's flexible to overload every operator to get the best
>>>>> performance. As Walter likes to say the best way should be the most
>>>>> obvious. Besides we forgot that D2 allows to return references, which
>>>>> eliminates the issue.
>>>> If your property really just hides a private member variable, it was
>>>> probably for encapsulation purposes or because you want redundant actions
>>>> to be taken for every access. If you return a reference, you give
>>>> unlimited
>>>> and unrestricted access to that variable with only one call, and you
>>>> might
>>>> as well not have used a property at all.
>>>>
>>>> If your property is derived -- that is, if it doesn't directly mirror a
>>>> variable --, there is no reference to return.
>>>>
>>>> Besides, in D, you can probably use mixins to copy the entire interface
>>>> of a
>>>> type into the property without code duplication.
>>> You're suggesting adding something like 25 operator overloads to every
>>> property.  Can you say "code bloat"?
>>>
>>> Why not just use the following solution, which has been proposed
>>> God-knows-how-many-times and already has precedence in other languages
>>> (like C#)?
>>>
>>> obj.prop op= value;
>>>
>>> Simply becomes:
>>>
>>> obj.prop.set(obj.prop.get op value);
>> I think this would be inefficient in many cases.
> 
> That is true but I also think:
> * probably that is the best that can be achieved automatically.
> * if better can be achieved automatically then the compiler can just
> do that under the hood.  No user code needs to change.
> * set(get op value) probably won't be inefficient in some cases.
> * having a op= b work automatically in an inefficient way is better
> than forcing the users of the code to make the same ineffecient
> transformation in their code manually (which is the status quo of
> property syntax in D).
> 
> So I think how to make a op= b whizzy fast can be decided later.  And
> for now set(get op value) is good enough.

Ok, I got convinced.

Andrei
July 24, 2009
On Fri, Jul 24, 2009 at 6:44 PM, Andrei Alexandrescu<SeeWebsiteForEmail@erdani.org> wrote:
> Bill Baxter wrote:
>>
>> On Fri, Jul 24, 2009 at 2:19 PM, Andrei Alexandrescu<SeeWebsiteForEmail@erdani.org> wrote:
>>>
>>> Jarrett Billingsley wrote:
>>>>
>>>> On Thu, Jul 23, 2009 at 5:56 PM, Michiel Helvensteijn<m.helvensteijn.remove@gmail.com> wrote:
>>>>>
>>>>> Eldar Insafutdinov wrote:
>>>>>
>>>>>> from your post:
>>>>>>
>>>>>> property int length {
>>>>>>   get() { return this.len; }
>>>>>>   set(newLen) { this.len = newLen; }
>>>>>>   void opIncrement() { this.len++; }
>>>>>> }
>>>>>>
>>>>>> I don't think that's flexible to overload every operator to get the
>>>>>> best
>>>>>> performance. As Walter likes to say the best way should be the most
>>>>>> obvious. Besides we forgot that D2 allows to return references, which
>>>>>> eliminates the issue.
>>>>>
>>>>> If your property really just hides a private member variable, it was
>>>>> probably for encapsulation purposes or because you want redundant
>>>>> actions
>>>>> to be taken for every access. If you return a reference, you give
>>>>> unlimited
>>>>> and unrestricted access to that variable with only one call, and you
>>>>> might
>>>>> as well not have used a property at all.
>>>>>
>>>>> If your property is derived -- that is, if it doesn't directly mirror a variable --, there is no reference to return.
>>>>>
>>>>> Besides, in D, you can probably use mixins to copy the entire interface
>>>>> of a
>>>>> type into the property without code duplication.
>>>>
>>>> You're suggesting adding something like 25 operator overloads to every property.  Can you say "code bloat"?
>>>>
>>>> Why not just use the following solution, which has been proposed God-knows-how-many-times and already has precedence in other languages (like C#)?
>>>>
>>>> obj.prop op= value;
>>>>
>>>> Simply becomes:
>>>>
>>>> obj.prop.set(obj.prop.get op value);
>>>
>>> I think this would be inefficient in many cases.
>>
>> That is true but I also think:
>> * probably that is the best that can be achieved automatically.
>> * if better can be achieved automatically then the compiler can just
>> do that under the hood.  No user code needs to change.
>> * set(get op value) probably won't be inefficient in some cases.
>> * having a op= b work automatically in an inefficient way is better
>> than forcing the users of the code to make the same ineffecient
>> transformation in their code manually (which is the status quo of
>> property syntax in D).
>>
>> So I think how to make a op= b whizzy fast can be decided later.  And for now set(get op value) is good enough.
>
> Ok, I got convinced.

Have you also seen Michiel's suggestion?

obj.prop op= value;
// auto temp = obj.prop.get();
// temp op= value;
// obj.prop.set(value);

I think this is probably somewhat more correct, in the face of operator overloading on typeof(temp).  That is, if obj.prop is a struct that overloads opAddAssign, you'd want "obj.prop += 5" to really call obj.prop's opAddAssign, rather than its opAdd.
July 25, 2009
Walter Bright wrote:
> Michiel Helvensteijn wrote:
>> That's just a few reasons right there. D's properties lack elegance
>> and they
>> lack potential.

> Let's start with:
> 
> 1. What is a property?

A piece of state referred to by some name; state which is usually part of some object.  Semantically the same as a field, except it may be more complex then just an address in memory.

I'm going to fudge your next question a little...  We've already established that pure functions won't cut it since they prevent you from doing lazy loads, or anything involving mutation (like tracking access counts).  So let's pretend the question was:

> 2. How is that different from, say, a function?

Which has a more interesting answer.  :)

In terms of machine code, nothing.  This isn't a question about functionality, in the end, it's a question of intent.

To reiterate: something like Qt depends pretty heavily on properties being distinct from functions.  For example:

T foo();
T foo(T);

Is foo a property or a function?  It's impossible to tell.  It could be a function with two overloads, one of which requires an argument, or it could be a getter/setter pair.

This causes a few problems.  The first of which, and the one that I think is the most important, is it cripples IDEs and other graphical tools.  Another example: I'm in the planning stages for a graphical editor that snaps together components.  I'd love to be able to just introspect an object at runtime and grab all the properties, but I can't.  I cannot determine, for certain, which methods are properties and which are REALLY methods, so I'll probably end up doing something like this:

class Blah : Component
{
    mixin(properties("a", "b", "c"));
}

To identify them.  Of course, this isn't much use for a general IDE, unless this sort of thing becomes a standard in the language, and even then it's pretty hideous.

Another problem is the issue with leaving off parens.  The canonical example being:

struct Foo
{
  int delegate() bar();
}

You have to put in *two* sets of parens to call the result from bar, not one as might be assumed.

There's also the argument for debuggers being able to automatically display properties; I think this one is fairly borderline since if I was writing a debugger, it would only *automatically* display the result of pure functions, property or not.

Finally, there's a speculative argument in that the current arrangement limits properties to having a getter and/or a setter.  Andrei made a comment a while back about needing three primitives for them to work properly; in the current system.

---

So basically, the issues are not *really* technical; they all boil down to ambiguity in terms of interpretation, or clumsy corner cases.

To speak of the technical implementation of properties, my comments on DIP4 basically recommends to implement "new style" properties almost EXACTLY THE SAME as current ones; the difference is that "new style" properties have slightly more information available about them.


I hope all that helps, although I suspect I'm just rambling at this point.  :)
July 25, 2009
Ary Borenszweig wrote:
> Maybe what scares Walter is a whole new syntax for properties. If at least you could say which functions are properties and which are not, that would be a small change and it'll make it possible for other things. Something like:
> 
> int property foo(); // getter
> int property foo(int value); // setter

My proposal requires no new keywords and no new syntax:

int getfoo(); // getter
int setfoo(int); // setter


-- 
Rainer Deyke - rainerd@eldwood.com
July 25, 2009
Leandro Lucarella wrote:
> Yes, that's true, but this is a common problem. Think of operator overloading... Even in Python that could mean *anything* (__getattr__).

I want a programming language that enables me to write good code, not
one that prevents me from writing bad code.  Abusing operator
overloading is bad code, and I try to avoid it.  Using 'a = b; a.x = 5;'
 to mean two different things is also bad code, but it's not something I
can avoid in D.


-- 
Rainer Deyke - rainerd@eldwood.com
July 25, 2009
Andrei Alexandrescu wrote:
> Rainer Deyke wrote:
>> Composition instead of inheritance?  Doesn't work if I need virtual functions.
> 
> This is going in circles. You started from "implementation inheritance of value types, even without actual polymorphism".

Yes, but implementation inheritance may involve customization points. Virtual functions are one of of providing this customization.  The curiously recurring template pattern provides another way.  Either way, the class looks like a non-polymorphic value type from the outside.

Simple composition does *not* work here, unless the inner component has a pointer to the outer aggregate.


-- 
Rainer Deyke - rainerd@eldwood.com
July 25, 2009
Rainer Deyke wrote:
> Ary Borenszweig wrote:
>> Maybe what scares Walter is a whole new syntax for properties. If at
>> least you could say which functions are properties and which are not,
>> that would be a small change and it'll make it possible for other
>> things. Something like:
>>
>> int property foo(); // getter
>> int property foo(int value); // setter
> 
> My proposal requires no new keywords and no new syntax:
> 
> int getfoo(); // getter
> int setfoo(int); // setter

This is pretty clean, and in keep with the opXxx approach. Actually how about defining property foo by defining opGet_foo and opSet_foo.

Andrei
13 14 15 16 17 18 19 20 21 22 23 24 25
Top | Discussion index | About this forum | D home