Property discussion wrap-up
Artur Skawina
art.08.09 at gmail.com
Tue Jan 29 07:09:52 PST 2013
On 01/29/13 14:29, Timon Gehr wrote:
> On 01/29/2013 01:02 PM, Artur Skawina wrote:
>> 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.
>>
>
> I do not see any consistent line of reasoning that I could agree with or argue against in the above paragraphs. I think the reasoning is contradictory, because it argues for opposite cases in analogous setups. What am I missing?
That enforcement matters. A recurring argument that has been made in this
thread, basically that 'you can still use the () if you want', misses the point
- it's not about preferences, it's about the information given by the lack of
'()' after an expression.
>>>> 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.
>>
>
> I forgot about the side-effects of the ~500 alias this lookups in that example. :o)
"Alias this" is just another syntax for "opImplicitCast". :^)
So the same restrictions apply.
>> No, I just assumed that sane op overloading policies were given, in this
>> case similar to the @property restrictions.
>
> I see. The post explicitly stated that @property policies would be sufficient though. Anyway, I guess extending on the points made, the only sane op overloading policy is to not use op overloading, because not using it means you gain the information that every operator is a built-in?
In some cases forbidding op overloading can be reasonable, in others keeping
them "sane" is enough. For some definition of "sane"; mostly a) behaving as
can reasonably be expected to, and b) not having unexpected side effects. It
all depends on the context.
>> 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.
>>
>
> If the ultimate goal is to _reason_ about code more easily, using only safe pure functions and/or writing down actual machine-checkable specifications and/or using static analysis tools that actually prove the absence of certain classes of issues is a much better way to go about it. () or not () just does not enable very powerful statements about the code. (Most code snippets will actually contain a () call.)
A feature not powerful enough to handle all cases can still be very useful
for dealing with a subset.
>> ...
>>> 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.
>
> I have no idea how that differs from "If you have to resort to explicitly encoding unnecessary information in '()' then something is wrong."
'()' is universally recognized as (aot) a call op. Inventing another
convention is not an improvement.
>> 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.
>>
>
> So information beyond 'a', 'b', 'c', 'c.d', 'e', 'f' is unnecessary, but a trailing '()' is extremely important?
Obviously, these are supposed to represent expressions that the reader
may not immediately recognize. 'c.d()' carries more info than 'c.d'
alone. No, it won't *always* help. But having the parens be optional
means it will *never* help.
>>>> 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.
>>
>
> We disagree here. (It's not that I don't get what you are saying, it is just that this is not important at all in some projects, and therefore it imo makes no sense to hard-wire it into the language.)
Having different project/site specific dialects would be a bad idea.
I understand why you'd like to skip the parens, but think the gain
is rather small. It's much more important to be able to clearly and
unambiguously express the intent, than saving a little horizontal
screen space.
>> 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.
>>
>
> The checking tool should be built using a compiler API and run alongside normal compilation.
If it's cheap enough it should be (an optional) part of the language
and always-on; if it's costly it won't always be run. But we're
getting off-topic, let's wait with this discussion for an "Attributes
- take it behind the woodshed and shoot it?" thread. ;)
> So () needs automated checking but not the part about allocations, locks, thread ops, etc, for which code reviews are sufficient?
Unlike the things you list, '()' checking is easily doable right now
- because it's not actually 'checking', but just disallowing the
currently legal ()-less case. Implementing the more complex checks
would be much more costly. It's really a separate problem from this
one, which is 'how to handle @property best, and how does it interact
with other language constructs'.
>> No, this is not a good solution.
>>
>
> What else are you arguing for the entire time?
> Furthermore, the ()-checking tool already exists as part of DMD!
It can be made to flag ()-less calls as errors? Note that this
wouldn't be a good solution; would just introduce another dialect.
>>>> 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)
>
> No problem there.
>
>> ...
>>>
>>> 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.
>
> Yes, it is a (quite weak) spec.
Yes, but one that already allows /some/ reasoning.
>> The alternative is to be aware of the complete implementation,
>
> 'The' alternative to what exactly?
Not having any spec at all.
>> which does not scale beyond small and self-contained programs.
>>
>>> ...
>>>> 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).
>>
>
> But that is obviously because it is called 'a'!
>
> Make it 'x.map!(a=>2*a)'. No more issues.
Really? What about if ufcs isn't involved, and it's a free function?
Anyway, even the UFCS case is unclear:
struct X {
template map(alias F) {
int map[F(size_t.sizeof)];
}
}
X x;
Yeah, I know this isn't the best example, as it's unlikely to compile,
but that's just a compiler limitation. But with ()-less calls you always
have to either guess, or know the X implementation.
artur
More information about the Digitalmars-d
mailing list