www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - D's hidden function calls getting crazier.

reply Hors <ho rs.com> writes:
It's impossible to tell if reading or writing an object's 
variable would call a function because of UFCS or  property. it 
just hurts because you can't know if it's a function call without 
looking at implementation.

The most basic example is
```
obj.val = 1; // actually calls obj.setVal(1)
assert(obj.val == 1); // actually calls obj.getVal(), assertion 
may fail if getVal or setVal is not implemented correctly. Which 
is unexpected
```

To fix this hidden function craziness, UFCS needs to require 
parantheses, and property function needs to be removed entirely.
Oct 27 2023
next sibling parent reply duckchess <duckchess chess.com> writes:
On Friday, 27 October 2023 at 12:38:10 UTC, Hors wrote:
 It's impossible to tell if reading or writing an object's 
 variable would call a function because of UFCS or  property. it 
 just hurts because you can't know if it's a function call 
 without looking at implementation.

 The most basic example is
 ```
 obj.val = 1; // actually calls obj.setVal(1)
 assert(obj.val == 1); // actually calls obj.getVal(), assertion 
 may fail if getVal or setVal is not implemented correctly. 
 Which is unexpected
 ```

 To fix this hidden function craziness, UFCS needs to require 
 parantheses, and property function needs to be removed entirely.
why do you need to know if it is a function call?
Oct 27 2023
parent reply Hors <ho rs.com> writes:
On Friday, 27 October 2023 at 12:40:39 UTC, duckchess wrote:
 On Friday, 27 October 2023 at 12:38:10 UTC, Hors wrote:
 It's impossible to tell if reading or writing an object's 
 variable would call a function because of UFCS or  property. 
 it just hurts because you can't know if it's a function call 
 without looking at implementation.

 The most basic example is
 ```
 obj.val = 1; // actually calls obj.setVal(1)
 assert(obj.val == 1); // actually calls obj.getVal(), 
 assertion may fail if getVal or setVal is not implemented 
 correctly. Which is unexpected
 ```

 To fix this hidden function craziness, UFCS needs to require 
 parantheses, and property function needs to be removed 
 entirely.
why do you need to know if it is a function call?
Because it kills D's "fast", you may think you just accessing a normal variable but in fact you just calling a lot functions, it can hurt performance. Also for years, obj.val means a variable amd obj.m() means a function. There is no good reason to change that
Oct 27 2023
next sibling parent reply duckchess <duckchess chess.com> writes:
On Friday, 27 October 2023 at 12:46:37 UTC, Hors wrote:
 On Friday, 27 October 2023 at 12:40:39 UTC, duckchess wrote:
 On Friday, 27 October 2023 at 12:38:10 UTC, Hors wrote:
 It's impossible to tell if reading or writing an object's 
 variable would call a function because of UFCS or  property. 
 it just hurts because you can't know if it's a function call 
 without looking at implementation.

 The most basic example is
 ```
 obj.val = 1; // actually calls obj.setVal(1)
 assert(obj.val == 1); // actually calls obj.getVal(), 
 assertion may fail if getVal or setVal is not implemented 
 correctly. Which is unexpected
 ```

 To fix this hidden function craziness, UFCS needs to require 
 parantheses, and property function needs to be removed 
 entirely.
why do you need to know if it is a function call?
Because it kills D's "fast", you may think you just accessing a normal variable but in fact you just calling a lot functions, it can hurt performance. Also for years, obj.val means a variable amd obj.m() means a function. There is no good reason to change that
if they can get inlined, they are, so it doesn't really change anything. also there absolutely is a good reason for this. if you have code ```d struct Foo { int x = 5; } Foo foo; writeln(foo.x); ``` but later realize that you need to have x be a function for any reason, then you don't have to update your sourcecode everywhere. like this: ```d struct Foo { int _x; int x( = 5) { assert(_x > 0); return _x;} } Foo foo; writeln(foo.x); ```
Oct 27 2023
parent reply Hors <ho rs.com> writes:
On Friday, 27 October 2023 at 12:53:32 UTC, duckchess wrote:
 On Friday, 27 October 2023 at 12:46:37 UTC, Hors wrote:
 On Friday, 27 October 2023 at 12:40:39 UTC, duckchess wrote:
 On Friday, 27 October 2023 at 12:38:10 UTC, Hors wrote:
 [...]
