April 25, 2011 [phobos] Time to get ready for the next release | ||||
---|---|---|---|---|
| ||||
Posted in reply to Steve Schveighoffer | On Mon, 25 Apr 2011 09:58:47 -0400, Steve Schveighoffer <schveiguy at yahoo.com> wrote:
> ----- Original Message -----
>> From: Robert Jacques <sandford at jhu.edu>
>> To: Discuss the phobos library for D <phobos at puremagic.com>
>> Cc:
>> Sent: Sunday, April 24, 2011 12:03 PM
>> Subject: Re: [phobos] Time to get ready for the next release
>>
>> On Sun, 24 Apr 2011 07:33:30 -0400, Jacob Carlborg <doob at me.com> wrote:
>
>>> If writeln = "foo"; doesn't compile but printf =
>> "foo"; does then I would consider it not fixed. The way I would want
>> @property to behave is disallow bar = "foo"; for functions not marked
>> with @property. But still allow functions not marked with @property to
>> be
>> callable without parentheses.
>>
>> Also, this and another post have given me an idea: what if
>> non- at property methods
>> could be assigned to if and only if a valid 'getter' also existed. This
>> would still 'fix' writeln = "foo" but be a less restrictive
>> than an outright ban.
>
> It was an idea that Andrei brought up (before @property syntax was introduced), but I don't think it can be properly enforced:
>
> int select(int timeout = 0); // both "getter" and "setter"
>
> -Steve
Hmm... good point. Counter-point, select's functions signature is still a 'setter' signature. A 'select' / 'select()' method doesn't exist, it's simply syntactic sugar for select(0). Since a lookup of select by DMD involves the actual function overloads and not their sugary-transforms, it should still detect that select has no zero-arg overload and therefore error on 'select = 5'.
|
April 25, 2011 [phobos] Time to get ready for the next release | ||||
---|---|---|---|---|
| ||||
Posted in reply to Jacob Carlborg | On Mon, 25 Apr 2011 13:50:25 -0400, Jacob Carlborg <doob at me.com> wrote:
> On 24 apr 2011, at 18:03, Robert Jacques wrote:
>> On Sun, 24 Apr 2011 07:33:30 -0400, Jacob Carlborg <doob at me.com> wrote:
>>> On 23 apr 2011, at 23:20, Robert Jacques wrote:
>>>> On Sat, 23 Apr 2011 16:06:35 -0400, Jacob Carlborg <doob at me.com> wrote:
>>>>> On 23 apr 2011, at 17:32, David Simcha wrote:
>>>>>> On 4/23/2011 11:24 AM, Jacob Carlborg wrote:
>>>>>>> I think I would like to have something in the middle of strict and loose semantics. I would like that functions marked with @property have to be called like a field:
>>>>>>>
>>>>>>> auto bar = foo.field;
>>>>>>> foo.field = 3;
>>>>>>>
>>>>>>> But functions not marked with @property still can be called without the parentheses:
>>>>>>>
>>>>>>> foo.bar();
>>>>>>> foo.bar;
>>>>>>
>>>>>> Maybe there's been some misunderstanding, but actually this is what loose semantics means. Loose semantics (at least as I understand them) mean stuff marked @property would not be callable using method syntax, and this rule would be used to disambiguate the corner cases, but nothing would change for stuff not marked @property.
>>>>>
>>>>> Ok, then I probably misunderstood. What about:
>>>>>
>>>>> writeln = "foo";
>>>>>
>>>>> is that already fixed?
>>>>
>>>> If by fixed, you mean doesn't compile, then yes, it's fixed. But this might be a quality of implementation issue, regarding method syntax and templates and not a true theoretical fix. Case in point: printf = "foo" works. However, while ugly, neither writeln = "foo" nor printf = "foo" are doing something the original author didn't intend. The greater violators (which actually caused bug reports/confusion) are those where the statements became nonsense, like math_function = 5 or obj.factory_method = 6.[1] Fixes for most of these issues exist: Not using the result from a strongly pure function should be an error, not matter how it's called. And const/immutable methods shouldn't be assignable, since you can't assign to a const or immutable variable. Static/free functions can't be marked const/immutable, but considering the only thing they can modify is global state, pure is equivalent. So neither strongly nor weakly pure functions should be assignable.
>>>
>>> If writeln = "foo"; doesn't compile but printf = "foo"; does then I would consider it not fixed. The way I would want @property to behave is disallow bar = "foo"; for functions not marked with @property. But still allow functions not marked with @property to be callable without parentheses.
>>
>> I have not heard this particular combination before; thank you. More choices are always appreciated. There are real, practical use cases for not- at property methods with write-only field semantics, which this would prevent. And between a real use case and a synthetic straw-man, I believe the use case should win. However, I am interested in any of the practical issues which inspired writeln = "foo", if you know of any.
>
> I don't know if there is an issue with writeln = "foo" other than that it can be confusing and looks very odd.
Then how is it any worse than all the other strange, confusing, odd and downright cryptic code one _could_ write in C/C++/D/etc, but (almost) _never_ actually does? (outside of programming competitions, that is).
|
April 25, 2011 [phobos] Time to get ready for the next release | ||||
---|---|---|---|---|
| ||||
Posted in reply to Robert Jacques |
>________________________________
>From: Robert Jacques <sandford at jhu.edu>
>To: Steve Schveighoffer <schveiguy at yahoo.com>; Discuss the phobos library for D <phobos at puremagic.com>
>Sent: Monday, April 25, 2011 3:29 PM
>Subject: Re: [phobos] Time to get ready for the next release
>
>On Mon, 25 Apr 2011 09:58:47 -0400, Steve Schveighoffer <schveiguy at yahoo.com> wrote:
>> ----- Original Message -----
>>> From: Robert Jacques <sandford at jhu.edu>
>>> To: Discuss the phobos library for D <phobos at puremagic.com>
>>> Cc:
>>> Sent: Sunday, April 24, 2011 12:03 PM
>>> Subject: Re: [phobos] Time to get ready for the next release
>>>
>>> On Sun, 24 Apr 2011 07:33:30 -0400, Jacob Carlborg <doob at me.com> wrote:
>>
>>>>? If writeln = "foo"; doesn't compile but printf =
>>> "foo"; does then I would consider it not fixed. The way I would want @property to behave is disallow bar = "foo"; for functions not marked with @property. But still allow functions not marked with @property to be callable without parentheses.
>>>
>>> Also, this and another post have given me an idea: what if non- at property methods
>>> could be assigned to if and only if a valid 'getter' also existed. This
>>> would still 'fix' writeln = "foo" but be a less restrictive
>>> than an outright ban.
>>
>> It was an idea that Andrei brought up (before @property syntax was introduced), but I don't think it can be properly enforced:
>>
>> int select(int timeout = 0); // both "getter" and "setter"
>>
>> -Steve
>
>Hmm... good point. Counter-point, select's functions signature is still a 'setter' signature. A 'select' / 'select()' method doesn't exist, it's simply syntactic sugar for select(0). Since a lookup of select by DMD involves the actual function overloads and not their sugary-transforms, it should still detect that select has no zero-arg overload and therefore error on 'select = 5'.
Easily worked around:
int select() { return select(0); }
int select(int timeout) { ... }
This seems silly, but it actually is likely to occur with final interface methods that want to enforce what the default select does.
But in any case, this is diverging into the obscure.? I don't think rules that try to guess what the author is intending are as good as annotations which tell the compiler what the author is intending.
-Steve
|
April 25, 2011 [phobos] Time to get ready for the next release | ||||
---|---|---|---|---|
| ||||
Posted in reply to Steve Schveighoffer | On Mon, 25 Apr 2011 09:42:28 -0400, Steve Schveighoffer <schveiguy at yahoo.com> wrote: > ----- Original Message ----- >> From: Robert Jacques <sandford at jhu.edu> >> To: Discuss the phobos library for D <phobos at puremagic.com> >> Cc: >> Sent: Saturday, April 23, 2011 1:05 AM >> Subject: Re: [phobos] Time to get ready for the next release >> >> On Fri, 22 Apr 2011 07:59:54 -0400, Steve Schveighoffer <schveiguy at yahoo.com> wrote: >>>> From: Robert Jacques <sandford at jhu.edu> >>>> To: Discuss the phobos library for D <phobos at puremagic.com> >>>> Sent: Thursday, April 21, 2011 4:05 PM >>>> Subject: Re: [phobos] Time to get ready for the next release >>>> >>>> On Thu, 21 Apr 2011 15:57:57 -0400, Jonathan M Davis >> <jmdavisProg at gmx.com> wrote: >>>> >>>>>> How about the amount of existing code it breaks? How about the >> fact that >>>>>> it breaks using the same function for both method chaining and >> with >>>>>> property syntax? >>>>> >>>>> Something like >>>>> >>>>> auto b = a.prop1.prop2.prop3; >>>>> >>>>> should work. I doesn't at present, but it should. There's a >> bug report on it. >>>> >>>> What about auto b = a.prop1(5).prop2(6).prop3(7); ? >>> >>> Looks like a poor design. If a setter property returns anything, it >>> should >> return the value set. Otherwise: >>> auto b = a.prop1 = 5; // I would expect b == 5 >> >> I both David and I have responded in depth to this in other threads. In >> short, >> trying to decide the goodness of an API design using an abstracted, out >> of >> context, reduced code snippet designed to highlight the syntactic >> constructs it >> uses is fallacious. > > For you to reject my rebuttal of your obviously abstract example by saying my rebuttal is too abstract is also fallacious. *sigh* The original argument was "Here is an API design, A, that you can't express elegantly with @property alone". Your rebuttal was "Well, your abstracted example is obviously of Poor Design (TM), you should use B instead. Therefore, this 'issue' with @property, isn't really an issue". Your rebuttal introduced a new argument (that A was bad design, so use B instead) and I was pointing out the flaws in you making that argument based on abstracted information. The original argument was never about the 'goodness' of A, it was about the ability to express A in the first place; the biggest flaw in your argument is that the ability to express 'poor' design should warrant the removal of the ability to express it. > David did bring up his library, and it works pretty well with the dual property/function API, but there are some parts that could be used incorrectly, and I pointed those out. > >>> my solution: >>> >>> define a setProp1, setProp2, and setProp3 without property >>> semantics. It >> also reads better: >>> >>> auto b = a.setProp1(5).setProp2(6).setProp3(7); >> >> However, in the real API, setProp is a strictly inferior design. In >> point of >> fact, your proposed solution suffers from several issues: >> >> 1) It isn't DRY (Don't Repeat Yourself). Not only do you have simple >> code duplication, but you also have to maintain equality of >> implementation, not >> just bug updates. > > This isn't quite true, the setX is simply a wrapper for the property setter: > > typeof(this) setX(int newval) { x = newval; return this; } > > This can be reduced to a mixin. > >> 2) You've increased the radius of comprehension of your library. Users >> have >> to remember that both functions do the same thing and have to assign two >> different names to the action in their head and then use them >> appropriately in >> there code, based on the syntax they wish to use. > > No they don't. This is a common argument against larger APIs. I know many many APIs where I don't *know* the whole API, I just know what I need to get my work done, and lookup anything else. If you want to use the setX version all the time, there is no need to learn the property functions. In fact, there is no more learning required for the setX version and the dual property/function version -- both have the same number of API calls. > > Bottom line, increasing the number of documented calls does not make it harder to learn one of those calls. I'm not arguing for or against larger APIs. My issue is with inefficient APIs. With dual property/function I can store a single entity in my mind (or in the docs) and then apply language rules to get 2 API calls. In the alternative, I have to store 3 entities, the two names and their relationship to each other, to get 2 API calls. >> 3) If setProp actually read better in real life, we'd use it. But it >> doesn't, so we aren't. By the way, I would hazard that setX/getX are one >> of the most famous/infamous pieces of API design out there and that >> everyone >> can/does consider it. > > Your opinion. I have used it, and have had no complaints. I'm not sure what you're implying/responding to here. My response was was that I (and almost everyone else) has used setX/getX at some point and always considers whether its the best choice for job (or not) and that implying that we didn't consider it during API design seems a bit condescending, nor is it really helpful to the discussion. [snip] >>> Without strict properties, the author of the code cannot enforce usage >> semantics, and therefore, will lead to ambiguities. The only exception >> I can >> see is calling a function with no arguments which returns void without >> the >> parentheses. It cannot be mistaken for a property getter, because it >> can't >> be used as a getter. >> >> Could you please present an example of an ambiguity in the general >> case? (i.e. >> excluding assignment to const/immutable/(static pure) functions and the >> delegate() foo(){}) > > I mean semantic ambiguity, not compiler ambiguity. I mean, the choice of how the symbol can be used makes the symbol name ambiguous where it wouldn't be if the usage was locked to one way or the other. Sorry, I was implying examples of semantic ambiguity with my request. |
April 26, 2011 [phobos] Time to get ready for the next release | ||||
---|---|---|---|---|
| ||||
Posted in reply to Steve Schveighoffer | On Mon, 25 Apr 2011 09:47:32 -0400, Steve Schveighoffer <schveiguy at yahoo.com> wrote:
> ----- Original Message -----
>> From: Robert Jacques <sandford at jhu.edu>
>> To: Steve Schveighoffer <schveiguy at yahoo.com>; Discuss the phobos
>> library for D <phobos at puremagic.com>
>> Cc:
>> Sent: Saturday, April 23, 2011 1:42 PM
>> Subject: Re: [phobos] Time to get ready for the next release
>>
>> On Fri, 22 Apr 2011 15:26:51 -0400, Steve Schveighoffer
>> <schveiguy at yahoo.com> wrote:
>> [snip]
>>> Another analogy I like to draw upon is casing. What if D's casing was
>> insensitive? That is, ReadValue is the same thing as readValue and
>> readvalue .There are probably many people who would love to always use
>> their learned
>> conventions for calling your code (e.g. I always make methods upper
>> case), but
>> then someone comes along and types in reAdvalue (my super-uncreative
>> brain
>> can't come up with a clever example to show something worse, but you
>> get the
>> idea). The name is the same, but the casing makes all the difference to
>> interpreting what it means! Like it or not, the same thing applies to
>> things
>> like:
>>>
>>> writeln = "hello";
>>>
>>> Even though we know this is not the right way to call it, the compiler
>> doesn't give an error to enforce the semantics.
>>
>> The user is aways right. As a library designer, the user is your
>> customer. And
>> if they discover a new (and meaningful to them) way to use your code,
>> take it as
>> a chance to iterate in a new feature (or improve the design if it's a
>> bug).
>
> It's impossible to improve the design when the compiler doesn't let you enforce your design!
>
> And no, the user is not always right. That's why we have the compiler to tell them so ;)
Having D enforce your design, can not _ever_ improve a design. Every single time DMD rapts the user's knuckles enforcing your design, it is an instance where your design failed. It is a safety net. Nothing more.
Have a safety net is important, but the more it extends out beyond the concrete of invalid and meaningless code, the more it encourages mediocre design. And any fall into the safety net still hurts.
|
April 26, 2011 [phobos] Time to get ready for the next release | ||||
---|---|---|---|---|
| ||||
Posted in reply to Steve Schveighoffer | On Mon, 25 Apr 2011 09:45:18 -0400, Steve Schveighoffer <schveiguy at yahoo.com> wrote:
> ----- Original Message -----
>> From: Robert Jacques <sandford at jhu.edu>
>> To: Steve Schveighoffer <schveiguy at yahoo.com>; Discuss the phobos
>> library for D <phobos at puremagic.com>
>> Cc:
>> Sent: Saturday, April 23, 2011 1:28 PM
>> Subject: Re: [phobos] Time to get ready for the next release
>>
>> On Fri, 22 Apr 2011 15:26:51 -0400, Steve Schveighoffer <schveiguy at yahoo.com> wrote:
>>> Actually, I think you are right. I wasn't thinking about dmd erroring
>> on a statement that does nothing. You'd have to assign something to the expression, like:
>>>
>>> auto x = s.seconds = 5;
>>>
>>> Which still looks like it does something else, but is much less
>>> likely to
>> occur.
>>>
>>>
>>> You wouldn't need the parameter to be immutable, because the parameter
>> is a value, making this a strong-pure function.
>>>
>>>
>>> But this is still not an argument against strict properties.
>>
>> You were making an argument for strict properties, via actual bug
>> reports (which
>> I commend you on), and I was making a counter-argument to your argument.
>>
>
> What I meant was, the technicality that the function could be pure, and that would remove the possibility of using it the "wrong way" does not invalidate the property problem. In fact, it does not necessarily invalidate the example, since it is D1 we are talking about here (Tango).
I don't think in terms of a single property problem (I don't even know what you mean by that). I think in terms of many small problems of varying severity. (And this solves the vast majority of the high severity issues) And any solution to the 'property problem' would be D2/D3 only, so I don't know why you're mentioning D1.
|
April 26, 2011 [phobos] Time to get ready for the next release | ||||
---|---|---|---|---|
| ||||
Posted in reply to Steve Schveighoffer | On Mon, 25 Apr 2011 15:46:20 -0400, Steve Schveighoffer <schveiguy at yahoo.com> wrote: >> From: Robert Jacques <sandford at jhu.edu> >> To: Steve Schveighoffer <schveiguy at yahoo.com>; Discuss the phobos >> library for D <phobos at puremagic.com> >> Sent: Monday, April 25, 2011 3:29 PM >> Subject: Re: [phobos] Time to get ready for the next release >> >> On Mon, 25 Apr 2011 09:58:47 -0400, Steve Schveighoffer <schveiguy at yahoo.com> wrote: >>> ----- Original Message ----- >>>> From: Robert Jacques <sandford at jhu.edu> >>>> To: Discuss the phobos library for D <phobos at puremagic.com> >>>> Cc: >>>> Sent: Sunday, April 24, 2011 12:03 PM >>>> Subject: Re: [phobos] Time to get ready for the next release >>>> >>>> On Sun, 24 Apr 2011 07:33:30 -0400, Jacob Carlborg <doob at me.com> wrote: >>> >>>>> If writeln = "foo"; doesn't compile but printf = >>>> "foo"; does then I would consider it not fixed. The way I would want >>>> @property to behave is disallow bar = "foo"; for functions not marked >>>> with @property. But still allow functions not marked with @property >>>> to be >>>> callable without parentheses. >>>> >>>> Also, this and another post have given me an idea: what if >>>> non- at property methods >>>> could be assigned to if and only if a valid 'getter' also existed. >>>> This >>>> would still 'fix' writeln = "foo" but be a less restrictive >>>> than an outright ban. >>> >>> It was an idea that Andrei brought up (before @property syntax was introduced), but I don't think it can be properly enforced: >>> >>> int select(int timeout = 0); // both "getter" and "setter" >>> >>> -Steve >> >> Hmm... good point. Counter-point, select's functions signature is still a 'setter' signature. A 'select' / 'select()' method doesn't exist, it's simply syntactic sugar for select(0). Since a lookup of select by DMD involves the actual function overloads and not their sugary-transforms, it should still detect that select has no zero-arg overload and therefore error on 'select = 5'. > > > Easily worked around: > > int select() { return select(0); } > int select(int timeout) { ... } > > > This seems silly, but it actually is likely to occur with final interface methods that want to enforce what the default select does. That makes sense. Thanks for the example and rational. > But in any case, this is diverging into the obscure. I don't think rules that try to guess what the author is intending are as good as annotations which tell the compiler what the author is intending. Of course not, but there's a reason that most cars are automatics: there is a non-negligible cost to using a heavy annotation based system. The question isn't so much as is one 'better' or not, but is it good enough most of the time? There are problems with using @property alone. There are problem with using Methods-as-Fields alone. This would indicate that some compromise is needed, and therefore every point of the synthesis/trade-off curve is valuable. (Or alternatively that we need to scrape both and start a fresh) |
April 26, 2011 [phobos] Fw: Time to get ready for the next release | ||||
---|---|---|---|---|
| ||||
Posted in reply to Steve Schveighoffer | On Mon, 25 Apr 2011 07:57:45 -0400, Steve Schveighoffer <schveiguy at yahoo.com> wrote: > ----- Forwarded Message ----- >> From: Steve Schveighoffer <schveiguy at yahoo.com> >> To: Robert Jacques <sandford at jhu.edu> >> Cc: >> Sent: Monday, April 25, 2011 7:57 AM >> Subject: Re: [phobos] Time to get ready for the next release >>> From: Robert Jacques <sandford at jhu.edu> >>> To: Steve Schveighoffer <schveiguy at yahoo.com>; Discuss the phobos >> library for D <phobos at puremagic.com> >>> Sent: Saturday, April 23, 2011 1:03 PM >>> Subject: Re: [phobos] Time to get ready for the next release >>> >>> On Fri, 22 Apr 2011 13:22:31 -0400, Steve Schveighoffer >> <schveiguy at yahoo.com> wrote: >>>>> From: Robert Jacques <sandford at jhu.edu> >>> [snip] >>>>> Third, came criticisms of naming a factory method 'seconds' >> in the first place (noun/verb distinctions, etc), and the associative >> criticisms >> of fixing a bad design via a proverbial sledgehammer. >>>> >>>> The goal was to have something short. At the time, I was fighting >>>> for >> changing the time code in Tango, and one of the criticisms was that the >> factory >> method names were too long (don't remember what I originally had). Code >> like Socket.select(5.0) was going to be replaced with >> Socket.select(TimeSpan.seconds(5)). This was sort of a compromise. >> Ironically, >> we had to change it to something more verbose because of this problem. >>> >>> I understand. But I think you side-stepped my actually thought a bit, so let >> me be a bit more verbose. Conciseness and clarity are probably the two >> most at >> odds aspects of any API design. The general conventions are >> variables/fields/'property's should be names and functions/methods >> should be verbs. I have seen many a post in the 'property' discussions >> regarding how verb = value or noun(value) is Evil(TM). Personally, I >> take it as >> a rule of thumb and not as a hard and fast rule. Anyways, any designer >> who uses >> a noun for an action, should be aware that they are actively courting >> the very >> confusion in this bug report. So, from an API design perspective, the >> author >> sacrificed too much clarity for consciousness. And this criticism >> remains true >> even with @property, enabled. Users are still going to try compile >> s.seconds = >> 5, because the API design of 'seconds' differs from the design of its >> class, its module, its library and general expectations. They are >> just going to get a compiler error, instead of a do nothing expression. >> And >> while we deal with non-standard designs all the time, in the form of >> DSLs, >> mini-DSLs and 'by convention' frameworks, a single, non-standard >> function generally only increases a programmer's cognitive load, >> without any >> additional benefits. And proposing the creation/justification of a >> language >> level features in order to mitigate (not fix) a minor issue with an >> API, which >> exists solely due to the API's poor design (which violates its class's >> design guides), is like taking a sledge hammer to kill a fly. >> >> I agree with all of this, actually. I don't love the interface, but it >> was >> a compromise, and as far as intuitiveness goes, it's pretty low on the >> scale. However, you have to pick your battles, and having a much >> better time >> API in tango was better than having the code refused over somewhat >> bikeshed >> issues. >> >> >> I think this was half a victim of D's flawed property design, and half a >> victim of being able to call static functions using an instance for >> namespace.Fix the latter problem in the compiler, and the function can >> only be called with >> the struct name as the namespace. However, it does not remove the >> property >> issue: >> >> TimeSpan.seconds(5); // looks ok to me, not great, but not misleading either.It looks like a constructor, which it actually is. This looks unnatural to me, both as a reviewer and as a coder. The reviewer sorts the meaning quickly enough, after a little inference. But a coder will not naturally write this the first time, assuming they haven't read the manual recently. >> TimeSpan.seconds = 5; // absolutely misleading. And _looks_ absolutely natural ( therefore is absolutely misleading to both coder and reviewer :) ). By the way, a 'bug' like this is a good indication that the user wants a way to set a TimeSpan's span(?) outside of the factory methods (i.e. an additional API feature). >> So I think even though it's somewhat of a combination of issues, the property issue is not invalid. >> >> The issue is more prevalent when talking about ambiguous words, >> especially ones >> that can be both nouns/adjectives and verbs. The issue I have with not >> being >> able to require parentheses is, lack of parentheses evoke an >> expectation of a >> field, either setting or getting that field. Parentheses evoke more of >> an >> action than a field, but that's not as strong, because you can easily >> make a >> function that is a property. Yes, parentheses evoke actions more that data, and I would expect the average coder to use them in a natural (for them) way. Generally, coders do not try to make their code unnatural, except in sport. And differences in 'natural' coding styles (i.e. ambiguities) are perfectly expected. To combat this (among other things), the designer writes documentation. Therefore confusion and misuse should only occur if the user a) hasn't fully read the docs or b) it has been some time since they did read the docs, and they've forgotten pertinent information. And if a user has forgotten what 'seconds' does, they've probably also forgotten whether it's const, or immutable, or pure or @property. Mandating parentheses doesn't help the user remember how to call/use a function, because we Humans remember names extremely well, purpose/behavior decently well and arbitrary tags poorly. >> If you just require @properties to be called without parentheses, it >> doesn't >> solve the largest problem with D's property system -- that is, being >> able to >> omit parentheses for things that should clearly be viewed as actions. :) If it's clearly an action, then shouldn't it have a nice actiony name? :) >> Here is an artificial, but not so improbable example: >> >> stream.read = buf; >> >> what does this mean? Does it mean, set the read buffer to buf? Does >> it mean >> read buf into the stream? I have no idea. Really? I know it supposed to be a synthetic example, but read's a verb. It does stuff. If it was the read buffer, it would be named readBuffer; a noun. >> But this is completely clear: >> >> stream.read(buf); >> >> it means, read the stream data into buf. >> >> However, D treats them both equivalently, and throws no error on the super-confusing usage. You are aware that the 'stream.read = buf;' is logically correct? And that for whoever that wrote it that way, it had to be natural and non-confusing. >> I understand that *most* people won't use it that >> way, but as a library designer, I want to patch all the holes, and make >> the API >> as tight as possible. But a hole isn't _always_ generated by 'unapproved' syntactic usage. A hole only occurs when the syntactic usage causes the code to become meaningless or invalid; and we can patch most (all?) of those. >> I want code that uses the library to read as naturally as >> possible. So I might rename the function "readTo" to ensure the verb >> status of "read." To me, this is a sucky compromise that feels >> artificially created by the compiler. Improving the clarity of your library is a 'sucky compromise'? >> You might have different design goals, and different opinions, but the >> reality >> is, we have to choose one way or the other, we can't have both. Well, >> we >> could have both, if we could annotate both styles, but then the question >> becomes, is it worth complicating the language to have one style over >> the other. >> >> D should either adopt @property as a strict enforcement, or not have it at all.Having it half-ass reflects poorly on the language design. Thank you for your opinion. |
April 26, 2011 [phobos] Time to get ready for the next release | ||||
---|---|---|---|---|
| ||||
Posted in reply to Robert Jacques | I'm consolidating responses here... ----- Original Message ----- > From: Robert Jacques <sandford at jhu.edu> > On Mon, 25 Apr 2011 09:47:32 -0400, Steve Schveighoffer <schveiguy at yahoo.com> wrote: >> It's impossible to improve the design when the compiler doesn't let > you enforce your design! >> >> And no, the user is not always right.? That's why we have the compiler > to tell them so ;) > > Having D enforce your design, can not _ever_ improve a design. Every single time DMD rapts the user's knuckles enforcing your design, it is an instance where your design failed. It is a safety net. Nothing more. Incorrect.? DMD enforces you use the names the author designed, with the capitalization the author designed.? If you do not, it doesn't compile.? It is no different with parentheses, which are part of the name. > Have a safety net is important, but the more it extends out beyond the concrete of invalid and meaningless code, the more it encourages mediocre design. And any fall into the safety net still hurts. It hurts when you're not allowed to write confusing code?? That sounds like a pretty good design to me :) >> What I meant was, the technicality that the function could be pure, and > that would remove the possibility of using it the "wrong way" does not invalidate the property problem.? In fact, it does not necessarily invalidate the example, since it is D1 we are talking about here (Tango). > > I don't think in terms of a single property problem (I don't even know what you mean by that). I think in terms of many small problems of varying severity. (And this solves the vast majority of the high severity issues) And any solution to the 'property problem' would be D2/D3 only, so I don't know why you're mentioning D1. I'm referring to the ability to call any method with the right arguments as properties.? This exists in D1 and currently in D2 (to be deprecated). >> But in any case, this is diverging into the obscure.? I don't think > rules that try to guess what the author is intending are as good as annotations which tell the compiler what the author is intending. > > Of course not, but there's a reason that most cars are automatics: there is a non-negligible cost to using a heavy annotation based system. The question isn't so much as is one 'better' or not, but is it good enough most of the time? Most of the time isn't good enough.? Is it ok if the compiler's code generation works "most of the time"?? If the compiler allows me to get the design I want most of the time, and other times it's impossible, that is a half-ass flawed design, and it's actually worse than not having @property at all. > There are problems with using @property alone. There are problem with using Methods-as-Fields alone. This would indicate that some compromise is needed, and therefore every point of the synthesis/trade-off curve is valuable. (Or alternatively that we need to scrape both and start a fresh) But the problem with enforcing @property is just that you don't want to name your functions differently from the properties.? It's not that the functionality cannot be had.? Essentially, it's a bikeshed problem, not a real problem. >> For you to reject my rebuttal of your obviously abstract example by saying > my rebuttal is too abstract is also fallacious. > > *sigh* The original argument was "Here is an API design, A, that you can't express elegantly with @property alone". Your rebuttal was "Well, your abstracted example is obviously of Poor Design (TM), you should use B instead. Therefore, this 'issue' with @property, isn't really an issue". Your rebuttal introduced a new argument (that A was bad design, so use B instead) and I was pointing out the flaws in you making that argument based on abstracted information. The original argument was never about the 'goodness' of A, it was about the ability to express A in the first place; the biggest flaw in your argument is that the ability to express 'poor' design should warrant the removal of the ability to express it. *double sigh* So essentially, you put forth an argument showing how you are correct using abstract data, and you reject my rebuttal that uses the same abstract data because... it's abstract?? Give me a break!? Is your argument impervious to attack then, because it's abstract? In all concrete examples that exist, if this compiles: a = b.x = c; Where x is a property name, I would expect a == c.? Any reasonable programmer would. Look, the problem is not about what you are thinking when you write it, it's what you are thinking when you read it.? Essentially, your design promotes confusing/unreviewable code, i.e. it's design could be improved. >> No they don't.? This is a common argument against larger APIs.? I know > many many APIs where I don't *know* the whole API, I just know what I need to get my work done, and lookup anything else.? If you want to use the setX version all the time, there is no need to learn the property functions.? In fact, there is no more learning required for the setX version and the dual property/function version -- both have the same number of API calls. >> >> Bottom line, increasing the number of documented calls does not make it > harder to learn one of those calls. > > I'm not arguing for or against larger APIs. My issue is with inefficient APIs. With dual property/function I can store a single entity in my mind (or in the docs) and then apply language rules to get 2 API calls. In the alternative, I have to store 3 entities, the two names and their relationship to each other, to get 2 API calls. No you don't.? You only have to store the relationship "if I want to use this as a property, use x.? If I want to use it using fluent programming, use setX()", then you just have to remember the names for the properties.? It's no different than the relationship "If I want to use this as a property, use x.? If I want to use it using fluent programming use x()".? You actually will not notice a difference, I guarantee your brain will not run out of space because of this :) The docs can state the same relationship in one spot, or just use ///ditto to group both the property and the setter together. > >>> 3) If setProp actually read better in real life, we'd use it. But > it >>> doesn't, so we aren't. By the way, I would hazard that > setX/getX are one >>> of the most famous/infamous pieces of API design out there and that > everyone >>> can/does consider it. >> >> Your opinion.? I have used it, and have had no complaints. > > I'm not sure what you're implying/responding to here. I was responding to "If setProp actually read better in real life, we'd use it.? But it doesn't, so we aren't."? Sorry, did you think that was a fact and not an opinion? > My response was was that I (and almost everyone else) has used setX/getX at some point and always considers whether its the best choice for job (or not) and that implying that we didn't consider it during API design seems a bit condescending, nor is it really helpful to the discussion. I do not know your reasons for your design.? All I can guess is that you did not consider the possibilities for abuse, or the confusion of the reviewer, or thought they were not important, and thought "hey, I can do two things with one function! neat!"? It's a classic folly of software developers not considering the usability of what they are writing.? It is why you have people who aren't software engineers design or review UIs. >>>> ? Without strict properties, the author of the code cannot enforce > usage >>> semantics, and therefore, will lead to ambiguities.? The only exception > I can >>> see is calling a function with no arguments which returns void without > the >>> parentheses.? It cannot be mistaken for a property getter, because it > can't >>> be used as a getter. >>> >>> Could you please present an example of an ambiguity in the general > case? (i.e. >>> excluding assignment to const/immutable/(static pure) functions and the >>> delegate() foo(){}) >> >> I mean semantic ambiguity, not compiler ambiguity.? I mean, the choice of > how the symbol can be used makes the symbol name ambiguous where it wouldn't be if the usage was locked to one way or the other. > > Sorry, I was implying examples of semantic ambiguity with my request. OK, when you referred to the delegate thing, I thought you meant functional ambiguity. Any term which could be confused as an action or a property.? For instance backup.? backup can be a noun ("do you have the backup?"), or it could be a verb ("did you backup the system?") So if I have a backup function, and you use it as a property, what happens: auto x = y.backup; Is x now a backup copy of y, or is it a return value from some backup process?? Contrast that with the function style: auto x = y.backup(); it definitely reads more like backup is doing something on y (like backing it up) and x is some sort of status. ? >>> TimeSpan.seconds(5); // looks ok to me, not great, but not misleading > either.It looks like a constructor, which it actually is. > > This looks unnatural to me, both as a reviewer and as a coder. The reviewer sorts the meaning quickly enough, after a little inference. But a coder will not naturally write this the first time, assuming they haven't read the manual recently. As a constructor it looks unnatural?? How would you expect a constructor to look? > >>> TimeSpan.seconds = 5; // absolutely misleading. > > And _looks_ absolutely natural ( therefore is absolutely misleading to both coder and reviewer :) ). By the way, a 'bug' like this is a good indication that the user wants a way to set a TimeSpan's span(?) outside of the factory methods (i.e. an additional API feature). The problem is, I can only "solve" this bug by changing the name of seconds to be more explicit, I can't disallow this code. >>> So I think even though it's somewhat of a combination of issues, > the >>> property issue is not invalid. >>> >>> The issue is more prevalent when talking about ambiguous words, > especially ones >>> that can be both nouns/adjectives and verbs.? The issue I have with not > being >>> able to require parentheses is, lack of parentheses evoke an > expectation of a >>> field, either setting or getting that field.? Parentheses evoke more of > an >>> action than a field, but that's not as strong, because you can > easily make a >>> function that is a property. > > Yes, parentheses evoke actions more that data, and I would expect the average coder to use them in a natural (for them) way. Generally, coders do not try to make their code unnatural, except in sport. And differences in 'natural' coding styles (i.e. ambiguities) are perfectly expected. To combat this (among other things), the designer writes documentation. Therefore confusion and misuse should only occur if the user a) hasn't fully read the docs or b) it has been some time since they did read the docs, and they've forgotten pertinent information. And if a user has forgotten what 'seconds' does, they've probably also forgotten whether it's const, or immutable, or pure or @property. Mandating parentheses doesn't help the user remember how to call/use a function, because we Humans remember names extremely well, purpose/behavior decently well and arbitrary tags poorly. Again, the enforcement is for the reader of the code, not the writer.? If you had no idea what TimeSpan.seconds(5) does, you would probably know what it means if you saw: auto timeout = TimeSpan.seconds(5); It's self-documenting. > >>> If you just require @properties to be called without parentheses, it > doesn't >>> solve the largest problem with D's property system -- that is, > being able to >>> omit parentheses for things that should clearly be viewed as actions. > > :) If it's clearly an action, then shouldn't it have a nice actiony name? :) constructors usually don't have actiony names, they are named after the type they are constructing.? Factory methods are sort of a hybrid between actions and constructors, so I think it's ok to have nouns as factory method names.? But this is part of the design decision I should be able to make.? It should not be enforced on me to change the name because the language allows someone to misuse the library. > >>> Here is an artificial, but not so improbable example: >>> >>> stream.read = buf; >>> >>> what does this mean?? Does it mean, set the read buffer to buf?? Does > it mean >>> read buf into the stream?? I have no idea. > > Really? I know it supposed to be a synthetic example, but read's a verb. It does stuff. If it was the read buffer, it would be named readBuffer; a noun. read (pronounced like 'red') is also an adjective, which can be a property: book.read = true; But even readBuffer could be an action (read the buffer) or a noun? (the read buffer) :) >>> But this is completely clear: >>> >>> stream.read(buf); >>> >>> it means, read the stream data into buf. >>> >>> However, D treats them both equivalently, and throws no error on the >>> super-confusing usage. > > You are aware that the 'stream.read = buf;' is logically correct? And that for whoever that wrote it that way, it had to be natural and non-confusing. I wouldn't be so sure.? If I wrote that, thinking I was clever, and then went back to read it, I'd have to look stuff up again.? And then when I figured it out, I'd change it to the other style. But in any case, it's the reader that I'm concerned about, not the writer.? The strict enforcement of properties helps the writer create maintainable code.? If you want to use a language that helps you write unmaintainable code, there's always perl :) >>> I understand that *most* people won't use it that >>> way, but as a library designer, I want to patch all the holes, and make > the API >>> as tight as possible. > > But a hole isn't _always_ generated by 'unapproved' syntactic usage. A hole only occurs when the syntactic usage causes the code to become meaningless or invalid; and we can patch most (all?) of those. The translation of the code into binary form is not what's at stake here, it's the human interpretation of it.? A good design takes both into account. > >>> I want code that uses the library to read as naturally as >>> possible.? So I might rename the function "readTo" to ensure > the verb >>> status of "read."? To me, this is a sucky compromise that > feels >>> artificially created by the compiler. > > Improving the clarity of your library is a 'sucky compromise'? If the library does not allow: read = buffer; then read is clear enough, because read(buffer) is clear. The point is, the loose properties are forcing me to make something that's already clear in one context less clear in another context to discourage usage.? I want to make the user only call read using parentheses, I'd rather just have the compiler make them do it, not have to "hint" at it using a more verbose term. -Steve |
April 26, 2011 [phobos] Time to get ready for the next release | ||||
---|---|---|---|---|
| ||||
Posted in reply to David Simcha | >________________________________
>From: David Simcha <dsimcha at gmail.com>
>To: Discuss the phobos library for D <phobos at puremagic.com>
>Sent: Friday, April 22, 2011 6:58 PM
>Subject: Re: [phobos] Time to get ready for the next release
>
>
> On 4/22/2011 5:05 PM, Steve Schveighoffer wrote:
>> How do you automate 4182?? That is, how does the wrapper know that I want the result to be covariant in derived classes?
>>
>>
> Right, I agree with you that this is a bug and should be fixed.? I am not sure, however, how it relates to the operator overloading issue.
OK, I looked over it again, and it's actually a bug that affects my workaround for the other bug.? Essentially, in order to work around the fact that I can't have templates in my interfaces, I aliased the virtual functions to opCat, etc.? But that did not pass on the covariance.
However, thinking about it, it would be nice if I could simply alias the operators to virtual functions.? Hm...? Would something like this make sense?
class C
{
?? C opCatAssign(C other) {...}
?? template opOpAssign(string s) if s == "~="
?? {
?????? alias opCatAssign opOpAssign;
?? }
}
This would be awesome, because then we have a way to get all our virtual functions without having to incur template bloat, or rely on inlining.? I can see how this would be useful for opDispatch.
It would also make for a super-easy mixin implementation.? In fact, you wouldn't need to deal with parameters at all.
But in this case, 4182 would need to be fixed ;)
I need to file another enhancement request...
-Steve
|
Copyright © 1999-2021 by the D Language Foundation