DIP5

From D Wiki
Revision as of 05:11, 17 January 2013 by Jmdavis (talk | contribs) (Migrating DIP5 from old wiki)
(diff) ← Older revision | Latest revision (diff) | Newer revision → (diff)
Jump to: navigation, search
Title: Properties 2
DIP: 5
Version: 1
Status: Superceded
Created: 2009-07-27
Last Modified: 2009-07-28
Note: Properties are being officially added to D2.x as part of Annotations.

Abstract

An alternative design of properties. A variant of DIP4.

Rationale

Parts of DIP4 are too puristic. Especially this one:

class Foo
{
    int width = int.init
    {
        get { return value; }
        set { value = set; }
    }
}

Description

Namespaces were proposed as a solution for the properties problem:

class Foo
{
    namespace width
    {
        int val;
        int opGet() { return val; }
        int opSet(int newVal) { val = newVal; }
        //other functions, classes, variables
    }
}

They can be a cool feature but they don't look like properties.

Properties can be implemented similarly to namespaces:

class Form
{
    // Full syntax
    int width
    {
        private int val;
        int opGet() { return val; }
        int opSet(int newVal) { return val = newVal; }
    }

    // Default implementation declaration.
    int height { default; }

    EventHandler onClick
    {
        private EventHandler[] invocationList;
        auto opCatAssign(EventHandler h) { return invocationList~=h; }
        void opCall() { invocationList[](EventData.Default); }
    }
}

Default storage is not a big deal to worry so much about it. The {get;set;} syntax exists in C# only for reflection purposes: librarier such as NHibernate are implemented so they look only for properties while serializing objects, so some default implementation for property-as-a-field is needed there. Another usecase for it is enforcing properties with interfaces, while most properties are trivial. And it seems to be the only usecases for default implementation for property getter, setter and backing storage. Though the same syntax is used for property declaration in interfaces. If one writes nontrivial getter or setter, explicit declarations of backing storage and setter parameter are no more an issue.

The problem with hidden backing storage is C# programmers for some reason used to access an event's invocation list and rearrange it, though getting access to it is tricky.

As to typename duplication, type inference can be used for opGet and opSet.

class Form
{
    int width
    {
        //auto declaration without initializer can be resolved
        //to the enclosing property type
        private auto val; //can be an error if you wrote auto by mistake
        //such type inference is planned for lambda expressions either
        opGet() { return val; }
        opSet(newVal) { return val = newVal; }
    }
}

Interface declaration.

interface IControl
{
    int width
    {
        opGet();
        opSet(int);
    }
    int height { default; } //the same as above
    string title { opGet(); }
}

Problems

  • Namespaces have the same ambiguity as current properties have, but in a more severe form:
int delegate() foo() {
        return delegate int(){ return 5; };
}

// Is this an int or a delegate?
auto x = foo();
//is opGet a member of property or of its return value?
auto x = form.width.opGet();

So virtually no property member can be accessed reliably (guanteeing no clash with the return value members). This can be solved by making properties fully transparent as they are in C#.

  • Possible misuse
int width
{
        int val;
        float opGet() { return val; }
}
  • Peculiar ambiguity: with nested methods whether the member is a property or a function becomes a matter of minor detail: braces after the member identifier.
int width
{
        int val;
        int opGet() { return val; }
}

int incrementWidth()
{
        int val;
        float get() { return val; }
        return get();
}

This may be not a big issue, but still...

Copyright

This document has been placed in the Public Domain.