www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - Is there a way to enforce UFCS?

reply thebluepandabear <therealbluepandabear protonmail.com> writes:
Say you have the following class which represents a dog 🐶:

```D
class Dog {
      property {
         string name();

         void name(string name) {
             _name = name;
         }
     }

     private {
         string _name;
     }
}
```

And you have the following code with constructs a `Dog` object:

```D
void main() {
     Dog d = new Dog();

     d.name = "Poodle";
     writeln(d.name);
}
```

In the code we can see that we have utilized UFCS (universal 
function call syntax) to set the properties for the object. This 
feature is great. We have also used D's ` property` annotation 
which gives us some other advantages that you can see in the 
documentation.

The issue I have is that UFCS is not enforced, which I thought 
would be a rather good use for the ` property` annotation. This 
means that we can do the following in our code:

```D
void main() {
     Dog d = new Dog();

     d.name("poodle");
     writeln(d.name());
}
```

I prefer the UFCS version over the non-UFCS version since it is 
more clear that it is a property and it matches closely with the 
official D style guide.

I am disappointed that ` property` does not enforce UFCS, as I 
believe that it would add to its usefulness. Sometimes throughout 
my codebase I get confused and write properties in non-UFCS 
syntax, which bugs me a bit.

My question is: is there a way to enforce UFCS-syntax?
Jan 03 2023
next sibling parent =?UTF-8?Q?Ali_=c3=87ehreli?= <acehreli yahoo.com> writes:
On 1/3/23 19:42, thebluepandabear wrote:

       property {
As your post proves, that feature is at most half-baked and is discouraged. Today, there is just one known obscure effect of using it.
          void name(string name) {
              _name = name;
          }
      d.name = "Poodle";
 In the code we can see that we have utilized UFCS (universal function
 call syntax)
UFCS is for calling free-standing functions as if they are member functions. Since your example already uses member functions, this feature is not UFCS. And I don't think it has a name. It is always possible to pass a single-argument with the assignment syntax: void foo(int i) {} void main() { foo = 42; } Pretty wild! :) But that's what makes your assignment above work. (Not UFCS.)
 not enforced [...] we can
 do the following in our code:
      d.name("poodle");
I don't see a problem with that. :)
 I am disappointed that ` property` does not
Many people are disappointed that property is pretty much useless.
 is there a way to enforce
D gives us the tools to do that but it's not trivial. The function can return an object that represents a variable (member variable or not). And an assignment to that representative object can set the actual variable. However, I tried to achieve it with an intermediary object but failed because the same ="Poodle" syntax broke it and demanded that we type the empty parenthesis. So, I think what you want does not exist. // I have a feeling something similar exists in Phobos // but I could not find it. // // This is a reference to any variable. struct MyRef(T) { T * ptr; void opAssign(T value) { *ptr = value; } void toString(scope void delegate(in char[]) sink) const { sink(*ptr); } } // This is a convenience function template so that // the users don't have to say e.g. MyRef!string // themselves. (See name() below.) auto myRef(T)(ref T var) { return MyRef!T(&var); } class Dog { property name() { return myRef(_name); } private { string _name; } } void main() { Dog d = new Dog(); // Great: The following won't work. // d.name("poodle"); // However, now there is the empty parenthesis. :( d.name() = "Poodle"; import std.stdio; writeln(d.name); } Ali
Jan 03 2023
prev sibling parent reply bauss <jacobbauss gmail.com> writes:
On Wednesday, 4 January 2023 at 03:42:28 UTC, thebluepandabear 
wrote:
 ...

 My question is: is there a way to enforce UFCS-syntax?
None of your code actually uses UFCS. This is UFCS: ``` class Foo { int bar; } void setBar(Foo foo, int value) { foo.bar = value; } void main() { foo.setBar(100); // UFCS setBar(foo, 100); // Non-UFCS (Above expands to this) } ``` This is not UFCS but just class method calling: ``` class Foo { int bar; void setBar(Foo foo, int value) { foo.bar = value; } } void main() { foo.setBar(100); // Not UFCS - just method call to the class foo.setBar = 100; // Not UFCS - simply a setter function call (equal to the above) setBar(foo, 100); // Error } ``` Also note that property doesn't really do anything now and there's even talk about deprecating it. Althought I personally still use it, then it doesn't have much of a function and none of your code is affected if you remove it.
Jan 04 2023
next sibling parent thebluepandabear <therealbluepandabear protonmail.com> writes:
On Wednesday, 4 January 2023 at 14:21:46 UTC, bauss wrote:
 On Wednesday, 4 January 2023 at 03:42:28 UTC, thebluepandabear 
 wrote:
 ...

 My question is: is there a way to enforce UFCS-syntax?
None of your code actually uses UFCS. This is UFCS: ``` class Foo { int bar; } void setBar(Foo foo, int value) { foo.bar = value; } void main() { foo.setBar(100); // UFCS setBar(foo, 100); // Non-UFCS (Above expands to this) } ``` This is not UFCS but just class method calling: ``` class Foo { int bar; void setBar(Foo foo, int value) { foo.bar = value; } } void main() { foo.setBar(100); // Not UFCS - just method call to the class foo.setBar = 100; // Not UFCS - simply a setter function call (equal to the above) setBar(foo, 100); // Error } ``` Also note that property doesn't really do anything now and there's even talk about deprecating it. Althought I personally still use it, then it doesn't have much of a function and none of your code is affected if you remove it.
Yeah I got mixed up. I think a good use of ` property` is for code clarity, it makes it clear which parts of your code should be treated as properties.
Jan 04 2023
prev sibling parent reply Dom DiSc <dominikus scherkl.de> writes:
On Wednesday, 4 January 2023 at 14:21:46 UTC, bauss wrote:
 ```d
 class Foo {
   int bar;

   void setBar(Foo foo, int value) {
     foo.bar = value;
   }
 }

 void main() {
   foo.setBar(100); // Not UFCS - just method call to the class
   foo.setBar = 100; // Not UFCS - simply a setter function call 
 (equal to the above)
   setBar(foo, 100); // Error
 }
 ```
I think this is really another usecase for property: we should forbid the function call syntax for them (so one needs to use them like a variable). This is useful to enable library authors to enforce this, so that if a property is replaced by a variable (e.g. during refactoring), it's not a braking change for the users of the library. Else it could be that setters or getters are directly called, which would not compile anymore with a variable (that doesn't have getters or setters). properties are intended to be used like variables - the only differences (and the reason why they exist) is the read or write protection they provide, and that they may be calculated on the fly (not stored in memory at all). That they are realized with a construct that looks similar to (a pair of) functions should be completely transparent for the user of a library. Properties are not functions. If you want a function, use a function. If properties would be the same as functions, they are superfluous garbage. Either make something useful out of them or remove them.
Jan 05 2023
next sibling parent "H. S. Teoh" <hsteoh qfbox.info> writes:
On Thu, Jan 05, 2023 at 02:32:17PM +0000, Dom DiSc via Digitalmars-d-learn
wrote:
[...]
 I think this is really another usecase for  property: we should forbid the
 function call syntax for them (so one needs to use them like a variable).