why do you need to know if it is a function call?
Because it kills D's "fast", you may think you just accessing a normal variable but in fact you just calling a lot functions, it can hurt performance. Also for years, obj.val means a variable amd obj.m() means a function. There is no good reason to change that
if they can get inlined, they are, so it doesn't really change anything. also there absolutely is a good reason for this. if you have code ```d struct Foo { int x = 5; } Foo foo; writeln(foo.x); ``` but later realize that you need to have x be a function for any reason, then you don't have to update your sourcecode everywhere. like this: ```d struct Foo { int _x; int x( = 5) { assert(_x > 0); return _x;} } Foo foo; writeln(foo.x); ```
It can be useful in some rare cases. But sometimes it does more harm than good, like string operations ``` str.toLower ``` calls toLower function which makes a new string every time you use it. So it's incorrect to tell everything can be solved with inline functions
Oct 27 2023
next sibling parent reply Dom DiSc <dominikus scherkl.de> writes:
On Friday, 27 October 2023 at 13:37:14 UTC, Hors wrote:
 ```d
 str.toLower
 ```
 calls toLower function which makes a new string every time you 
 use it. So it's incorrect to tell everything can be solved with 
 inline functions
I would recommend to ALWAYS assume that everything is a function call. Because having direct access to object members is a very rare case, and very bad design for a public interface of a library. So it will only happen in code written by yourself. And then, if you don't like UFCS, don't use it. UFCS is a huge advantage for reading algorithms in order instead of using lots of parantheses if you want to chain something. ```d a(b(c(d(e(f()))),5)); ``` which is pretty unreadable will become ```d f.e.d.c.b(5).a; ``` which is shorter, more readable and calling order as well as the receiver of additional parameters is now obvious. UFCS is one of D's major boons. If you work with it for a while, you will start to hate other languages that don't provide it.
Oct 27 2023
parent reply Hors <ho rs.com> writes:
On Friday, 27 October 2023 at 14:18:45 UTC, Dom DiSc wrote:
 On Friday, 27 October 2023 at 13:37:14 UTC, Hors wrote:
 ```d
 str.toLower
 ```
 calls toLower function which makes a new string every time you 
 use it. So it's incorrect to tell everything can be solved 
 with inline functions
I would recommend to ALWAYS assume that everything is a function call. Because having direct access to object members is a very rare case, and very bad design for a public interface of a library. So it will only happen in code written by yourself. And then, if you don't like UFCS, don't use it. UFCS is a huge advantage for reading algorithms in order instead of using lots of parantheses if you want to chain something. ```d a(b(c(d(e(f()))),5)); ``` which is pretty unreadable will become ```d f.e.d.c.b(5).a; ``` which is shorter, more readable and calling order as well as the receiver of additional parameters is now obvious. UFCS is one of D's major boons. If you work with it for a while, you will start to hate other languages that don't provide it.
I love UFCS as it makes easier to chain functions. But I have some little worries about confusing variables with functions, and I can't understand what is bad about using variables for a public interface, do we really need a getter and setter functions instead of variables?
Oct 27 2023
parent Dom DiSc <dominikus scherkl.de> writes:
On Friday, 27 October 2023 at 17:30:34 UTC, Hors wrote:
 I love UFCS as it makes easier to chain functions. But I have 
 some little worries about confusing variables with functions, 
 and I can't understand what is bad about using variables for a 
 public interface, do we really need a getter and setter 
 functions instead of variables?
It depends. If you have a pure data-type, variables (fields) are ok. But in an object (class) normally I'd rather nobody messes with the internal stuff. Its just too likely to botch things up. So, yes, in a library I prefer getters (setters not so much for the same reason). But setters are still better than variables, as they don't allow to take the address of the field controlled by them. Of course nothing can stop you from shooting yourself in the foot if you really try - but it should not be the first and easiest thing to do.
Oct 27 2023
prev sibling parent Atila Neves <atila.neves gmail.com> writes:
On Friday, 27 October 2023 at 13:37:14 UTC, Hors wrote:
 On Friday, 27 October 2023 at 12:53:32 UTC, duckchess wrote:
 On Friday, 27 October 2023 at 12:46:37 UTC, Hors wrote:
 On Friday, 27 October 2023 at 12:40:39 UTC, duckchess wrote:
 On Friday, 27 October 2023 at 12:38:10 UTC, Hors wrote:
 [...]
