August 10, 2013
To add to the mess - or maybe suggest a new approach, what about:

class A
{
    int foo();
    void foo=(int a);
    private foo_;
}

Then a.foo = 42; calls the foo= method. No other conversions from a=b to a method invocation.

It may be suggested in one of these 46 pages which I haven't read. And it'll probably break a lot of stuff.
August 10, 2013
Missing an int before foo_ there.
August 10, 2013
On Thursday, 24 January 2013 at 08:35:01 UTC, Walter Bright wrote:
> This has turned into a monster. We've taken 2 or 3 wrong turns somewhere.
>
> Perhaps we should revert to a simple set of rules.
>


Here is my mindless proposal after reading some of the posts.


1. Properties are data. Cannot be called with parenthesis, unless it returns a delegate, in which case the delegate is called. The address of a property is invalid(possibly gotten through a trait).

2. Properties cannot be used in UFCS calls, unless they return a delegate. Only UFCS chaining allows for parenthesis-less calls. e.g., (). -> .

Properties are great, C# doesn't have any issues with them... so why should D? From the little reading, the best I can tell is that the issue comes directly from UFCS and trying to avoid using parenthesis.

Conflating the issue between properties and UFCS isn't an issue with properties or UFCS but with distinguishing between them. UFCS wants property like behavior but then that causes problems with properties themselves. Properties want function call behavior but then that makes them look like functions, yet this defeats the purpose of a property.

Alternative, use a different syntax for chaining UFCS's. Instead of using ()., use something like func1:func2:func3:func4; where : seems to be a somewhat unused symbol... ~, >, >>, or others could potentially be used. Such a symbol AUTOMATICALLY is replaced with `().` since everything is a function call. (no properties can exist in the chain unless they return delegates, in which case the delegate is uses)



August 10, 2013
On Saturday, 10 August 2013 at 10:29:51 UTC, Stian Pedersen wrote:
> To add to the mess - or maybe suggest a new approach, what about:
>
> class A
> {
>     int foo();
>     void foo=(int a);
>     private foo_;
> }
>
> Then a.foo = 42; calls the foo= method. No other conversions from a=b to a method invocation.
>
> It may be suggested in one of these 46 pages which I haven't read. And it'll probably break a lot of stuff.

I like this. It is something like in Ruby. And Ruby also has some meta programming support and there are these 3 "templates" attr_reader, attr_writer and attr_accessor (both of the previous together). And one could write (in Ruby):

class Foo
    attr_writer :name, :number #Ruby has symbols(something like enums)
end

And translates to:

class Foo
    def name=(name)
        @name = name //the @name means class private variable in Ruby
    end
    def number=(number)
        @number = number //and they don't need to be declared(dynamicly typed lang)
    end
end

So in D those 3 attr 'shortcuts' could be implemented with mixin templates. And instead of those 'symbols' they could be just a list of strings. Example:

class Foo {
    mixin Reader!("name", "number");
}