[...]
 Properties are not functions. If you want a function, use a function. If
  properties would be the same as functions, they are superfluous garbage.
 Either make something useful out of them or remove them.
We have been talking about deprecating and removing property for years now. Somebody just has to bite the bullet and push it through the deprecation process... ... OR come up with a DIP that implements property in a sane, fully worked out way, not the half-hearted, incomplete, leaky implementation that it is today. // In my own code, I've stopped bothering with property for the most part. Parentheses are optional for argumentless functions in general anyway, so there's really no need to write property on anything. This works: struct S { private int _x; int x() { return _x; } } S s; int y = s.x; The only significant thing property does right now is to add confusion when the unary & operator is used or when the property function returns a delegate. Not worth the trouble, I say. Just don't use property at all, plain old member functions work just fine. T -- Some ideas are so stupid that only intellectuals could believe them. -- George Orwell
Jan 05 2023
prev sibling parent reply thebluepandabear <therealbluepandabear protonmail.com> writes:
 them or remove them.
I agree, forbidding function call syntax would be a great usecase for ` property`. It will probably never get implemented though.
Jan 05 2023
parent reply Salih Dincer <salihdb hotmail.com> writes:
On Thursday, 5 January 2023 at 23:05:17 UTC, thebluepandabear 
wrote:
 them or remove them.
I agree, forbidding function call syntax would be a great usecase for ` property`. It will probably never get implemented though.
In older versions, it worked when printing values ​​with writeln. But due to an error in formattedWrite, the program breaks. So I stopped using property. For example, you can't see the difference in: ```d struct Funy(T) { import std.conv : to; this(X)(X x) {   value = x.to!T; } T value; alias value this; // [a] getter  // property T opAssign(T x) { // [b] setter return value = x; } alias opCall = opAssign; /* hack: `opAssign` methods are not used for initialization, but for subsequent assignments [c] incrementor: */  // property T opOpAssign(string op: "+", X)(X x) { return value = value + x.to!T; } } import std.stdio; void main() { auto funy = Funy!uint(20); funy.value.writeln; // 20  funy = 10; funy.value.writeln; // 1 class Fun { Funy!int n; this(int i) { n = i; // or: n(i + 1); } } auto fun = new Fun(-2); fun.n.writeln; // -1  fun.n += 19.999;  fun.n.writeln; // 18 } ``` Let's continue the fun... ```d struct Funy(T) { this(T x) { value = x; } T value; alias opCall this; // property: T opCall(T n) { return value = n; } T opCall() { return value; } } import std.stdio; void main() { auto funy = Funy!uint(20); funy.value.writeln; // 20 funy = 10; funy.value.writeln; // 1 class Fun { Funy!int n; this(int i) { n = i; // or: n(i + 1); } } auto fun = new Fun(-2); fun.n.writeln; // -1 fun.n = 20; fun.n.writeln; // 0 } /* PRINTS: 20 10 Funy!int(-1) Funy!int(20) ``` If you don't want to get the above output you should use the previous example. But don't forget to connect alias and opCall. For example, you can use property in version 2.0.83 without all the fanfare. SDB 79
Jan 06 2023
parent Salih Dincer <salihdb hotmail.com> writes:
On Friday, 6 January 2023 at 15:31:09 UTC, Salih Dincer wrote:
 If you don't want to get the above output you should use the 
 previous example. But don't forget to connect alias and opCall. 
 For example, you can use  property in version 2.0.83 without 
 all the fanfare.
I forgot one thing: if you implement getter/setter like below use inout as well. Actually, this must be a bit of a bug and neither I nor anyone else reported it! Ok they will say don't use property. But this time the screen output will be like typeid. If you don't want that to happen, you have to use inout in getter functions. ```d struct Funy(T) { this(T x) {   value = x; }  T value; alias opCall this;   property:  T opCall(T n) {   return value = n; }  T opCall() inout { return value; } } ``` SDB 79
Jan 06 2023