why do you need to know if it is a function call?
Because it kills D's "fast", you may think you just accessing a normal variable but in fact you just calling a lot functions, it can hurt performance. Also for years, obj.val means a variable amd obj.m() means a function. There is no good reason to change that
if they can get inlined, they are, so it doesn't really change anything. also there absolutely is a good reason for this. if you have code ```d struct Foo { int x = 5; } Foo foo; writeln(foo.x); ``` but later realize that you need to have x be a function for any reason, then you don't have to update your sourcecode everywhere. like this: ```d struct Foo { int _x; int x( = 5) { assert(_x > 0); return _x;} } Foo foo; writeln(foo.x); ```
It can be useful in some rare cases.
Either the code isn't being used, or refactoring is common. One of my slides at DConf 2023 was literally an extended version of "don't use parens for function calls", the reasoning being it hurts refactoring. I'd go so far as to say that these days refactoring is orders of magnitude more likely to happen than optimising. I think the answer to "how can I know if it's a function call" is "you shouldn't care unless a profiler told you so". Especially since reading data isn't necessarily free either (cache lines). But sometimes it does more
 harm than good, like string operations
 ```
 str.toLower
 ```
 calls toLower function which makes a new string every time you 
 use it. So it's incorrect to tell everything can be solved with 
 inline functions
Oct 30 2023
prev sibling parent Steven Schveighoffer <schveiguy gmail.com> writes:
On Friday, 27 October 2023 at 12:46:37 UTC, Hors wrote:

 Also for years, obj.val means a variable amd obj.m() means a 
 function. There is no good reason to change that
D1 was released in 2007. And has this feature. And had it before the 1.0 release (I don't know how long before, I can't find it in the changelog). A bit late to object. -Steve
Oct 27 2023
prev sibling next sibling parent Paul Backus <snarwin gmail.com> writes:
On Friday, 27 October 2023 at 12:38:10 UTC, Hors wrote:
 It's impossible to tell if reading or writing an object's 
 variable would call a function because of UFCS or  property. it 
 just hurts because you can't know if it's a function call 
 without looking at implementation.

 The most basic example is
 ```
 obj.val = 1; // actually calls obj.setVal(1)
 assert(obj.val == 1); // actually calls obj.getVal(), assertion 
 may fail if getVal or setVal is not implemented correctly. 
 Which is unexpected
 ```

 To fix this hidden function craziness, UFCS needs to require 
 parantheses, and property function needs to be removed entirely.
If you want to know what function is being called, you can always read the documentation. Every programming language makes its own choices about which details get shown directly in the code, and which details require you to look something up. Ideally, we would like to show the details people care about, and hide the details they don't care about, but different people care about different things, so there is no choice that will make everyone happy. If you care very strongly about always being able to tell when a function is being called, maybe D isn't the language for you, and you'd prefer a language like C or Rust. Or maybe D has enough other advantages that this is an annoyance you can learn to live with. Either way, I'd encourage you to keep in mind that your perspective isn't the only one, and that people who care about different things than you have valid reasons for doing so.
Oct 27 2023
prev sibling next sibling parent Timon Gehr <timon.gehr gmx.ch> writes:
On 10/27/23 14:38, Hors wrote:
 It's impossible to tell if reading or writing an object's variable would 
 call a function because of UFCS or  property. it just hurts because you 
 can't know if it's a function call without looking at implementation.
 
 The most basic example is
 ```
 obj.val = 1; // actually calls obj.setVal(1)
 assert(obj.val == 1); // actually calls obj.getVal(), assertion may fail 
 if getVal or setVal is not implemented correctly. Which is unexpected
 ```
 ...
I guess you can write `*&obj.val = 1;` to make sure it is a field.
 To fix this hidden function craziness, UFCS needs to require 
 parantheses, and property function needs to be removed entirely.
No. Abstractions are valuable. Being able to add (and remove) e.g., logging code after the fact onto an existing exposed public field is very useful.
Oct 28 2023
prev sibling parent user1234 <user1234 12.de> writes:
On Friday, 27 October 2023 at 12:38:10 UTC, Hors wrote:
 It's impossible to tell if reading or writing an object's 
 variable would call a function because of UFCS or  property. it 
 just hurts because you can't know if it's a function call 
 without looking at implementation.

 The most basic example is
 ```
 obj.val = 1; // actually calls obj.setVal(1)
 assert(obj.val == 1); // actually calls obj.getVal(), assertion 
 may fail if getVal or setVal is not implemented correctly. 
 Which is unexpected
 ```

 To fix this hidden function craziness, UFCS needs to require 
 parantheses, and property function needs to be removed entirely.
that's nothing compared to hidden variables ``` void main() { struct S{ int a; } S get(){return S(0);} get.a = 42; // pointless assignment on a hidden variable } ```
Oct 28 2023