And something similar to happen. Although this not solves all @property problems it makes writing them shorter. And I have started experimenting with that and it is almost finished, but it is a little different from what I described above. The file is a part of a thing I started working on in D, but here is a direct link(https://github.com/nikibobi/spine-d/blob/master/src/spine/util/property.d) and the files is isolated from the other things. See the unittests for examples.
August 10, 2013
On Saturday, 10 August 2013 at 10:29:51 UTC, Stian Pedersen wrote:
> To add to the mess - or maybe suggest a new approach, what about:
>
> class A
> {
>     int foo();
>     void foo=(int a);
>     private foo_;
> }
>
> Then a.foo = 42; calls the foo= method. No other conversions from a=b to a method invocation.
>
> It may be suggested in one of these 46 pages which I haven't read. And it'll probably break a lot of stuff.

The problem with this approach is that the getter is still operating under the semantics of a method, but, as a property, it should be acting like a field. An approach like this would work:

class A {


August 10, 2013
On Saturday, 10 August 2013 at 17:48:34 UTC, BLM768 wrote:
> On Saturday, 10 August 2013 at 10:29:51 UTC, Stian Pedersen wrote:
>> To add to the mess - or maybe suggest a new approach, what about:
>>
>> class A
>> {
>>    int foo();
>>    void foo=(int a);
>>    private foo_;
>> }
>>
>> Then a.foo = 42; calls the foo= method. No other conversions from a=b to a method invocation.
>>
>> It may be suggested in one of these 46 pages which I haven't read. And it'll probably break a lot of stuff.
>
> The problem with this approach is that the getter is still operating under the semantics of a method, but, as a property, it should be acting like a field. An approach like this would work:
>
> class A {
Sorry; message got cut off when I tried to insert a tab and then pressed space with the "Send" button focused.

Continuing...

class A {
    @property int foo();
    @property void foo=(int a);
    //etc.
}

However, it doesn't offer any significant advantage over the current property syntax other than providing a clearer distinction between getters and setters, which is only important when dealing with UFCS, as pointed out in an earlier post.

August 10, 2013
On 10 August 2013 13:27, JS <js.mdnq@gmail.com> wrote:
> On Thursday, 24 January 2013 at 08:35:01 UTC, Walter Bright wrote:
>>
>> This has turned into a monster. We've taken 2 or 3 wrong turns somewhere.
>>
>> Perhaps we should revert to a simple set of rules.
>>
>
>
> Here is my mindless proposal after reading some of the posts.
>
>
> 1. Properties are data. Cannot be called with parenthesis, unless it returns a delegate, in which case the delegate is called. The address of a property is invalid(possibly gotten through a trait).
>
> 2. Properties cannot be used in UFCS calls, unless they return a delegate. Only UFCS chaining allows for parenthesis-less calls. e.g., (). -> .
>
> Properties are great, C# doesn't have any issues with them... so why should D? From the little reading, the best I can tell is that the issue comes directly from UFCS and trying to avoid using parenthesis.
>

Might be wrong "cause I've only spent 5 minutes looking at it severall months ago" but C#'s properties are just simple getter/setter's, no?

-- 
Iain Buclaw

*(p < e ? p++ : p) = (c & 0x0f) + '0';
August 10, 2013
On Thursday, 24 January 2013 at 08:35:01 UTC, Walter Bright wrote:
> This has turned into a monster. We've taken 2 or 3 wrong turns somewhere.

My 2 cents on this, very late in the day. Sorry for the essay, hopefully it'll be worth your time.

No fancy new ideas, but I *think* I have a nice solution that keeps everyone happy and avoids nasty corner-cases and ambiguities:


1) non-property functions must be called with parenthesis, *except* on UFCS calls. No assignment call syntax for non-properties.
  fun1.fun2; //illegal
  fun1.fun2(); //illegal
  fun1().fun2; //OK
  fun1().fun2(); //OK
  fun1; //illegal
  fun1(); //OK
  fun2 = var; //not a function call, but a normal assignment where
              //possible, e.g. initialising a function pointer.
  iota(100).map!"a+1".filter!"a%5".writeln;   //LEGAL :-)

Alternative way of saying it: optional paren for single argument calls in UFCS, enforced parenthesis elsewhere.

Reason: No one wants writeln = "Hello World!"; but a lot of people want "Hello World!".writeln; Also, this removes visual ambiguity between the name of a function and it's result. That only really matters with alias/variadic template parameters but it is very annoying there. That will never conflict with UFCS so everyone's happy.
Note that in the first two examples, fun1 is *NOT* a UFCS call and therefore cannot be done without parens. Preventing optional parens on non-UFCS stops any possible mixing of optional parenthesis and the unary & operator.


2) @property enforces parenthesis-less calling for getters and assignment syntax for setters. No properties with more than one argument allowed. If a property returns a callable, any parenthesis will be applied to the callable

Reason: Properties are not for general purpose work. The whole point of them is to be used roughly like variables and this is as close to that as we're gonna get.


3) Getting the address of a function is done with the & operator. Callables must always have parenthesis *except* when: Parenthesis would be optional for an equivalent normal function AND not when the callable is the result of an expression:
  42.aDelegate.foo; //OK
  42.returnsADelegate.foo; //OK, foo acts on the delegate
  42.returnsADelegate().foo; //OK, foo acts on the delegate
  42.returnsADelegate()().foo; //OK, foo acts on
                               //the return value of the delegate
  aDelegate; //illegal
  aDelegate() //OK
  "blah".aDelegate; //OK
  "blah".aDelegate(); //OK

  auto a = &returnsADelegate; //a is function pointer to
                              //returnsADelegate
  a; //illegal
  auto b = a(); //OK, calls a and b is a delegate
  b; //illegal
  b(); //OK, calls the delegate.

  //given some struct Bar that overloads opBinary!"+" and opCall
  Bar x,y;
  x; //illegal
  x(); //opCall
  (x+y)  //does not call opCall
  (x+y)() //does call opCall

Reason: This allows them to be used transparently like normal functions where possible, but removes ambiguity elsewhere.

3a) Explicitly taking the address of a property. Attempting to shoe-horn this in with normal syntax causes hell for the rest. I suggest some magic here, e.g. __traits(propertyGetterAddress, a.prop) and __traits(propertySetterAddress, a.prop)
A function pointer to a property must be called as a normal function pointer.

  auto v = __traits(propertyGetterAddress, a.prop);
  auto n = v; //n is a function pointer to the getter a.prop,
              //same as v
  auto n = v(); //n is equal to the return of the getter a.prop

  auto h = __traits(propertySetterAddress, a.prop);
  auto j = h; //j is a function pointer to the setter a.prop
              //same as h
  h = 3; //illegal
  h(3); //OK, equates to a.prop = 3;

