Property discussion wrap-up

Artur Skawina art.08.09 at gmail.com
Tue Jan 29 04:02:34 PST 2013


On 01/28/13 23:17, Timon Gehr wrote:
> On 01/28/2013 01:34 AM, Artur Skawina wrote:
>> On 01/28/13 00:23, Timon Gehr wrote:
>>> On 01/27/2013 07:12 PM, Artur Skawina wrote:
>>>> ...
>>>> While it's true that "counter-example" is not the best way to describe the issue,
>>>> it /is/ something that is worth considering. And I say that as somebody who was
>>>> (and still is) arguing for keeping the last pair of (),
>>>
>>> You can do that in any case.
>>>
>>>> because they carry important info for the person /reading/ the code,
>>>
>>> Often that is not the case.
>>
>> Hmm, let's try an example.
>> Imagine a project, which defines a set of reasonable policies, such as:
>>
>> Properties may not:
>>   - allocate memory
>>   - grab locks
>>   - block
>>   - throw exceptions
>>   - cause unexpected changes to the program flow (eg no thread ops)
>>   - etc
>>
>> Now you only need to make sure that these are followed, either via reviews
>> and audits, or some checking tool. But once you're reasonably sure that this
>> is done, you can reason about code, without having to either know or check
>> absolutely everything - which simply does not scale.
> 
> As long as there is no actual proof, you still need to take all those cases into account.

A proof is always nice to have, therefore the mention of "some checking tool".
However even in the absence of one, you get the benefit of being able to split
the problem into smaller ones. First check that the above policies for
@properties are followed, which should be a much smaller task, and one that
can be done incrementally (reviewing new code). Then use that information when
reading /all/ of the code (including the @property implementations). 

> If you think otherwise, I do not see why you believe that anyone on such a project would leave off the parens when they should not be in order to make the above conventions visible at the call site.

If it would be possible to completely trust every programmer to follow
conventions then we wouldn't need eg "const". Because nobody would modify
an object when they're not supposed to, right? Now imagine a scenario
where 'const' does exist but is optional, ie not enforced by the compiler. 


>> So a block like
>>
>>     {
>>         auto lock = grab.some.lock();
>>         if (a && b && c.d)
>>            e = f;
>>     }
>>
>> is then safe from certain class of bugs.
>>
> 
> Not yet. You missed the potential memory allocation, lock, blocking operation, thrown exception and thread op in e's opAssign.

No, I just assumed that sane op overloading policies were given, in this
case similar to the @property restrictions. But it's not about catching
every single potential bug, it's about making the common cases safe. You
do need /some/ context information when reading code; it's requiring that
you either know or check every possibility that does not scale.

Even if "assignments to typeof(e) may allocate, block and throw" is true,
it just means there's one more thing to be aware of, but does not reduce
the value of being able to reason about the other accesses.


>> Now welcome the ()-less calls to the party, and you've lost important
>> information - when auditing you need to make sure that 'a' doesn't
>> contain a blocking call, does not allocate, does not violate locking
>> rules etc. Ditto for 'b', 'c', 'c.d', 'e' and 'f'. And everything that
>> these expressions may call, potentially many levels deep.
>>
> 
> Add another policy that states that this must not be necessary. Why force your strange conventions on eg. me, who does not even use Exceptions, locks, blocking operations or thread ops and does not need to tightly control memory allocations beyond some global performance considerations, because eg. out of memory does never have too worrisome consequences?

Well, function calls requiring trailing parens is not really a "strange convention".


> And another one about identifier naming. 'a', 'b', 'c', 'c.d', 'e', 'f'?

If you have to resort to explicitly encoding unnecessary information in
identifiers then something is wrong. Having a naming convention is ok,
but having it say "methods must end in Method, to avoid confusion with
plain data and properties" is not.


>> Yes, the "proper" way to handle this is using attributes,
> 
> Yes.
> 
>> but that is not possible now, nor will it be in the near future.
> 
> Well, you already bring forward the possibility of having a checking tool.

Proper attributes are way out of scope of this discussion. I do think
they can be done relatively cleanly in a D-like language (via aot udas
and ctfe), but for now @properties are here and would be much more
useful if ()-less calls weren't.

A checking tool should only be necessary for things that the compiler
can't handle itself, mostly expensive checks that can't be run at
compile time.

>> OTOH @properties
>> are already available and in combination with enforcing () on all
>> other calls can often be used in their place.
>>
> 
> And there are quite a few other ways to achieve the same thing.

Like?

>> Allow parens-less calls, and suddenly the value of having @property
>> drops significantly, almost to the point that they can be eliminated.
> 
> Your policies can still be applied, if you like.

With a tool that checks the source for ()-less calls. No, this is
not a good solution.

>> This is why the optional-parens debate is /directly/ related to
>> @properties.
>>
> 
> As far as I am concerned, @properties are mostly sugar most of which is not implemented yet.

Of course. But they would be useful when implemented. The ()-less
calls are the main problem, both when evaluating a property (which
returns a callable) and distinguishing them from functions.
So very little is missing, and killing @properties at this point
would be a step back.


>> OK, so what is the rationale?
> 
> Reading the code.

We disagree here,

> Less noise, compactness of UFCS-chains.

... and agree partially here (as long as the resulting chain isn't ambiguous).


> Note that I always add () when leaving them off does not help, eg. for simple method calls.

And I'm saying that relying on every programmer to get this right won't
work. You and me can agree on a convention, but that won't help, if our
code is modified by someone with a different view on what "helps".
Also, the fact that you /always/ add them means that you do see the
value in making the calls explicit, otherwise there would be no reason
for the extra characters. So, are "less noise" and "compactness" enough
to remove an important piece of information?


>> I'll say upfront that syntax isn't
>> nearly as important as the ability to reason about code;
> 
> They obviously go hand in hand. But "reasoning" about code that refers to definitions one is not aware of any specifications of is just rambling anyway.

Umm, a policy, such as the one in my example, *is* a spec. The alternative
is to be aware of the complete implementation, which does not scale beyond
small and self-contained programs.


> Also, it is not necessarily good practice to write code that is easier to reason about given a handful policies speaking about global program state. The problem should be decomposed in a sane way and the concerns kept separate instead.
> 
>> syntax is something one can get used to, lost information is gone.
>>
> 
> No information is lost. It's you who defined that any information is carried at the call site.

Allowing 'a' to act as 'a()' means that the reader no longer can tell
that 'a' isn't a function call (or was /specifically designed/ to be
used like this). 

artur


More information about the Digitalmars-d mailing list