Difference between revisions of "DIP23"

From D Wiki
Jump to: navigation, search
(Write properties)
(Write properties)
Line 212: Line 212:
  
 
1. If "foo" is a function that has the @property annotation AND takes exactly one parameter, then "foo = x" calls foo with argument x. Calling "foo(x)" is disallowed.
 
1. If "foo" is a function that has the @property annotation AND takes exactly one parameter, then "foo = x" calls foo with argument x. Calling "foo(x)" is disallowed.
 +
 +
<syntaxhighlight lang=D>
 +
unittest
 +
{
 +
    void fun(int x) { assert(x == 42); }
 +
    fun = 42;
 +
}
 +
</syntaxhighlighting>
  
 
2. If "foo" is a function that has the @property annotation AND takes exactly two parameters, then "x.foo = y" calls foo with arguments x and y. Calling "foo(x, y)" or "x.foo(y)" is disallowed.
 
2. If "foo" is a function that has the @property annotation AND takes exactly two parameters, then "x.foo = y" calls foo with arguments x and y. Calling "foo(x, y)" or "x.foo(y)" is disallowed.

Revision as of 07:31, 3 February 2013

DIP23: Fixing properties redux

Title: Fixing properties
DIP: 23
Version: 1
Status: Draft
Created: 2013-02-02
Last Modified: 2013-02-02
Author: Andrei Alexandrescu and Walter Bright
Links:

Abstract

There has been significant debate about finalizing property implementation. This document attempts to provide a proposal of reasonable complexity along with checkable examples.

Forces:

  • Break as little code as possible
  • Avoid departing from the existing and intended syntax and semantics of properties
  • Make economy of means (little or no new syntax to learn)
  • Avoid embarrassing situations such as expressions with unexpressible types or no-op address-of operator (as is the case with C functions).

Rationale

goes here

Description

Optional parens stay in

One can't discuss properties without also discussing optional parens. These obviate to some extent the need for properties (at least of the read-only kind) and make for potential ambiguities.

This proposal sustains that optional parentheses should stay in. That means, if a function or method may be called without arguments, the trailing parens may be omitted.

unittest
{
    int a;
    void fun1() { ++a; }
    // will call fun
    fun1;
    assert(a == 1);

    // Works with default arguments, too
    void fun2(string s = "abc") { ++a; }
    fun2;
    assert(a == 2);
}

The same goes about methods:

unittest
{
    int a;
    struct S1 { void fun1() { ++a; } }
    S1 s1;
    // will call fun
    s1.fun1;
    assert(a == 1);

    // Works with default arguments, too
    struct S2 { void fun2(string s = "abc") { ++a; } }
    S2 s2;
    s2.fun2;
    assert(a == 2);
}

However, that's not the case with function objects, delegate objects, or objects that implement the function call operator.

unittest
{
    static int a;
    static void fun1() { ++a; }
    auto p1 = &fun1;
    // Error: var has no effect in expression (p1)
    p1;
    assert(a == 0);
}
unittest
{
    int a;
    void fun1() { ++a; }
    auto p1 = &fun1;
    // Error: var has no effect in expression (p1)
    p1;
}
unittest
{
    static int a;
    struct S1 { void opCall() { ++a; } }
    S1 s1;
    // Error: var has no effect in expression (s1)    s1;
    s1;
}

Taking the type of a symbol that may be used in a paren-less call results in the type of the returned object. THIS IS A CHANGE OF SEMANTICS.

unittest
{
    int fun1() { return 42; }
    static assert(is(typeof(fun1) == int));
}

To get the function type, one must apply the address-of operator.

unittest
{
    int fun1() { return 42; }
    static assert(is(typeof(&fun1) == int delegate()));
    static int fun2() { return 42; }
    static assert(is(typeof(&fun2) == int function()));
}

The same goes about member functions. THIS IS A CHANGE OF BEHAVIOR.

unittest
{
    struct S1 { int fun() { return 42; } }
    S1 s1;
    assert(s1.fun == 42);
    static assert(is(typeof(s1.fun) == int)); // currently fails
}

The basic motivation here is that "s1.fun" should not change type when under "typeof".

If a function returns a reference, then assignment through the paren-less call should work:

unittest
{
    static int x;
    ref int fun1() { return x; }
    fun1 = 42;
    assert(x == 42);
}

A function that returns an object that in turn supports a call with "()" will never automatically apply implicit parens to the returned object. Using either `fun` or `fun()` will return the callable entity. To invoke the callable entity immediately one must use `fun()()`.

unittest
{
    static int x;
    int function() fun1() { return () => 42; }
    assert(is(typeof(fun1) == int function()));
    assert(is(typeof(fun1()) == int function()));
    assert(is(typeof(fun1()()) == int));
    assert(fun1()() == 42);
}

Explicit @property annotations

Functions annotated with @property are subject to additional restrictions compared to regular functions.

In brief, the "()" operator may NEVER be applied EXPLICITLY to a function annotated with @property. THIS IS A CHANGE OF SEMANTICS.

unittest
{
    @property int prop1() { return 42; }
    assert(prop1 == 42);
    static assert(is(typeof(prop1) == int));
    static assert(!__traits(compiles, prop1()));
}

Applying the "()" to a property will simply apply it to the result of the property. THIS IS A CHANGE OF BEHAVIOR.

unittest
{
    @property int function() prop1() { return () => 42; }
    assert(prop1() == 42);
}

(Note: The @property annotation is not part of the function type, so it is impossible for a property to return a property.)

Write properties

In order to use the assignment operator "=" property-style, the @property annotation MUST be used.

The rule for allowing assignment with properties is simple.

1. If "foo" is a function that has the @property annotation AND takes exactly one parameter, then "foo = x" calls foo with argument x. Calling "foo(x)" is disallowed.

<syntaxhighlight lang=D> unittest {

   void fun(int x) { assert(x == 42); }
   fun = 42;

} </syntaxhighlighting>

2. If "foo" is a function that has the @property annotation AND takes exactly two parameters, then "x.foo = y" calls foo with arguments x and y. Calling "foo(x, y)" or "x.foo(y)" is disallowed.

3. If "foo" is a member function of a class or struct that has the @property annotation AND takes exactly one parameter (aside from the implicit parameter this), then "x.foo = y" calls x.foo with argument y.

Talk

goes here

Copyright

This document has been placed in the Public Domain.