Reason: This needs to be possible, but using & for it is broken. The most important thing is to keep the syntax of common use-cases clean and unsurprising. ***any other workable syntax for this is ok, __traits is just an (ugly, verbose) example***
Losing all property-ness when working through a function pointer keeps things simple.


4) OPTIONAL: Free functions can be marked as properties, with the same rules applied to them as properties in aggregates, except they must take 1 or 2 arguments instead of 0 or 1.

Reason: Free functions and ufcs => good for encapsulation and nice to look at. Is there any reason why not to allow us to declare free properties?


These rules should provide the least possible surprise and breakage, as far as I can tell. I don't think personally I would have to alter a single line of my code.

What inevitable corner-cases have I missed?
August 11, 2013
Part 1 - Ordinary Functions (the rewrite rules)
--------------------------
Not strictly the discussion topic, but it was a big issue and is related, so:

Q: Do you think the "property" rewrite rules (ie optional opCall operators) are a bad idea.
A: Yes I do
Q: Why?
A1: Look at all the confusion its caused!
A2: Because I have shot myself in the foot too many times because of this to be convinced otherwise. Judging from the posts, I am not the only one hopping around on one foot.
Q: Accepting that it's here to stay, what can you do about it?
A: Pretend they don't exist and always write expressions the "correct" way.

Basically If you're having problems with the "property" rewrite rules, then stop trying to use them. Adopt the habit of always writing your function calls verbosely (even with UFCS) and your problems should go away.

Unless they are going to be gotten rid of entirely, the rewrite rules should not be changed. All of the suggestions I've seen (including Walter's original one) will only lead to more complexity, more special cases, more inconsistency, more confusion, and more problems.

Part 2 - @property
--------------------------

A big problem with @property is that people are thinking of them as functions, no small part due to the "property" rewrite rules discussed above. Even people who suggest that @properties should not use opCall still talk about calling properties, as though they are functions.

The trick is to stop thinking of properties in terms of functions, and to think of them as variables. To "call" a property should make as much sense as calling a variable.

Some have mentioned this, and others have hinted at it, but I think that it has not been made fully clear.

To illustrate, consider this:

   int v;

v is a reference to a block of memory which is accessed by a machine code getter and setter. So

   int a;

is really

   @property { int a() { fetch from memory }; int a(int) { write to memory } }

When you're declaring a true property, you're still declaring a variable, but you're simply providing the complier with altertives to the traditional fetch/write instructions.

------------------------------

Here is another (admittedly more complex) way to look at it.

struct {
  int opAssign(int) { ... };
  int opCast!(int) { ... } ;
} Property

Property prop;

You will need to imagine the opCast as an implicit cast for it to work, but hopefully you will get what I mean.

Here prop is a variable, so it would be parsed like one by the compiler.

The expression "prop = 3" would assign 3 to the contents of prop, Which would result in the opAssign (the setter) being called.

In the expression "y = prop++", we get the contents of prop (via opCast, the getter), apply the ++ operator to it, and assign the result to y.

If prop were an actual integer, the above expressions would work exactly the same way.

Using struct for this is a little ugly, so you could say instead:

@property { int prop(int) { ... }; int prop() { ... }; }

A little neater, but would work the same way.

Part 3 - functions returning delegates.
---------------------------------------

When it comes to delegate properties (ie the getter returns a delegate), I'm sensing that people are having trouble getting their heads around it, always needing to treat it like a special case. Let's try to see it in a way that it is not special.

Lets have

    delegate int() f1;

and

    @property delegate int() f2() { ... };

In the eyes of the greater world f1 and f2 should work the same way.

In the expression y = f1(), we get the contents of f1 (the delegate), apply the "()" operator to it, and assign the result to y;

In the expression y = f2(), we get the contents of f2 (via the getter, which gives us the delegate), apply the "()" operator to it, and assign the result to y;

In both cases y is an int.

Recall an eariler statement:

In the expression "y = prop++", we get the contents of prop (via opCast, the getter), apply the ++ operator to it, and assign the result to y.

Note the similarity. () is like other operators and is no longer special.

How does this work for ordinary functions?

    int func()

can be seen as

    auto func = function int()

func is a variable and its contents is the function. The expression func() means "get the contents of func (the function) an apply () to it (gives int)".

For

    delegate int() delg()

which can be seen as

    auto delg = function (delegate int())()

The expression delg() means "get the contents of delg (the function) and apply () to it (gives delegate)", and delg()() would apply () to whatever delg() gives and, ultimately, give us an int.

----------------------------------

How come people are getting confused about it? That damn optional opCall rewrite rule.

Some people probably think that, for functions returning delegates, if
   f becomes f()
then
   f() becomes f()()

THIS DOES NOT HAPPEN! Nor should it. Remember that and you should be OK. Otherwise consider my suggestion in Part 1.

Part 4 - Answering the original question.
----------------------------------------

Should @property be shot?

No, but it should be reworked

Regards
Jason
38 39 40 41 42 43 44 45 46 47 48
Next ›   Last »