www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.announce - Revised RFC on range design for D2

reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
In wake of the many excellent comments and suggestions made here, I made 
one more pass through the draft proposal for ranges.

http://ssli.ee.washington.edu/~aalexand/d/tmp/std_range.html

There are some comments in red illustrating some uncertainties (not 
all), and the names of the primitives have been updated. Bicycle shed 
galore! But don't forget to comment on the reactor as well :o).


Andrei
Sep 11 2008
next sibling parent reply Benji Smith <dlanguage benjismith.net> writes:
Andrei Alexandrescu wrote:
 In wake of the many excellent comments and suggestions made here, I made 
 one more pass through the draft proposal for ranges.
 
 http://ssli.ee.washington.edu/~aalexand/d/tmp/std_range.html
 
 There are some comments in red illustrating some uncertainties (not 
 all), and the names of the primitives have been updated. Bicycle shed 
 galore! But don't forget to comment on the reactor as well :o).

Well done. I think the design has come together very nicely. I'm especially happy with all the new names, which make a big difference for me in being able to visualize how the proposal works. The old names (fromLeft, etc) were very opaque to me. (btw: head & toe? i love it!) In its current state, this actually gets me pretty excited about D2. Maybe even enough to finally slog my way through all the const stuff. One tiny typo, though: In the "Forward range" section, the code sample still uses "left" instead of "head". And, there are two sections (which I think are related to one another) that left me scratching my head: Input Ranges: "In case ElementType!(R) has aliases (such as a reference, pointer, or array type), the iterator is free to recycle it it upon the call to r.next, so client code must do a deep copy if preserving the value is needed. [NOTE: This is a weakness in the design. A better way of figuring recycling should be found.] The call is defined only right after r.done returned false." Forward Ranges: "Also, forward ranges iterate over "real" elements, which makes in-place mutation of r.head possible." Those paragraphs are completely greek to me. Can you elaborate? --benji
Sep 11 2008
parent reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
Benji Smith wrote:
 Andrei Alexandrescu wrote:
 In wake of the many excellent comments and suggestions made here, I 
 made one more pass through the draft proposal for ranges.

 http://ssli.ee.washington.edu/~aalexand/d/tmp/std_range.html

 There are some comments in red illustrating some uncertainties (not 
 all), and the names of the primitives have been updated. Bicycle shed 
 galore! But don't forget to comment on the reactor as well :o).

Well done. I think the design has come together very nicely. I'm especially happy with all the new names, which make a big difference for me in being able to visualize how the proposal works. The old names (fromLeft, etc) were very opaque to me. (btw: head & toe? i love it!) In its current state, this actually gets me pretty excited about D2. Maybe even enough to finally slog my way through all the const stuff. One tiny typo, though: In the "Forward range" section, the code sample still uses "left" instead of "head".

Fixed, thanks.
 And, there are two sections (which I think are related to one another) 
 that left me scratching my head:
 
 Input Ranges:
 "In case ElementType!(R) has aliases (such as a reference, pointer, or 
 array type), the iterator is free to recycle it it upon the call to 
 r.next, so client code must do a deep copy if preserving the value is 
 needed. [NOTE: This is a weakness in the design. A better way of 
 figuring recycling should be found.] The call is defined only right 
 after r.done returned false."
 
 Forward Ranges:
 "Also, forward ranges iterate over "real" elements, which makes in-place 
 mutation of r.head possible."
 
 Those paragraphs are completely greek to me. Can you elaborate?

Consider iterating over an array of int[], specifically an int[][]. Then when you call r.head you see the current int[]. You can store it and use it a few steps later or even after the iteration is done for as long as the array is around. Contrast that with iterating a file containing lines of integers. The file has a buffer of type int[]. Every time you call r.next, the file range reads a new line, parses the integers, and REFILLS the array with them. If if generated a new array every line, that would cause a great deal of allocations. Same about the in-place thing. If you modify elements in the array, they stay modified. If you modify the buffer of a file, your changes get overwritten upon r.next. Andrei
Sep 12 2008
next sibling parent Benji Smith <dlanguage benjismith.net> writes:
Andrei Alexandrescu wrote:
 Consider iterating over an array of int[], specifically an int[][]. Then 
 when you call r.head you see the current int[]. You can store it and use 
 it a few steps later or even after the iteration is done for as long as 
 the array is around.
 
 Contrast that with iterating a file containing lines of integers. The 
 file has a buffer of type int[]. Every time you call r.next, the file 
 range reads a new line, parses the integers, and REFILLS the array with 
 them. If if generated a new array every line, that would cause a great 
 deal of allocations.

Gotcha. I understand now. Thinking off the top of my head, though, is it actually necessary define input ranges as reusing the same array? Wouldn't a peephole optimizer get rid of those extra allocations anyhow? --benji
Sep 12 2008
prev sibling parent Bruno Medeiros <brunodomedeiros+spam com.gmail> writes:
Andrei Alexandrescu wrote:
 Benji Smith wrote:
 Andrei Alexandrescu wrote:
 In wake of the many excellent comments and suggestions made here, I 
 made one more pass through the draft proposal for ranges.

 http://ssli.ee.washington.edu/~aalexand/d/tmp/std_range.html

 There are some comments in red illustrating some uncertainties (not 
 all), and the names of the primitives have been updated. Bicycle shed 
 galore! But don't forget to comment on the reactor as well :o).

Well done. I think the design has come together very nicely. I'm especially happy with all the new names, which make a big difference for me in being able to visualize how the proposal works. The old names (fromLeft, etc) were very opaque to me. (btw: head & toe? i love it!) In its current state, this actually gets me pretty excited about D2. Maybe even enough to finally slog my way through all the const stuff. One tiny typo, though: In the "Forward range" section, the code sample still uses "left" instead of "head".

Fixed, thanks.
 And, there are two sections (which I think are related to one another) 
 that left me scratching my head:

 Input Ranges:
 "In case ElementType!(R) has aliases (such as a reference, pointer, or 
 array type), the iterator is free to recycle it it upon the call to 
 r.next, so client code must do a deep copy if preserving the value is 
 needed. [NOTE: This is a weakness in the design. A better way of 
 figuring recycling should be found.] The call is defined only right 
 after r.done returned false."

 Forward Ranges:
 "Also, forward ranges iterate over "real" elements, which makes 
 in-place mutation of r.head possible."

 Those paragraphs are completely greek to me. Can you elaborate?

Consider iterating over an array of int[], specifically an int[][]. Then when you call r.head you see the current int[]. You can store it and use it a few steps later or even after the iteration is done for as long as the array is around. Contrast that with iterating a file containing lines of integers. The file has a buffer of type int[]. Every time you call r.next, the file range reads a new line, parses the integers, and REFILLS the array with them. If if generated a new array every line, that would cause a great deal of allocations. Same about the in-place thing. If you modify elements in the array, they stay modified. If you modify the buffer of a file, your changes get overwritten upon r.next. Andrei

How about having the element type of the file-containing-lines-of-integers range be const(int[]) instead of int[]? I think it would solve the ownership problem, but it might introduce other problems, such as, how to make InputRange!(int[]) be covariant with InputRange!(const(int[]). -- Bruno Medeiros - Software Developer, MSc. in CS/E graduate http://www.prowiki.org/wiki4d/wiki.cgi?BrunoMedeiros#D
Sep 25 2008
prev sibling next sibling parent Sergey Gromov <snake.scaly gmail.com> writes:
Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> wrote:
 In wake of the many excellent comments and suggestions made here, I made 
 one more pass through the draft proposal for ranges.
 
 http://ssli.ee.washington.edu/~aalexand/d/tmp/std_range.html
 
 There are some comments in red illustrating some uncertainties (not 
 all), and the names of the primitives have been updated. Bicycle shed 
 galore! But don't forget to comment on the reactor as well :o).

I propose to finally move to D group from D.announce. The best way would be to post your announcement there.
Sep 12 2008
prev sibling next sibling parent reply "Bill Baxter" <wbaxter gmail.com> writes:
On Fri, Sep 12, 2008 at 2:44 PM, Andrei Alexandrescu
<SeeWebsiteForEmail erdani.org> wrote:
 In wake of the many excellent comments and suggestions made here, I made one
 more pass through the draft proposal for ranges.

 http://ssli.ee.washington.edu/~aalexand/d/tmp/std_range.html

 There are some comments in red illustrating some uncertainties (not all),
 and the names of the primitives have been updated. Bicycle shed galore! But
 don't forget to comment on the reactor as well :o).

Reactor? You meant "refactor" maybe? --bb
Sep 12 2008
parent Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
Bill Baxter wrote:
 On Fri, Sep 12, 2008 at 2:44 PM, Andrei Alexandrescu
 <SeeWebsiteForEmail erdani.org> wrote:
 In wake of the many excellent comments and suggestions made here, I made one
 more pass through the draft proposal for ranges.

 http://ssli.ee.washington.edu/~aalexand/d/tmp/std_range.html

 There are some comments in red illustrating some uncertainties (not all),
 and the names of the primitives have been updated. Bicycle shed galore! But
 don't forget to comment on the reactor as well :o).

Reactor? You meant "refactor" maybe?

I think the original bicicle shed story was about a nuclear plant design including a bicycle shed. So I meant to ask you to comment about the nuclear reactor, not only the bicycle shed. Andrei
Sep 12 2008
prev sibling next sibling parent reply bearophile <bearophileHUGS lycos.com> writes:
Andrei Alexandrescu:

In some situations (longer functions, where there are 2 or more types, etc) It
may be better to use longer & more descriptive names for types, instead of E T
etc.

Bye,
bearophile
Sep 12 2008
parent Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
bearophile wrote:
 Andrei Alexandrescu:
 
 In some situations (longer functions, where there are 2 or more types, etc) It
may be better to use longer & more descriptive names for types, instead of E T
etc.
 
 Bye,
 bearophile

Fixed, thanks. Andrei
Sep 12 2008
prev sibling next sibling parent reply Pablo Ripolles <in-call gmx.net> writes:
Andrei Alexandrescu Wrote:

 In wake of the many excellent comments and suggestions made here, I made 
 one more pass through the draft proposal for ranges.
 
 http://ssli.ee.washington.edu/~aalexand/d/tmp/std_range.html
 
 There are some comments in red illustrating some uncertainties (not 
 all), and the names of the primitives have been updated. Bicycle shed 
 galore! But don't forget to comment on the reactor as well :o).
 
 
 Andrei

Well, it looks prety clean! :D However, I'm not completely sure I like these "head" and "toe" names selection. It projects to much on it, doesn't it? couldn't it be more neutral? perhaps more conceptual? I haven't been able to read the last days' comments... but my last impressions were that this "head" was not the best choice. If "head" is the header item, why not call it "header"? If ''toe" is the last item, why not call it "last"? Other comment goes for the "done" property, for the seek of consistence shouldn't it better be named "isDone"? Cheers!
Sep 12 2008
next sibling parent reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
Pablo Ripolles wrote:
 Andrei Alexandrescu Wrote:
 
 In wake of the many excellent comments and suggestions made here, I made 
 one more pass through the draft proposal for ranges.

 http://ssli.ee.washington.edu/~aalexand/d/tmp/std_range.html

 There are some comments in red illustrating some uncertainties (not 
 all), and the names of the primitives have been updated. Bicycle shed 
 galore! But don't forget to comment on the reactor as well :o).


 Andrei

Well, it looks prety clean! :D However, I'm not completely sure I like these "head" and "toe" names selection. It projects to much on it, doesn't it? couldn't it be more neutral? perhaps more conceptual? I haven't been able to read the last days' comments... but my last impressions were that this "head" was not the best choice. If "head" is the header item, why not call it "header"? If ''toe" is the last item, why not call it "last"? Other comment goes for the "done" property, for the seek of consistence shouldn't it better be named "isDone"? Cheers!

Thanks. One problem in coding with first and last was that sometimes the code looks unnatural, especially when your range exposes a few more functions. In a stream parser, dealing with the "first" element is not the most natural way to think of it. But I agree that first and last are definitely palatable and natural most of the time. But then again, shouldn't any design have the inevitable cutesy that makes it memorable? :o) Andrei
Sep 12 2008
next sibling parent reply Pablo Ripolles <in-call gmx.net> writes:
Andrei Alexandrescu Wrote:

 Pablo Ripolles wrote:
 Andrei Alexandrescu Wrote:
 
 In wake of the many excellent comments and suggestions made here, I made 
 one more pass through the draft proposal for ranges.

 http://ssli.ee.washington.edu/~aalexand/d/tmp/std_range.html

 There are some comments in red illustrating some uncertainties (not 
 all), and the names of the primitives have been updated. Bicycle shed 
 galore! But don't forget to comment on the reactor as well :o).


 Andrei

Well, it looks prety clean! :D However, I'm not completely sure I like these "head" and "toe" names selection. It projects to much on it, doesn't it? couldn't it be more neutral? perhaps more conceptual? I haven't been able to read the last days' comments... but my last impressions were that this "head" was not the best choice. If "head" is the header item, why not call it "header"? If ''toe" is the last item, why not call it "last"? Other comment goes for the "done" property, for the seek of consistence shouldn't it better be named "isDone"? Cheers!

Thanks. One problem in coding with first and last was that sometimes the code looks unnatural, especially when your range exposes a few more functions. In a stream parser, dealing with the "first" element is not the most natural way to think of it. But I agree that first and last are definitely palatable and natural most of the time. But then again, shouldn't any design have the inevitable cutesy that makes it memorable? :o) Andrei

Well, in the first place thanks to you! I really enjoy this enthusiastic attitude. About "first", well, I didn't mentioned "first" as an alternative to "head" but "header" which is not necessarily an animal-like characteristic. Anyway, "head" is definitely more poly-meaning than "toe"!!!! I definitely would look for neutral alternative... What about "isDone"? And yes, to have a personality in the naming is nice but I guess that in this case seems too concrete, anthropocentric, I don't know, what the heck! even "tail" is semantically wider! Keep this great interaction! Thanks!
Sep 12 2008
parent reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
Pablo Ripolles wrote:
 What about "isDone"?

isDone is great, I just wanted to keep the one-word streak going. Let's see what everyone else says. Andrei
Sep 12 2008
next sibling parent reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
Bill Baxter wrote:
 On Fri, Sep 12, 2008 at 11:39 PM, Andrei Alexandrescu
 <SeeWebsiteForEmail erdani.org> wrote:
 Pablo Ripolles wrote:
 What about "isDone"?

what everyone else says.

Hmm. std.algorithm does have an "isSorted" function. So I guess I agree it would be more consistent if you call it isDone or isEmpty. Or rename "isSorted" to "sorted". :-) But then you have to face the consequences later when you want to have a predicate that is ambiguous without the "is". Probably a lot of noun predicates are in that category -- i.e. checking isSomeNoun(x). Like "isRange(x)" to see if x is a range. That would have to just become "range(x)" which is a bit ambiguous. So I agree. Stick the "is" in there.

Thing is, people will call isSorted much less often than (isD|d)one. In std.algorithm clearly the one-word paradigm can't scale. But for a handful of heavily-used names I'd be willing to take the Pepsi challenge. Andrei P.S. The more I think of it, the more I like "tip" of the range. Short, poignant, easy to remember. Not pressing the red button just yet.
Sep 12 2008
next sibling parent reply Pablo Ripolles <in-call gmx.net> writes:
Andrei Alexandrescu Wrote:

 Bill Baxter wrote:
 On Fri, Sep 12, 2008 at 11:39 PM, Andrei Alexandrescu
 <SeeWebsiteForEmail erdani.org> wrote:
 Pablo Ripolles wrote:
 What about "isDone"?

what everyone else says.

Hmm. std.algorithm does have an "isSorted" function. So I guess I agree it would be more consistent if you call it isDone or isEmpty. Or rename "isSorted" to "sorted". :-) But then you have to face the consequences later when you want to have a predicate that is ambiguous without the "is". Probably a lot of noun predicates are in that category -- i.e. checking isSomeNoun(x). Like "isRange(x)" to see if x is a range. That would have to just become "range(x)" which is a bit ambiguous. So I agree. Stick the "is" in there.

Thing is, people will call isSorted much less often than (isD|d)one. In std.algorithm clearly the one-word paradigm can't scale. But for a handful of heavily-used names I'd be willing to take the Pepsi challenge. Andrei P.S. The more I think of it, the more I like "tip" of the range. Short, poignant, easy to remember. Not pressing the red button just yet.

brilliant! I think "tip" is just fantastic! "head" and "tip" wonderful couple. yes, *this* one is as neutral as it can be! besides, the tip of something is unique! if you hold something by its toe, which one of how many are you holding? :) when I name boolean identifiers, I kind follow these "rules": *properties: the instance is the grammatical subject, so it is very natural to use "is" as in self.isDone *scalar variables: I tend to append the "whether" as in whetherDone *array variables: here you have that the index to access the value is related to the grammatical subject, then I use something as visited[i] *functions: here you have that the argument to be passed to the function might be the grammatical subject, then I use something as searched(x) or pointIsInCell(P, C) Its always nice that a std library is as regular as it can be, perhaps as your own library. for special, particular words we already have the keywords, right? Cheers!
Sep 12 2008
parent reply KennyTM~ <kennytm gmail.com> writes:
Pablo Ripolles wrote:
 Andrei Alexandrescu Wrote:
 
 Bill Baxter wrote:

 -- snip --

 Andrei

 P.S. The more I think of it, the more I like "tip" of the range. Short, 
 poignant, easy to remember. Not pressing the red button just yet.

brilliant! I think "tip" is just fantastic! "head" and "tip" wonderful couple. yes, *this* one is as neutral as it can be! besides, the tip of something is unique! if you hold something by its toe, which one of how many are you holding? :) -- snip -- Cheers!

I don't think people know what rng.tip means at the first glance. I interpret it as a sharp, pointy object, and that makes no sense for an iterator/range. I still favor "traditional" names e.g. ".tail", ".back" or ".last". Cheers, Kenny. P.S. ".retreat" is a very nice name. I like it. :)
Sep 13 2008
parent Pablo Ripolles <in-call gmx.net> writes:
KennyTM~ Wrote:

 Pablo Ripolles wrote:
 Andrei Alexandrescu Wrote:
 
 Bill Baxter wrote:


>> -- snip --
 Andrei

 P.S. The more I think of it, the more I like "tip" of the range. Short, 
 poignant, easy to remember. Not pressing the red button just yet.

brilliant! I think "tip" is just fantastic! "head" and "tip" wonderful couple. yes, *this* one is as neutral as it can be! besides, the tip of something is unique! if you hold something by its toe, which one of how many are you holding? :) -- snip -- Cheers!

I don't think people know what rng.tip means at the first glance. I interpret it as a sharp, pointy object, and that makes no sense for an iterator/range. I still favor "traditional" names e.g. ".tail", ".back" or ".last".

Well, me too although I misinterpreted as an alternative to .toe which wasn't the intent anyway... :$ So, yes, I agree .tip is not a nice option, however not for its "sharpness"... Cheers!
Sep 14 2008
prev sibling next sibling parent reply Pablo Ripolles <in-call gmx.net> writes:
Bill Baxter Wrote:

 On Sat, Sep 13, 2008 at 12:03 AM, Andrei Alexandrescu
 <SeeWebsiteForEmail erdani.org> wrote:
 Bill Baxter wrote:
 On Fri, Sep 12, 2008 at 11:39 PM, Andrei Alexandrescu
 <SeeWebsiteForEmail erdani.org> wrote:
 Pablo Ripolles wrote:
 What about "isDone"?

isDone is great, I just wanted to keep the one-word streak going. Let's see what everyone else says.

Hmm. std.algorithm does have an "isSorted" function. So I guess I agree it would be more consistent if you call it isDone or isEmpty. Or rename "isSorted" to "sorted". :-) But then you have to face the consequences later when you want to have a predicate that is ambiguous without the "is". Probably a lot of noun predicates are in that category -- i.e. checking isSomeNoun(x). Like "isRange(x)" to see if x is a range. That would have to just become "range(x)" which is a bit ambiguous. So I agree. Stick the "is" in there.

Thing is, people will call isSorted much less often than (isD|d)one. In std.algorithm clearly the one-word paradigm can't scale. But for a handful of heavily-used names I'd be willing to take the Pepsi challenge. Andrei P.S. The more I think of it, the more I like "tip" of the range. Short, poignant, easy to remember. Not pressing the red button just yet.

Hmm. One semantic issue I have is that the tip usually refers to the infinitessimal point at the end. Not a thing with substance. I'm having trouble feeling like I'm going to get an item back when I look at "x.tip". Head has huge history being used for the item at the front of a list, so I think that's much less likely to cause anyone looking at D code to scratch their heads. It will be obvious what it means even in relative isolation. head/tip will often appear without "toe" in forward range algos. So you need to be able to easily recognize what "tip" means without seeing that "toe" to give context. Toe on the other hand will probably almost always appear with his mate. Ooh, another scale thing, but a head is obviously a very different scale than a toe. A foot is closer to the same scale. Maybe head/foot is better than head/toe. The connection between retreating / feet is stronger that retreating / toes, too! --bb

neither the tip of the tail, nor the tip of the wing, nor the tip of the flagellum are really infinitesimal... I'm not sure whether I understand your reasoning about the "tip" / "toe", I interpreted that "tip" could be a substitute of "toe"... my problem with foot is that there is necessarily more than one. perhaps in the world of the anatomy of the chordates we can find something... dunno, "coccyx" is pretty weird but at least there is only one. Cheers!
Sep 12 2008
parent reply Sean Kelly <sean invisibleduck.org> writes:
Bill Baxter wrote:
 On Sat, Sep 13, 2008 at 1:03 AM, Pablo Ripolles <in-call gmx.net> wrote:
 Hmm.  One semantic issue I have is that the tip usually refers to the
 infinitessimal point at the end.  Not a thing with substance.  I'm
 having trouble feeling like I'm going to get an item back when I look
 at "x.tip".  Head has huge history being used for the item at the
 front of a list, so I think that's much less likely to cause anyone
 looking at D code to scratch their heads.  It will be obvious what it
 means even in relative isolation.  head/tip will often appear without
 "toe" in forward range algos.  So you need to be able to easily
 recognize what "tip" means without seeing that "toe" to give context.
 Toe on the other hand will probably almost always appear with his
 mate.

 Ooh, another scale thing, but a head is obviously a very different
 scale than a toe.  A foot is closer to the same scale.  Maybe
 head/foot is better than head/toe.  The connection between retreating
 / feet is stronger that retreating / toes, too!

 --bb

I'm not sure whether I understand your reasoning about the "tip" / "toe", I interpreted that "tip" could be a substitute of "toe"...

Nope. I'm pretty sure that the discussion is about replacing "head" with "tip". There's an expression "from tip to toe".

There's also a song entitled "tiptoe through the tulips," which will probably be stuck in my head for the rest of the day now.
 Well, there is only one tail... but it's what functional guys call
 everything but the head, so Anrdrei wants to avoid it.

And what was wrong with first/last? Sean
Sep 12 2008
next sibling parent reply Jerry Quinn <jlquinn optonline.net> writes:
Sean Kelly Wrote:

 Bill Baxter wrote:
 On Sat, Sep 13, 2008 at 1:03 AM, Pablo Ripolles <in-call gmx.net> wrote:
 Hmm.  One semantic issue I have is that the tip usually refers to the
 infinitessimal point at the end.  Not a thing with substance.  I'm
 having trouble feeling like I'm going to get an item back when I look
 at "x.tip".  Head has huge history being used for the item at the
 front of a list, so I think that's much less likely to cause anyone
 looking at D code to scratch their heads.  It will be obvious what it
 means even in relative isolation.  head/tip will often appear without
 "toe" in forward range algos.  So you need to be able to easily
 recognize what "tip" means without seeing that "toe" to give context.
 Toe on the other hand will probably almost always appear with his
 mate.

 Ooh, another scale thing, but a head is obviously a very different
 scale than a toe.  A foot is closer to the same scale.  Maybe
 head/foot is better than head/toe.  The connection between retreating
 / feet is stronger that retreating / toes, too!

 --bb

I'm not sure whether I understand your reasoning about the "tip" / "toe", I interpreted that "tip" could be a substitute of "toe"...

Nope. I'm pretty sure that the discussion is about replacing "head" with "tip". There's an expression "from tip to toe".

There's also a song entitled "tiptoe through the tulips," which will probably be stuck in my head for the rest of the day now.
 Well, there is only one tail... but it's what functional guys call
 everything but the head, so Anrdrei wants to avoid it.

And what was wrong with first/last?

Another few possibilities fore/aft front/back start/end begin/end
Sep 12 2008
parent downs <default_357-line yahoo.de> writes:
Jerry Quinn wrote:
 Sean Kelly Wrote:
 
 Bill Baxter wrote:
 On Sat, Sep 13, 2008 at 1:03 AM, Pablo Ripolles <in-call gmx.net> wrote:
 Hmm.  One semantic issue I have is that the tip usually refers to the
 infinitessimal point at the end.  Not a thing with substance.  I'm
 having trouble feeling like I'm going to get an item back when I look
 at "x.tip".  Head has huge history being used for the item at the
 front of a list, so I think that's much less likely to cause anyone
 looking at D code to scratch their heads.  It will be obvious what it
 means even in relative isolation.  head/tip will often appear without
 "toe" in forward range algos.  So you need to be able to easily
 recognize what "tip" means without seeing that "toe" to give context.
 Toe on the other hand will probably almost always appear with his
 mate.

 Ooh, another scale thing, but a head is obviously a very different
 scale than a toe.  A foot is closer to the same scale.  Maybe
 head/foot is better than head/toe.  The connection between retreating
 / feet is stronger that retreating / toes, too!

 --bb

I'm not sure whether I understand your reasoning about the "tip" / "toe", I interpreted that "tip" could be a substitute of "toe"...

with "tip". There's an expression "from tip to toe".

probably be stuck in my head for the rest of the day now.
 Well, there is only one tail... but it's what functional guys call
 everything but the head, so Anrdrei wants to avoid it.


Another few possibilities fore/aft front/back start/end begin/end

Bow/stern? Of course, if we ever get a tree implementation, we have to use port/starboard :p
Sep 12 2008
prev sibling parent Sergey Gromov <snake.scaly gmail.com> writes:
Sean Kelly <sean invisibleduck.org> wrote:
 Bill Baxter wrote:
 On Sat, Sep 13, 2008 at 1:03 AM, Pablo Ripolles <in-call gmx.net> wrote:
 Hmm.  One semantic issue I have is that the tip usually refers to the
 infinitessimal point at the end.  Not a thing with substance.  I'm
 having trouble feeling like I'm going to get an item back when I look
 at "x.tip".  Head has huge history being used for the item at the
 front of a list, so I think that's much less likely to cause anyone
 looking at D code to scratch their heads.  It will be obvious what it
 means even in relative isolation.  head/tip will often appear without
 "toe" in forward range algos.  So you need to be able to easily
 recognize what "tip" means without seeing that "toe" to give context.
 Toe on the other hand will probably almost always appear with his
 mate.

 Ooh, another scale thing, but a head is obviously a very different
 scale than a toe.  A foot is closer to the same scale.  Maybe
 head/foot is better than head/toe.  The connection between retreating
 / feet is stronger that retreating / toes, too!

 --bb

I'm not sure whether I understand your reasoning about the "tip" / "toe", I interpreted that "tip" could be a substitute of "toe"...

Nope. I'm pretty sure that the discussion is about replacing "head" with "tip". There's an expression "from tip to toe".

There's also a song entitled "tiptoe through the tulips," which will probably be stuck in my head for the rest of the day now.
 Well, there is only one tail... but it's what functional guys call
 everything but the head, so Anrdrei wants to avoid it.

And what was wrong with first/last?

AFAIK, after Andrei switched to isEmpty/next/first for an Input range some algos started to look ugly to him. I can't remember him mentioning which algorithms made him so upset that he reconsidered the name. Perhaps it was something about input stream where you called its next and then accessed its first... Probably it looked a bit bad. But if we ever get rid of first/head/tip/Obama in Input range then I cannot see a reason why ban first/last. It's the most clean and abstract pair of all the body/airplane/politician parts being proposed.
Sep 13 2008
prev sibling next sibling parent Benji Smith <dlanguage benjismith.net> writes:
Andrei Alexandrescu wrote:
 P.S. The more I think of it, the more I like "tip" of the range. Short, 
 poignant, easy to remember. Not pressing the red button just yet.

When I see "x.tip", it makes me think of the verb ("a function that tips x over"). If I force myself to think of the noun-sense, "tip" is obviously at one of the limits of the range, but is it at the front or the back? I'd have to check the docs to remember. --benji
Sep 12 2008
prev sibling parent reply Pablo Ripolles <in-call gmx.net> writes:
Andrei Alexandrescu Wrote:

 Bill Baxter wrote:
 On Fri, Sep 12, 2008 at 11:39 PM, Andrei Alexandrescu
 <SeeWebsiteForEmail erdani.org> wrote:
 Pablo Ripolles wrote:
 What about "isDone"?

what everyone else says.

Hmm. std.algorithm does have an "isSorted" function. So I guess I agree it would be more consistent if you call it isDone or isEmpty. Or rename "isSorted" to "sorted". :-) But then you have to face the consequences later when you want to have a predicate that is ambiguous without the "is". Probably a lot of noun predicates are in that category -- i.e. checking isSomeNoun(x). Like "isRange(x)" to see if x is a range. That would have to just become "range(x)" which is a bit ambiguous. So I agree. Stick the "is" in there.

Thing is, people will call isSorted much less often than (isD|d)one. In std.algorithm clearly the one-word paradigm can't scale. But for a handful of heavily-used names I'd be willing to take the Pepsi challenge. Andrei P.S. The more I think of it, the more I like "tip" of the range. Short, poignant, easy to remember. Not pressing the red button just yet.

A perhaps more aeronautical term but short and nice as an alternative to "tip" might be "aft". So, what about "aft" instead of "tip"? "head"/"aft" it's not that bad, is it? Cheers!
Sep 12 2008
parent Pablo Ripolles <in-call gmx.net> writes:
Bill Baxter Wrote:

 According to http://en.wikipedia.org/wiki/Aft
 The opposite of aft is forward, pronounced "forrard" for some reason.
 Not good I think.

Well, those are the adjective forms. I was thinking about the aft of an airplane...
 
 I see nothing wrong with head/foot.
 There's "head of a bed / foot of a bed" so it is not the case that
 these words always refer to people or that
 there's always more than one foot.

Ok, that sounds more convincing than "head"/"toe", that's for sure.
 
 ---bb
 
 On Sat, Sep 13, 2008 at 7:12 AM, Pablo Ripolles <in-call gmx.net> wrote:
 Andrei Alexandrescu Wrote:

 Bill Baxter wrote:
 On Fri, Sep 12, 2008 at 11:39 PM, Andrei Alexandrescu
 <SeeWebsiteForEmail erdani.org> wrote:
 Pablo Ripolles wrote:
 What about "isDone"?

what everyone else says.

Hmm. std.algorithm does have an "isSorted" function. So I guess I agree it would be more consistent if you call it isDone or isEmpty. Or rename "isSorted" to "sorted". :-) But then you have to face the consequences later when you want to have a predicate that is ambiguous without the "is". Probably a lot of noun predicates are in that category -- i.e. checking isSomeNoun(x). Like "isRange(x)" to see if x is a range. That would have to just become "range(x)" which is a bit ambiguous. So I agree. Stick the "is" in there.

Thing is, people will call isSorted much less often than (isD|d)one. In std.algorithm clearly the one-word paradigm can't scale. But for a handful of heavily-used names I'd be willing to take the Pepsi challenge. Andrei P.S. The more I think of it, the more I like "tip" of the range. Short, poignant, easy to remember. Not pressing the red button just yet.

A perhaps more aeronautical term but short and nice as an alternative to "tip" might be "aft". So, what about "aft" instead of "tip"? "head"/"aft" it's not that bad, is it? Cheers!


Sep 12 2008
prev sibling next sibling parent reply Leandro Lucarella <llucax gmail.com> writes:
Andrei Alexandrescu, el 12 de septiembre a las 09:39 me escribiste:
 Pablo Ripolles wrote:
What about "isDone"?

isDone is great, I just wanted to keep the one-word streak going. Let's see what everyone else says.

I much prefer done, because is shorter, and, well, maybe because I hate CamelCase too ;) -- Leandro Lucarella (luca) | Blog colectivo: http://www.mazziblog.com.ar/blog/ ---------------------------------------------------------------------------- GPG Key: 5F5A8D05 (F8CD F9A7 BF00 5431 4145 104C 949E BFB6 5F5A 8D05) ---------------------------------------------------------------------------- Creativity is great but plagiarism is faster
Sep 12 2008
next sibling parent Pablo Ripolles <in-call gmx.net> writes:
Leandro Lucarella Wrote:

 Andrei Alexandrescu, el 12 de septiembre a las 09:39 me escribiste:
 Pablo Ripolles wrote:
What about "isDone"?

isDone is great, I just wanted to keep the one-word streak going. Let's see what everyone else says.

I much prefer done, because is shorter, and, well, maybe because I hate CamelCase too ;)

I do not like it either, we already have it though. I definitely prefer CamelCase than inconsistency. Cheers!
Sep 12 2008
prev sibling parent Pablo Ripolles <in-call gmx.net> writes:
Leandro Lucarella Wrote:

 Andrei Alexandrescu, el 12 de septiembre a las 09:39 me escribiste:
 Pablo Ripolles wrote:
What about "isDone"?

isDone is great, I just wanted to keep the one-word streak going. Let's see what everyone else says.

I much prefer done, because is shorter, and, well, maybe because I hate CamelCase too ;)

I do not like it either, we already have it though. I definitely prefer CamelCase than inconsistency. Cheers!
Sep 12 2008
prev sibling parent "Bill Baxter" <wbaxter gmail.com> writes:
On Sat, Sep 13, 2008 at 12:16 PM, downs <default_357-line yahoo.de> wrote:
 Jerry Quinn wrote:
 Sean Kelly Wrote:


 Another few possibilities

 fore/aft
 front/back
 start/end
 begin/end

Bow/stern? Of course, if we ever get a tree implementation, we have to use port/starboard :p

PeanutButter/Jelly? Sonny/Cher? Ooh I know: Obama/McCain! Then we can have fun seeing how far to the left() McCain can go and how far to the right() Obama can! It's a race to see who can get to the middle ground mass appeal positions first! Seriously though, its head/foot. End of story. --bb
Sep 12 2008
prev sibling next sibling parent reply Walter Bright <newshound1 digitalmars.com> writes:
Andrei Alexandrescu wrote:
 Thanks. One problem in coding with first and last was that sometimes the 
 code looks unnatural, especially when your range exposes a few more 
 functions. In a stream parser, dealing with the "first" element is not 
 the most natural way to think of it. But I agree that first and last are 
 definitely palatable and natural most of the time. But then again, 
 shouldn't any design have the inevitable cutesy that makes it memorable? 
 :o)

One consideration when selecting names is how it would interact with google. One advantage to head/toe is there is already head/tail when thinking of a list, and head/toe would fit into that naturally.
Sep 12 2008
parent Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
Walter Bright wrote:
 Andrei Alexandrescu wrote:
 Thanks. One problem in coding with first and last was that sometimes 
 the code looks unnatural, especially when your range exposes a few 
 more functions. In a stream parser, dealing with the "first" element 
 is not the most natural way to think of it. But I agree that first and 
 last are definitely palatable and natural most of the time. But then 
 again, shouldn't any design have the inevitable cutesy that makes it 
 memorable? :o)

One consideration when selecting names is how it would interact with google. One advantage to head/toe is there is already head/tail when thinking of a list, and head/toe would fit into that naturally.

I agree. Sorry tipsters. I think head/toe has one established thing and one cutesy. Tip/toe has two cutesies. An unwritten (or perhaps actually written) rule of good humor is that you shouldn't make a double pun. They somehow don't add to double effect. Andrei
Sep 12 2008
prev sibling parent reply "Dave" <Dave_member pathlink.com> writes:
"Andrei Alexandrescu" <SeeWebsiteForEmail erdani.org> wrote in message 
news:gadn7c$oe5$4 digitalmars.com...
 Pablo Ripolles wrote:
 Andrei Alexandrescu Wrote:

 In wake of the many excellent comments and suggestions made here, I made 
 one more pass through the draft proposal for ranges.

 http://ssli.ee.washington.edu/~aalexand/d/tmp/std_range.html

 There are some comments in red illustrating some uncertainties (not 
 all), and the names of the primitives have been updated. Bicycle shed 
 galore! But don't forget to comment on the reactor as well :o).


 Andrei

Well, it looks prety clean! :D However, I'm not completely sure I like these "head" and "toe" names selection. It projects to much on it, doesn't it? couldn't it be more neutral? perhaps more conceptual? I haven't been able to read the last days' comments... but my last impressions were that this "head" was not the best choice. If "head" is the header item, why not call it "header"? If ''toe" is the last item, why not call it "last"? Other comment goes for the "done" property, for the seek of consistence shouldn't it better be named "isDone"? Cheers!

Thanks. One problem in coding with first and last was that sometimes the code looks unnatural, especially when your range exposes a few more functions. In a stream parser, dealing with the "first" element is not the most natural way to think of it. But I agree that first and last are definitely palatable and natural most of the time. But then again, shouldn't any design have the inevitable cutesy that makes it memorable? :o) Andrei

From the limited posts I've had the time to read, it seems this topic has probably been already beaten to death, but how about "tail" instead of "toe". And instead of the "yellow" primitive, why not "intersect(r,s)" , "intersection(r,s)", "r.contains(s)" , "r.intersect(s)" , etc. Wasn't "intersect" the original? Thanks, - Dave
Sep 12 2008
next sibling parent reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
Dave wrote:
 
 "Andrei Alexandrescu" <SeeWebsiteForEmail erdani.org> wrote in message 
 news:gadn7c$oe5$4 digitalmars.com...
 Pablo Ripolles wrote:
 Andrei Alexandrescu Wrote:

 In wake of the many excellent comments and suggestions made here, I 
 made one more pass through the draft proposal for ranges.

 http://ssli.ee.washington.edu/~aalexand/d/tmp/std_range.html

 There are some comments in red illustrating some uncertainties (not 
 all), and the names of the primitives have been updated. Bicycle 
 shed galore! But don't forget to comment on the reactor as well :o).


 Andrei

Well, it looks prety clean! :D However, I'm not completely sure I like these "head" and "toe" names selection. It projects to much on it, doesn't it? couldn't it be more neutral? perhaps more conceptual? I haven't been able to read the last days' comments... but my last impressions were that this "head" was not the best choice. If "head" is the header item, why not call it "header"? If ''toe" is the last item, why not call it "last"? Other comment goes for the "done" property, for the seek of consistence shouldn't it better be named "isDone"? Cheers!

Thanks. One problem in coding with first and last was that sometimes the code looks unnatural, especially when your range exposes a few more functions. In a stream parser, dealing with the "first" element is not the most natural way to think of it. But I agree that first and last are definitely palatable and natural most of the time. But then again, shouldn't any design have the inevitable cutesy that makes it memorable? :o) Andrei

From the limited posts I've had the time to read, it seems this topic has probably been already beaten to death, but how about "tail" instead of "toe". And instead of the "yellow" primitive, why not "intersect(r,s)" , "intersection(r,s)", "r.contains(s)" , "r.intersect(s)" , etc. Wasn't "intersect" the original?

Intersect is good but doesn't reveal an important detail: the operation is not commutative. Andrei
Sep 14 2008
next sibling parent Pablo Ripolles <in-call gmx.net> writes:
Andrei Alexandrescu Wrote:

 Dave wrote:
 
 "Andrei Alexandrescu" <SeeWebsiteForEmail erdani.org> wrote in message 
 news:gadn7c$oe5$4 digitalmars.com...
 Pablo Ripolles wrote:
 Andrei Alexandrescu Wrote:

 In wake of the many excellent comments and suggestions made here, I 
 made one more pass through the draft proposal for ranges.

 http://ssli.ee.washington.edu/~aalexand/d/tmp/std_range.html

 There are some comments in red illustrating some uncertainties (not 
 all), and the names of the primitives have been updated. Bicycle 
 shed galore! But don't forget to comment on the reactor as well :o).


 Andrei

Well, it looks prety clean! :D However, I'm not completely sure I like these "head" and "toe" names selection. It projects to much on it, doesn't it? couldn't it be more neutral? perhaps more conceptual? I haven't been able to read the last days' comments... but my last impressions were that this "head" was not the best choice. If "head" is the header item, why not call it "header"? If ''toe" is the last item, why not call it "last"? Other comment goes for the "done" property, for the seek of consistence shouldn't it better be named "isDone"? Cheers!

Thanks. One problem in coding with first and last was that sometimes the code looks unnatural, especially when your range exposes a few more functions. In a stream parser, dealing with the "first" element is not the most natural way to think of it. But I agree that first and last are definitely palatable and natural most of the time. But then again, shouldn't any design have the inevitable cutesy that makes it memorable? :o) Andrei

From the limited posts I've had the time to read, it seems this topic has probably been already beaten to death, but how about "tail" instead of "toe". And instead of the "yellow" primitive, why not "intersect(r,s)" , "intersection(r,s)", "r.contains(s)" , "r.intersect(s)" , etc. Wasn't "intersect" the original?

Intersect is good but doesn't reveal an important detail: the operation is not commutative.

What about wedge(r, s) or r.wedge(s)? Perhaps meet(r, s) or r.meet(s)? The source of these proposals is based on the symbols and terms used in the field of algebraic topology. The meet operation in commutative topologies is the standard intersection, whereas in noncommutative topologies it is implemented employing noncommutative intersection. In both cases the term (meet) and the symbol (wedge) are used. Cheers!
Sep 15 2008
prev sibling next sibling parent reply KennyTM~ <kennytm gmail.com> writes:
Andrei Alexandrescu wrote:
 Dave wrote:
 "Andrei Alexandrescu" <SeeWebsiteForEmail erdani.org> wrote in message 
 news:gadn7c$oe5$4 digitalmars.com...
 Pablo Ripolles wrote:
 Andrei Alexandrescu Wrote:

 In wake of the many excellent comments and suggestions made here, I 
 made one more pass through the draft proposal for ranges.

 http://ssli.ee.washington.edu/~aalexand/d/tmp/std_range.html

 There are some comments in red illustrating some uncertainties (not 
 all), and the names of the primitives have been updated. Bicycle 
 shed galore! But don't forget to comment on the reactor as well :o).


 Andrei

Well, it looks prety clean! :D However, I'm not completely sure I like these "head" and "toe" names selection. It projects to much on it, doesn't it? couldn't it be more neutral? perhaps more conceptual? I haven't been able to read the last days' comments... but my last impressions were that this "head" was not the best choice. If "head" is the header item, why not call it "header"? If ''toe" is the last item, why not call it "last"? Other comment goes for the "done" property, for the seek of consistence shouldn't it better be named "isDone"? Cheers!

Thanks. One problem in coding with first and last was that sometimes the code looks unnatural, especially when your range exposes a few more functions. In a stream parser, dealing with the "first" element is not the most natural way to think of it. But I agree that first and last are definitely palatable and natural most of the time. But then again, shouldn't any design have the inevitable cutesy that makes it memorable? :o) Andrei

From the limited posts I've had the time to read, it seems this topic has probably been already beaten to death, but how about "tail" instead of "toe". And instead of the "yellow" primitive, why not "intersect(r,s)" , "intersection(r,s)", "r.contains(s)" , "r.intersect(s)" , etc. Wasn't "intersect" the original?

Intersect is good but doesn't reveal an important detail: the operation is not commutative. Andrei

.cross ? .across ? .join ? .onlyAlso ? By the way, how does "yellow" not commutative?
Sep 15 2008
parent Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
KennyTM~ wrote:
 Andrei Alexandrescu wrote:
 Dave wrote:
 "Andrei Alexandrescu" <SeeWebsiteForEmail erdani.org> wrote in 
 message news:gadn7c$oe5$4 digitalmars.com...
 Pablo Ripolles wrote:
 Andrei Alexandrescu Wrote:

 In wake of the many excellent comments and suggestions made here, 
 I made one more pass through the draft proposal for ranges.

 http://ssli.ee.washington.edu/~aalexand/d/tmp/std_range.html

 There are some comments in red illustrating some uncertainties 
 (not all), and the names of the primitives have been updated. 
 Bicycle shed galore! But don't forget to comment on the reactor as 
 well :o).


 Andrei

Well, it looks prety clean! :D However, I'm not completely sure I like these "head" and "toe" names selection. It projects to much on it, doesn't it? couldn't it be more neutral? perhaps more conceptual? I haven't been able to read the last days' comments... but my last impressions were that this "head" was not the best choice. If "head" is the header item, why not call it "header"? If ''toe" is the last item, why not call it "last"? Other comment goes for the "done" property, for the seek of consistence shouldn't it better be named "isDone"? Cheers!

Thanks. One problem in coding with first and last was that sometimes the code looks unnatural, especially when your range exposes a few more functions. In a stream parser, dealing with the "first" element is not the most natural way to think of it. But I agree that first and last are definitely palatable and natural most of the time. But then again, shouldn't any design have the inevitable cutesy that makes it memorable? :o) Andrei

From the limited posts I've had the time to read, it seems this topic has probably been already beaten to death, but how about "tail" instead of "toe". And instead of the "yellow" primitive, why not "intersect(r,s)" , "intersection(r,s)", "r.contains(s)" , "r.intersect(s)" , etc. Wasn't "intersect" the original?

Intersect is good but doesn't reveal an important detail: the operation is not commutative. Andrei

.cross ? .across ? .join ? .onlyAlso ? By the way, how does "yellow" not commutative?

In the forward ranges realm, if you start from the wrong range, you never get to compute the "yellow". Andrei
Sep 15 2008
prev sibling parent Lars Kyllingstad <public kyllingen.NOSPAMnet> writes:
Andrei Alexandrescu wrote:
 Dave wrote:
 And instead of the "yellow" primitive, why not "intersect(r,s)" , 
 "intersection(r,s)", "r.contains(s)" , "r.intersect(s)" , etc.

 Wasn't "intersect" the original?

Intersect is good but doesn't reveal an important detail: the operation is not commutative.

How about 'overlap', then? Still not perfect, but at least it doesn't carry the name of an inherently commutative mathematical operation. -Lars
Sep 16 2008
prev sibling parent Russell Lewis <webmaster villagersonline.com> writes:
Dave wrote:
 And instead of the "yellow" primitive, why not "intersect(r,s)" , 
 "intersection(r,s)", "r.contains(s)" , "r.intersect(s)" , etc.

When I first read this, I thought it said yellow = intersect(r,g) which, of course, only works in the RGB scheme. :)
Sep 15 2008
prev sibling next sibling parent "Bill Baxter" <wbaxter gmail.com> writes:
On Fri, Sep 12, 2008 at 11:39 PM, Andrei Alexandrescu
<SeeWebsiteForEmail erdani.org> wrote:
 Pablo Ripolles wrote:
 What about "isDone"?

isDone is great, I just wanted to keep the one-word streak going. Let's see what everyone else says.

Hmm. std.algorithm does have an "isSorted" function. So I guess I agree it would be more consistent if you call it isDone or isEmpty. Or rename "isSorted" to "sorted". :-) But then you have to face the consequences later when you want to have a predicate that is ambiguous without the "is". Probably a lot of noun predicates are in that category -- i.e. checking isSomeNoun(x). Like "isRange(x)" to see if x is a range. That would have to just become "range(x)" which is a bit ambiguous. So I agree. Stick the "is" in there. --bb
Sep 12 2008
prev sibling next sibling parent "Bill Baxter" <wbaxter gmail.com> writes:
On Sat, Sep 13, 2008 at 12:03 AM, Andrei Alexandrescu
<SeeWebsiteForEmail erdani.org> wrote:
 Bill Baxter wrote:
 On Fri, Sep 12, 2008 at 11:39 PM, Andrei Alexandrescu
 <SeeWebsiteForEmail erdani.org> wrote:
 Pablo Ripolles wrote:
 What about "isDone"?

isDone is great, I just wanted to keep the one-word streak going. Let's see what everyone else says.

Hmm. std.algorithm does have an "isSorted" function. So I guess I agree it would be more consistent if you call it isDone or isEmpty. Or rename "isSorted" to "sorted". :-) But then you have to face the consequences later when you want to have a predicate that is ambiguous without the "is". Probably a lot of noun predicates are in that category -- i.e. checking isSomeNoun(x). Like "isRange(x)" to see if x is a range. That would have to just become "range(x)" which is a bit ambiguous. So I agree. Stick the "is" in there.

Thing is, people will call isSorted much less often than (isD|d)one. In std.algorithm clearly the one-word paradigm can't scale. But for a handful of heavily-used names I'd be willing to take the Pepsi challenge. Andrei P.S. The more I think of it, the more I like "tip" of the range. Short, poignant, easy to remember. Not pressing the red button just yet.

Hmm. One semantic issue I have is that the tip usually refers to the infinitessimal point at the end. Not a thing with substance. I'm having trouble feeling like I'm going to get an item back when I look at "x.tip". Head has huge history being used for the item at the front of a list, so I think that's much less likely to cause anyone looking at D code to scratch their heads. It will be obvious what it means even in relative isolation. head/tip will often appear without "toe" in forward range algos. So you need to be able to easily recognize what "tip" means without seeing that "toe" to give context. Toe on the other hand will probably almost always appear with his mate. Ooh, another scale thing, but a head is obviously a very different scale than a toe. A foot is closer to the same scale. Maybe head/foot is better than head/toe. The connection between retreating / feet is stronger that retreating / toes, too! --bb
Sep 12 2008
prev sibling next sibling parent "Bill Baxter" <wbaxter gmail.com> writes:
On Sat, Sep 13, 2008 at 1:03 AM, Pablo Ripolles <in-call gmx.net> wrote:
 Hmm.  One semantic issue I have is that the tip usually refers to the
 infinitessimal point at the end.  Not a thing with substance.  I'm
 having trouble feeling like I'm going to get an item back when I look
 at "x.tip".  Head has huge history being used for the item at the
 front of a list, so I think that's much less likely to cause anyone
 looking at D code to scratch their heads.  It will be obvious what it
 means even in relative isolation.  head/tip will often appear without
 "toe" in forward range algos.  So you need to be able to easily
 recognize what "tip" means without seeing that "toe" to give context.
 Toe on the other hand will probably almost always appear with his
 mate.

 Ooh, another scale thing, but a head is obviously a very different
 scale than a toe.  A foot is closer to the same scale.  Maybe
 head/foot is better than head/toe.  The connection between retreating
 / feet is stronger that retreating / toes, too!

 --bb

neither the tip of the tail, nor the tip of the wing, nor the tip of the flagellum are really infinitesimal... I'm not sure whether I understand your reasoning about the "tip" / "toe", I interpreted that "tip" could be a substitute of "toe"...

Nope. I'm pretty sure that the discussion is about replacing "head" with "tip". There's an expression "from tip to toe". But I think your confusion (or is it mine?) about which end would be the tip is pretty damning.
 my problem with foot is that there is necessarily more than one.

There's also more than one toe. But I guess you thought that "toe" was out.
 perhaps in the world of the anatomy of the chordates we can find something...
dunno, "coccyx" is pretty weird but at least there is only one.

Well, there is only one tail... but it's what functional guys call everything but the head, so Anrdrei wants to avoid it. --bb
Sep 12 2008
prev sibling next sibling parent "Bill Baxter" <wbaxter gmail.com> writes:
According to http://en.wikipedia.org/wiki/Aft
The opposite of aft is forward, pronounced "forrard" for some reason.
Not good I think.

I see nothing wrong with head/foot.
There's "head of a bed / foot of a bed" so it is not the case that
these words always refer to people or that
there's always more than one foot.

---bb

On Sat, Sep 13, 2008 at 7:12 AM, Pablo Ripolles <in-call gmx.net> wrote:
 Andrei Alexandrescu Wrote:

 Bill Baxter wrote:
 On Fri, Sep 12, 2008 at 11:39 PM, Andrei Alexandrescu
 <SeeWebsiteForEmail erdani.org> wrote:
 Pablo Ripolles wrote:
 What about "isDone"?

what everyone else says.

Hmm. std.algorithm does have an "isSorted" function. So I guess I agree it would be more consistent if you call it isDone or isEmpty. Or rename "isSorted" to "sorted". :-) But then you have to face the consequences later when you want to have a predicate that is ambiguous without the "is". Probably a lot of noun predicates are in that category -- i.e. checking isSomeNoun(x). Like "isRange(x)" to see if x is a range. That would have to just become "range(x)" which is a bit ambiguous. So I agree. Stick the "is" in there.

Thing is, people will call isSorted much less often than (isD|d)one. In std.algorithm clearly the one-word paradigm can't scale. But for a handful of heavily-used names I'd be willing to take the Pepsi challenge. Andrei P.S. The more I think of it, the more I like "tip" of the range. Short, poignant, easy to remember. Not pressing the red button just yet.

A perhaps more aeronautical term but short and nice as an alternative to "tip" might be "aft". So, what about "aft" instead of "tip"? "head"/"aft" it's not that bad, is it? Cheers!

Sep 12 2008
prev sibling parent "Bill Baxter" <wbaxter gmail.com> writes:
On Sat, Sep 13, 2008 at 5:11 AM, Andrei Alexandrescu
<SeeWebsiteForEmail erdani.org> wrote:
 Walter Bright wrote:
 Andrei Alexandrescu wrote:
 Thanks. One problem in coding with first and last was that sometimes the
 code looks unnatural, especially when your range exposes a few more
 functions. In a stream parser, dealing with the "first" element is not the
 most natural way to think of it. But I agree that first and last are
 definitely palatable and natural most of the time. But then again, shouldn't
 any design have the inevitable cutesy that makes it memorable? :o)

One consideration when selecting names is how it would interact with google. One advantage to head/toe is there is already head/tail when thinking of a list, and head/toe would fit into that naturally.

I agree. Sorry tipsters. I think head/toe has one established thing and one cutesy. Tip/toe has two cutesies. An unwritten (or perhaps actually written) rule of good humor is that you shouldn't make a double pun. They somehow don't add to double effect. Andrei

Head / foot! No cutesie == even better! --bb
Sep 12 2008
prev sibling next sibling parent reply Ary Borenszweig <ary esperanto.org.ar> writes:
Andrei Alexandrescu wrote:
 In wake of the many excellent comments and suggestions made here, I made 
 one more pass through the draft proposal for ranges.
 
 http://ssli.ee.washington.edu/~aalexand/d/tmp/std_range.html
 
 There are some comments in red illustrating some uncertainties (not 
 all), and the names of the primitives have been updated. Bicycle shed 
 galore! But don't forget to comment on the reactor as well :o).
 
 
 Andrei

Just a little typo, in the "Output range" example: // Copies a range to another void copy(R1, R2)(R1 src, R2 tgt) { for (; !src.done; src.head) { tgt.put(src.head); } } the for increment should be src.next
Sep 12 2008
parent Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
Ary Borenszweig wrote:
 Andrei Alexandrescu wrote:
 In wake of the many excellent comments and suggestions made here, I 
 made one more pass through the draft proposal for ranges.

 http://ssli.ee.washington.edu/~aalexand/d/tmp/std_range.html

 There are some comments in red illustrating some uncertainties (not 
 all), and the names of the primitives have been updated. Bicycle shed 
 galore! But don't forget to comment on the reactor as well :o).


 Andrei

Just a little typo, in the "Output range" example: // Copies a range to another void copy(R1, R2)(R1 src, R2 tgt) { for (; !src.done; src.head) { tgt.put(src.head); } } the for increment should be src.next

Fixed, thanks Ary. Andrei
Sep 12 2008
prev sibling next sibling parent reply Sergey Gromov <snake.scaly gmail.com> writes:
Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> wrote:
 In wake of the many excellent comments and suggestions made here, I made 
 one more pass through the draft proposal for ranges.
 
 http://ssli.ee.washington.edu/~aalexand/d/tmp/std_range.html
 
 There are some comments in red illustrating some uncertainties (not 
 all), and the names of the primitives have been updated. Bicycle shed 
 galore! But don't forget to comment on the reactor as well :o).

I've got a heap of virtual sticky notes all over my monitor while reading the edited version, so here they are. *) done/next/head thing got even more confusing. Range is not a process, you cannot be done with it. If you call a spade a spade, then done() is actually a safe, guarded prefetch, head is, well, sort of what it claims to be, and next() just relaxes done's guard. The look ahead/fetch next approach was much more intuitive. I don't understand why you dropped it. I remember you saying that Bill convinced you it was wrong but I either missed the arguments themselves or hadn't understood them. *) How do you enforce noncopyable/release semantics over a range? I don't think D got any means for that. Also if an input range is a class passed by reference, will you allow it? *) before() is valid for a single-linked list, forward iterators should support it. *) You've replaced tail with toe because people may think tail could contain many elements. But it doesn't pair well with head, and besides, if I split a list in the middle I'd call the halves a head and a tail which form a logical pair. Maybe you consider tip-toe, or top-toe, from the top of my head? ;) *) A typo: in Output range sample: for (; !src.done; src.head) should be for (; !src.done; src.next) *) next() is specified to return a value, but reduce() is not. This is probably a typo. I'd prefer them both be implemented as (done(), head) and (reduce(), toe) respectively, but I don't know what your intention was. *) Still uncertain support for shrink-on-read, no support for shrink-on- write and slicing forward and bidirectional ranges. Are they going into a library?
Sep 12 2008
parent reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
Sergey Gromov wrote:
 Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> wrote:
 In wake of the many excellent comments and suggestions made here, I made 
 one more pass through the draft proposal for ranges.

 http://ssli.ee.washington.edu/~aalexand/d/tmp/std_range.html

 There are some comments in red illustrating some uncertainties (not 
 all), and the names of the primitives have been updated. Bicycle shed 
 galore! But don't forget to comment on the reactor as well :o).

I've got a heap of virtual sticky notes all over my monitor while reading the edited version, so here they are. *) done/next/head thing got even more confusing. Range is not a process, you cannot be done with it. If you call a spade a spade, then done() is actually a safe, guarded prefetch, head is, well, sort of what it claims to be, and next() just relaxes done's guard.

The art is to find something that's reasonably evocative without being too long. I doubt safeGuardedPrefetch will fare very well, although I agree it's very precise. Inevitably a shorter word will also be less precise.
 The look ahead/fetch next approach was much more intuitive.  I don't 
 understand why you dropped it.  I remember you saying that Bill 
 convinced you it was wrong but I either missed the arguments themselves 
 or hadn't understood them.
 
 *)
 How do you enforce noncopyable/release semantics over a range?  I don't 
 think D got any means for that.  Also if an input range is a class 
 passed by reference, will you allow it?

Walter is working on adding that feature now.
 *)
 before() is valid for a single-linked list, forward iterators should 
 support it.

This is subtle. Consider a singly-linked list that uses a Node* as a range. Condition for termination is p.next == null. Now to build list.before(another) you'd have to express a fragment of a list, so the one-Node* representation is insufficient. You have to return a range with TWO Node* objects, one pointing to the beginning and the other to the end of the fragment. That can be done, but it also means that the before operation is not closed under your Range type. You'd have to return a different Range type, a fatter one. Or we could impose that all lists use fat ranges to start with. But I don't want that. I hate people discussing, "Oh you want a list? use std.slist, it's cool. But beware, if you want a really fast list you better do it yourself." This is a no-go because whenever you're in the mood of using a singly-linked list, it being lightweight is invariably the main concern. So I want to write a singly-linked list that is as fast as the classic singly-linked list implemented by hand from first principles. If we fail to do so, we can as well go home. If we want to achieve that, we need to model the slist fundamentals properly. Part of that proper modeling is that slist is not closed under the "before" operation.
 *)
 You've replaced tail with toe because people may think tail could 
 contain many elements.  But it doesn't pair well with head, and besides, 
 if I split a list in the middle I'd call the halves a head and a tail 
 which form a logical pair.  Maybe you consider tip-toe, or top-toe, from 
 the top of my head? ;)

Head to toe is culturally appealing for English speakers. But tip to toe is also cool. Nice tip, thanks. :o)
 *)
 A typo: in Output range sample:
 	for (; !src.done; src.head)
 should be
 	for (; !src.done; src.next)

Fixed, thanks.
 *)
 next() is specified to return a value, but reduce() is not.  This is 
 probably a typo.  I'd prefer them both be implemented as (done(), head) 
 and (reduce(), toe) respectively, but I don't know what your intention 
 was.

Neither of next and retreat should return a value. In wake of constructors/destructors, we shouldn't return by-value lightly.
 *)
 Still uncertain support for shrink-on-read, no support for shrink-on-
 write and slicing forward and bidirectional ranges.  Are they going into 
 a library?

Yes, excellent point. The document on ranges tells what they must define. The standard library can and will define convenience functions using those primitives. This is appealing because there will be many range implementations and you don't want to burden everyone with implementing a ton of non-orthogonal stuff. Andrei
Sep 12 2008
parent reply Sergey Gromov <snake.scaly gmail.com> writes:
Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> wrote:
 Sergey Gromov wrote:
 *)
 next() is specified to return a value, but reduce() is not.  This is 
 probably a typo.  I'd prefer them both be implemented as (done(), head) 
 and (reduce(), toe) respectively, but I don't know what your intention 
 was.

Neither of next and retreat should return a value. In wake of constructors/destructors, we shouldn't return by-value lightly.

Then, in Input range primitives, e=r.next must be just r.next.
Sep 12 2008
parent Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
Sergey Gromov wrote:
 Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> wrote:
 Sergey Gromov wrote:
 *)
 next() is specified to return a value, but reduce() is not.  This is 
 probably a typo.  I'd prefer them both be implemented as (done(), head) 
 and (reduce(), toe) respectively, but I don't know what your intention 
 was.

constructors/destructors, we shouldn't return by-value lightly.

Then, in Input range primitives, e=r.next must be just r.next.

Oh, now I see. Fixed. But I still want us to contemplate the possibility of unifying input ranges with recycling, and other ranges, under the same interface. Andrei
Sep 12 2008
prev sibling next sibling parent reply "Bill Baxter" <wbaxter gmail.com> writes:
On Fri, Sep 12, 2008 at 2:44 PM, Andrei Alexandrescu
<SeeWebsiteForEmail erdani.org> wrote:
 In wake of the many excellent comments and suggestions made here, I made one
 more pass through the draft proposal for ranges.

 http://ssli.ee.washington.edu/~aalexand/d/tmp/std_range.html

 There are some comments in red illustrating some uncertainties (not all),
 and the names of the primitives have been updated. Bicycle shed galore! But
 don't forget to comment on the reactor as well :o).

Looking better! I disagree with this: """ User code may pair iterators wrongly to create meaningless ranges. For example, given collections a and b of the same type, a.begin to b.begin makes no sense as a range, yet both the compiler and the runtime are hard-pressed in rejecting such mistakes. Such problems are often (though not always) avoided if range is the primitive. """ You can just as easily create nonsensical ranges using before() and after(). And I don't recall ever making the mistake of mixing up one container's begin() with another's end(). I disagree here too: """ A bidirectional range models the natural way of iterating a doubly-linked list. """ It maybe provides an efficient way to implement many algorithms on a doubly linked list. But to say it implements the natural way of iterating one is a big stretch. I was about to argue that "head" should be "value" and "toe" should be "tvalue" on the grounds that most of the time iteration with a range going to be all about the head. For all intents and purposes it *is* the current value. Another benefit is that the terminology of ".value" generalizes to plain iterators well. And it may be useful for users to create iterators that support some but not all of the range interface (like .value and .next). The name .value doesn't work for the random access concept so well, but if you are using the random access range like a forward range it does, and if you aren't using it like a forward range, then you'd use r[0] anyway instead of r.value. However, that was mainly motivated by the feeling that if we ever do have iterators, that having to make the dereference op be .head would look fatally silly. But I think eventually if you get out of the iterator mindset, and think of ranges as everything, then it's not so silly to describe an iterator as a poor castrated half-range that knows where its head is, where it's next is, but isn't so sure about the rest. So I think I can live with .head. Even for iterators if we end up having them. But I do have some suggestions nonetheless! Marked with ==> below. -- Universal -- r.done r.init --- Input : Universal --- e=r.head e=r.next r1=r.release ==> r.transfer? Release sounds like ref counting (e.g. in COM) Also seems like r.transfer(r1) could make implementation more efficient. Or perhaps make it a .swap like STL. Maybe you have something against .swap? -- Output : Universal -- r.put(e) -- Forward : Input, (optional) Output -- r1 = r r.head = e t=r.after(s) -- Bidirectional : Forward -- e = r.toe r.toe = e r.reduce ==> r.retreat -- aka "pull back" t = r.before(s) -- Random access : Bidirectional -- l = r.length e = r[n] r[n] = e r1 = r[n1..n2] ------------ Just those two! --bb
Sep 12 2008
next sibling parent reply Sergey Gromov <snake.scaly gmail.com> writes:
Bill Baxter <wbaxter gmail.com> wrote:
 But I do have some suggestions nonetheless!  Marked with ==> below.
 
 -- Universal --
 r.done
 r.init
 
 --- Input : Universal ---
 e=r.head
 e=r.next
 r1=r.release  ==> r.transfer?  Release sounds like ref counting (e.g. in COM)
                           Also seems like r.transfer(r1) could make
 implementation more efficient.
                           Or perhaps make it a .swap like STL.  Maybe
 you have something against .swap?
 
 -- Output : Universal --
 r.put(e)
 
 -- Forward : Input, (optional) Output  --
 r1 = r
 r.head = e
 t=r.after(s)
 
 -- Bidirectional : Forward --
 e = r.toe
 r.toe = e
 r.reduce           ==> r.retreat  -- aka "pull back"
 t = r.before(s)
 
 -- Random access : Bidirectional --
 l = r.length
 e = r[n]
 r[n] = e
 r1 = r[n1..n2]
 ------------
 
 Just those two!

I've got a bit of insight! XD -- Common r.empty -- InOut v = r.next; => T R.next(); r.next = v; => T R.next(T v); -- Forward: InOut, copyable v = r.tip r.tip = v r1 = r.before(s) r1 = r.after(s) -- Bidir: Forward v = r.prev r.prev = v v = r.toe r.toe = v -- Random: Bidir no changes prev() seems very misleading, otherwise I like it.
Sep 12 2008
next sibling parent reply Sergey Gromov <snake.scaly gmail.com> writes:
Bill Baxter <wbaxter gmail.com> wrote:
 On Fri, Sep 12, 2008 at 11:22 PM, Sergey Gromov <snake.scaly gmail.com> wrote:
 Bill Baxter <wbaxter gmail.com> wrote:
 But I do have some suggestions nonetheless!  Marked with ==> below.

 -- Universal --
 r.done
 r.init

 --- Input : Universal ---
 e=r.head
 e=r.next
 r1=r.release  ==> r.transfer?  Release sounds like ref counting (e.g. in COM)
                           Also seems like r.transfer(r1) could make
 implementation more efficient.
                           Or perhaps make it a .swap like STL.  Maybe
 you have something against .swap?

 -- Output : Universal --
 r.put(e)

 -- Forward : Input, (optional) Output  --
 r1 = r
 r.head = e
 t=r.after(s)

 -- Bidirectional : Forward --
 e = r.toe
 r.toe = e
 r.reduce           ==> r.retreat  -- aka "pull back"
 t = r.before(s)

 -- Random access : Bidirectional --
 l = r.length
 e = r[n]
 r[n] = e
 r1 = r[n1..n2]
 ------------

 Just those two!

I've got a bit of insight! XD -- Common r.empty -- InOut v = r.next; => T R.next(); r.next = v; => T R.next(T v); -- Forward: InOut, copyable v = r.tip r.tip = v r1 = r.before(s) r1 = r.after(s) -- Bidir: Forward v = r.prev r.prev = v v = r.toe r.toe = v -- Random: Bidir no changes prev() seems very misleading, otherwise I like it.

So basically you changed done ==> empty head ==> tip retreat ==> prev ?

The insight was about get/put ==> next. That's the most significant change, others are merely renames as you rightfully point out. Hence the "prev" which should mean both "get at the end" and "put to the end".
 "prev" is horrible.  I still like "retreat" best so far.  We need the
 contrapositive of "next" not the "opposite".  :-)  And since that
 doesn't exist we should just go for a word that sorta means the right
 thing and won't be  confused with being the opposite (or with being
 something else entirely like "reduce").

v = r.retreat r.retreat = v It's definitely better than prev. Thank you for proposing. I'm not a native speaker so inventing names could be a tricky business for me.
Sep 12 2008
parent reply Sergey Gromov <snake.scaly gmail.com> writes:
Bill Baxter <wbaxter gmail.com> wrote:
 On Fri, Sep 12, 2008 at 11:58 PM, Sergey Gromov <snake.scaly gmail.com> wrote:
 So basically you changed
 done ==> empty
 head ==> tip
 retreat ==> prev
 ?

The insight was about get/put ==> next. That's the most significant change, others are merely renames as you rightfully point out. Hence the "prev" which should mean both "get at the end" and "put to the end".

Ah ok. Your switching to declaration syntax instead of usage syntax confused me. :-) That is cute. So r.put(e) ==> r.next = e It would also mean the copy to output idiom would become for(; ! i.done; i.next) o.next = i.head; Would be cooler if it could be just while(!i.done) o.next = i.next; .. oh well.

Exactly, I wanted it to be while (!i.done) o.next = i.next; because I didn't want any head in an input range.
 But maybe it's too cute for too little gain?  It's pretty darn obvious
 what a "put" function is for.  you can also search for it more easily
 then a bunch of next's that could be either writes or not writes.

I'm still not sure. The usability can only be measured by actual usage.
Sep 12 2008
parent reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
Sergey Gromov wrote:
 Bill Baxter <wbaxter gmail.com> wrote:
 On Fri, Sep 12, 2008 at 11:58 PM, Sergey Gromov <snake.scaly gmail.com> wrote:
 So basically you changed
 done ==> empty
 head ==> tip
 retreat ==> prev
 ?

change, others are merely renames as you rightfully point out. Hence the "prev" which should mean both "get at the end" and "put to the end".

confused me. :-) That is cute. So r.put(e) ==> r.next = e It would also mean the copy to output idiom would become for(; ! i.done; i.next) o.next = i.head; Would be cooler if it could be just while(!i.done) o.next = i.next; .. oh well.

Exactly, I wanted it to be while (!i.done) o.next = i.next;

Hmm, let's see. So: a) If i is an input range, then i.next returns by value. b) If i is a forward range, then i.next returns by reference. I assume that's what you had in mind? Andrei
Sep 12 2008
parent reply Sergey Gromov <snake.scaly gmail.com> writes:
Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> wrote:
 Sergey Gromov wrote:
 Bill Baxter <wbaxter gmail.com> wrote:
 On Fri, Sep 12, 2008 at 11:58 PM, Sergey Gromov <snake.scaly gmail.com> wrote:
 So basically you changed
 done ==> empty
 head ==> tip
 retreat ==> prev
 ?

change, others are merely renames as you rightfully point out. Hence the "prev" which should mean both "get at the end" and "put to the end".

confused me. :-) That is cute. So r.put(e) ==> r.next = e It would also mean the copy to output idiom would become for(; ! i.done; i.next) o.next = i.head; Would be cooler if it could be just while(!i.done) o.next = i.next; .. oh well.

Exactly, I wanted it to be while (!i.done) o.next = i.next;

Hmm, let's see. So: a) If i is an input range, then i.next returns by value. b) If i is a forward range, then i.next returns by reference. I assume that's what you had in mind?

Not quite. You cannot mutate an input range, it must be in specs. Therefore it's up to the range designer what to return. LineEater will return a const reference to an internal buffer. RNG will return int. Array will return a const reference to its element. Some could return a new class instance every time. If you want to mutate though, you require a forward range and use its i.first which must be a reference to the first element in the range. Then you can safely ignore the i.next result which will most likely be a reference to the previous i.first contents.
Sep 12 2008
parent reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
Sergey Gromov wrote:
 Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> wrote:
 Sergey Gromov wrote:
 Bill Baxter <wbaxter gmail.com> wrote:
 On Fri, Sep 12, 2008 at 11:58 PM, Sergey Gromov <snake.scaly gmail.com> wrote:
 So basically you changed
 done ==> empty
 head ==> tip
 retreat ==> prev
 ?

change, others are merely renames as you rightfully point out. Hence the "prev" which should mean both "get at the end" and "put to the end".

confused me. :-) That is cute. So r.put(e) ==> r.next = e It would also mean the copy to output idiom would become for(; ! i.done; i.next) o.next = i.head; Would be cooler if it could be just while(!i.done) o.next = i.next; .. oh well.

while (!i.done) o.next = i.next;

a) If i is an input range, then i.next returns by value. b) If i is a forward range, then i.next returns by reference. I assume that's what you had in mind?

Not quite. You cannot mutate an input range, it must be in specs. Therefore it's up to the range designer what to return. LineEater will return a const reference to an internal buffer. RNG will return int. Array will return a const reference to its element. Some could return a new class instance every time.

Given that in D const is transitive, we can't operate with const the same way C++ does. Consider something as trivial as a copy: Tgt copy(Src, Tgt)(Src src, Tgt tgt) { for (; !src.done; src.next) tgt.put(src.tip); } Problem is, you won't be able to copy e.g. an int[][] to another because the types aren't compatible.
 If you want to mutate though, you require a forward range and use its 
 i.first which must be a reference to the first element in the range.  
 Then you can safely ignore the i.next result which will most likely be a 
 reference to the previous i.first contents.

This part does, I think, work. Andrei
Sep 12 2008
parent reply Sergey Gromov <snake.scaly gmail.com> writes:
Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> wrote:
 Sergey Gromov wrote:
 Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> wrote:
 Sergey Gromov wrote:
 Bill Baxter <wbaxter gmail.com> wrote:
 On Fri, Sep 12, 2008 at 11:58 PM, Sergey Gromov <snake.scaly gmail.com> wrote:
 So basically you changed
 done ==> empty
 head ==> tip
 retreat ==> prev
 ?

change, others are merely renames as you rightfully point out. Hence the "prev" which should mean both "get at the end" and "put to the end".

confused me. :-) That is cute. So r.put(e) ==> r.next = e It would also mean the copy to output idiom would become for(; ! i.done; i.next) o.next = i.head; Would be cooler if it could be just while(!i.done) o.next = i.next; .. oh well.

while (!i.done) o.next = i.next;

a) If i is an input range, then i.next returns by value. b) If i is a forward range, then i.next returns by reference. I assume that's what you had in mind?

Not quite. You cannot mutate an input range, it must be in specs. Therefore it's up to the range designer what to return. LineEater will return a const reference to an internal buffer. RNG will return int. Array will return a const reference to its element. Some could return a new class instance every time.

Given that in D const is transitive, we can't operate with const the same way C++ does. Consider something as trivial as a copy: Tgt copy(Src, Tgt)(Src src, Tgt tgt) { for (; !src.done; src.next) tgt.put(src.tip); } Problem is, you won't be able to copy e.g. an int[][] to another because the types aren't compatible.

If Src is an input range you must make a deep copy. This holds true for your current design also. So this algorithm is flawed and it's good if it won't compile. I don't know how to make a generic deep copy, but I believe your meta-fu is much stronger than mine, so I'll leave it to you. ;)
Sep 12 2008
parent reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
Sergey Gromov wrote:
 Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> wrote:
 Sergey Gromov wrote:
 Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> wrote:
 Sergey Gromov wrote:
 Bill Baxter <wbaxter gmail.com> wrote:
 On Fri, Sep 12, 2008 at 11:58 PM, Sergey Gromov <snake.scaly gmail.com> wrote:
 So basically you changed
 done ==> empty
 head ==> tip
 retreat ==> prev
 ?

change, others are merely renames as you rightfully point out. Hence the "prev" which should mean both "get at the end" and "put to the end".

confused me. :-) That is cute. So r.put(e) ==> r.next = e It would also mean the copy to output idiom would become for(; ! i.done; i.next) o.next = i.head; Would be cooler if it could be just while(!i.done) o.next = i.next; .. oh well.

while (!i.done) o.next = i.next;

a) If i is an input range, then i.next returns by value. b) If i is a forward range, then i.next returns by reference. I assume that's what you had in mind?

You cannot mutate an input range, it must be in specs. Therefore it's up to the range designer what to return. LineEater will return a const reference to an internal buffer. RNG will return int. Array will return a const reference to its element. Some could return a new class instance every time.

same way C++ does. Consider something as trivial as a copy: Tgt copy(Src, Tgt)(Src src, Tgt tgt) { for (; !src.done; src.next) tgt.put(src.tip); } Problem is, you won't be able to copy e.g. an int[][] to another because the types aren't compatible.

If Src is an input range you must make a deep copy. This holds true for your current design also. So this algorithm is flawed and it's good if it won't compile.

Great point. I need to sleep on this some more.
 I don't know how to make a generic deep copy, but I believe your meta-fu 
 is much stronger than mine, so I'll leave it to you.  ;)

It is doable, and if not, Walter will make it so :o). Andrei
Sep 12 2008
parent reply Sergey Gromov <snake.scaly gmail.com> writes:
Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> wrote:
 Sergey Gromov wrote:
 Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> wrote:
 Given that in D const is transitive, we can't operate with const the 
 same way C++ does. Consider something as trivial as a copy:

 Tgt copy(Src, Tgt)(Src src, Tgt tgt)
 {
      for (; !src.done; src.next) tgt.put(src.tip);
 }

 Problem is, you won't be able to copy e.g. an int[][] to another because 
 the types aren't compatible.

If Src is an input range you must make a deep copy. This holds true for your current design also. So this algorithm is flawed and it's good if it won't compile.

Great point. I need to sleep on this some more.

Then I'll continue to think aloud. ------------------------ Here are implementations of a byte-oriented input range on top of a file in both 2- and 3-component input range bases. I thought 2-component version would be simpler. They turned out to be almost equivalent. class FileByteRange2 { private FILE* file; bool empty() { int c = fgetc(file); if (c == -1) { return true; } ungetc(c, file); return false; } ubyte next() { int c = fgetc(file); if (c == -1) { throw new Exception("File is empty!"); } return cast(ubyte) c; } } class FileByteRange3 { private FILE* file; private bool cached; ubyte head; bool done() { if (!cached) { int c = fgetc(file); if (c == -1) { return true; } head = cast(ubyte) c; } return false; } void next() { cached = false; } } ------------------------- A nice pair for .retreat() is .advance(). The words are even of the same length. Although I find it hard to imagine advancing or retreating a range. Needless to say that "next" has nothing to do with ranges at all. ------------------------ Done is not the word. Range is empty. It's not good when a range is done for. Besides, one can be done with a range before it's empty.
Sep 13 2008
next sibling parent Michel Fortin <michel.fortin michelf.com> writes:
On 2008-09-13 12:42:56 -0400, Sergey Gromov <snake.scaly gmail.com> said:

 A nice pair for .retreat() is .advance().  The words are even of the
 same length.

But then, why not .next() and .pull()? They're the same length too. :-) -- Michel Fortin michel.fortin michelf.com http://michelf.com/
Sep 14 2008
prev sibling parent reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
Sergey Gromov wrote:
 Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> wrote:
 Sergey Gromov wrote:
 Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> wrote:
 Given that in D const is transitive, we can't operate with const the 
 same way C++ does. Consider something as trivial as a copy:

 Tgt copy(Src, Tgt)(Src src, Tgt tgt)
 {
      for (; !src.done; src.next) tgt.put(src.tip);
 }

 Problem is, you won't be able to copy e.g. an int[][] to another because 
 the types aren't compatible.

your current design also. So this algorithm is flawed and it's good if it won't compile.


Then I'll continue to think aloud.

Thanks for the code samples, they're cool. I hit a problem related to the return type of head. Consider writing a range that iterates two other ranges in lockstep. A very useful generic range. I started coding it like this: struct Lockstep(R0, R1) { private R0 _r0; private R1 _r1; this(R0 r0, R1 r1) { _r0 = r0; _r1 = r1; } bool empty() { return _r0.empty || _r1.empty; } Tuple!(ElementType!(R0), ElementType!(R1)) head() { return tuple(_r0.head, _r1.head); } void next() { _r0.next; _r1.next; } } Before long I realized that the type of head was wrong. Along the way, the fact that the two ranges return by REFERENCE was irretrivably lost. Returning a ref Tuple!(ElementType!(R0), ElementType!(R1)) won't work either, because how do you move the references of the _r0.head and _r1._head in a common tuple? The type Tuple!(ref ElementType!(R0), ref ElementType!(R1)) does not exist because ref is not a type constructor. This is quite a pickle because it reveals that ref is not composable. I have a few ideas on how to handle this, but I don't want to bias anyone. If you have ideas, please fire them up! Andrei
Sep 14 2008
parent reply Sergey Gromov <snake.scaly gmail.com> writes:
Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> wrote:
 Sergey Gromov wrote:
 Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> wrote:
 Sergey Gromov wrote:
 Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> wrote:
 Given that in D const is transitive, we can't operate with const the 
 same way C++ does. Consider something as trivial as a copy:

 Tgt copy(Src, Tgt)(Src src, Tgt tgt)
 {
      for (; !src.done; src.next) tgt.put(src.tip);
 }

 Problem is, you won't be able to copy e.g. an int[][] to another because 
 the types aren't compatible.

your current design also. So this algorithm is flawed and it's good if it won't compile.


Then I'll continue to think aloud.

Thanks for the code samples, they're cool. I hit a problem related to the return type of head. Consider writing a range that iterates two other ranges in lockstep. A very useful generic range. I started coding it like this: struct Lockstep(R0, R1) { private R0 _r0; private R1 _r1; this(R0 r0, R1 r1) { _r0 = r0; _r1 = r1; } bool empty() { return _r0.empty || _r1.empty; } Tuple!(ElementType!(R0), ElementType!(R1)) head() { return tuple(_r0.head, _r1.head); } void next() { _r0.next; _r1.next; } }

Thinking aloud is fun. :P What you basically want to achieve is that Lockstep l1, l2; l1.head = l2.head; translates into l1._r0.head = l2._r0.head; l1._r1.head = l2._r1.head; all by itself. Umm... The simplest for a programmer would be compiler- supported multiple return values and multiple assignment: ref typeof(R0.head), ref typeof(R1.head) head() { return _r0.head, _r1.head; } then l1.head = l2.head is actually l1._r0.head, l1._r1.head = l2._r0.head, l2._r1.head; I'm not expecting this to be implemented though. Other methods, including returning a Tulpe!(), all return a struct. There's no use in returning references there, even if we could, as long as structs are bit-copied. All the tricks with reference fields rely substantially on the compiler performing specific actions on a per-field basis.
Sep 16 2008
parent reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
Sergey Gromov wrote:
 Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> wrote:
 Sergey Gromov wrote:
 Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> wrote:
 Sergey Gromov wrote:
 Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> wrote:
 Given that in D const is transitive, we can't operate with const the 
 same way C++ does. Consider something as trivial as a copy:

 Tgt copy(Src, Tgt)(Src src, Tgt tgt)
 {
      for (; !src.done; src.next) tgt.put(src.tip);
 }

 Problem is, you won't be able to copy e.g. an int[][] to another because 
 the types aren't compatible.

your current design also. So this algorithm is flawed and it's good if it won't compile.



Thanks for the code samples, they're cool. I hit a problem related to the return type of head. Consider writing a range that iterates two other ranges in lockstep. A very useful generic range. I started coding it like this: struct Lockstep(R0, R1) { private R0 _r0; private R1 _r1; this(R0 r0, R1 r1) { _r0 = r0; _r1 = r1; } bool empty() { return _r0.empty || _r1.empty; } Tuple!(ElementType!(R0), ElementType!(R1)) head() { return tuple(_r0.head, _r1.head); } void next() { _r0.next; _r1.next; } }

Thinking aloud is fun. :P What you basically want to achieve is that Lockstep l1, l2; l1.head = l2.head; translates into l1._r0.head = l2._r0.head; l1._r1.head = l2._r1.head; all by itself. Umm... The simplest for a programmer would be compiler- supported multiple return values and multiple assignment: ref typeof(R0.head), ref typeof(R1.head) head() { return _r0.head, _r1.head; } then l1.head = l2.head is actually l1._r0.head, l1._r1.head = l2._r0.head, l2._r1.head; I'm not expecting this to be implemented though. Other methods, including returning a Tulpe!(), all return a struct. There's no use in returning references there, even if we could, as long as structs are bit-copied. All the tricks with reference fields rely substantially on the compiler performing specific actions on a per-field basis.

I figured a deceptively simple(r) solution. With apologies to Abba, I let the code speak: /** Defines a range that moves two given ranges in lockstep. */ struct Lockstep(R0, R1) { struct ElementType { private R0 _r0; private R1 _r1; auto _0() { return _r0.head; } auto _1() { return _r1.head; } } private ElementType _e; this(R0 r0, R1 r1) { _e._r0 = r0; _e._r1 = r1; } bool empty() { return _e._r0.empty || _e._r1.empty; } ref ElementType head() { return _e; } void next() { _e._r0.next; _e._r1.next; } } Now when I write: Lockstep.(RA, RB) r; auto x = r.head._0; then I simply access whatever RA.head returns, be it by reference or value. Problem solved. Andrei
Sep 16 2008
parent Sergey Gromov <snake.scaly gmail.com> writes:
Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> wrote:
 Sergey Gromov wrote:
 What you basically want to achieve is that
 
 	Lockstep l1, l2;
 	l1.head = l2.head;
 
 translates into
 
 	l1._r0.head = l2._r0.head;
 	l1._r1.head = l2._r1.head;
 
 all by itself.  Umm...  The simplest for a programmer would be compiler-
 supported multiple return values and multiple assignment:
 
 	ref typeof(R0.head), ref typeof(R1.head) head()
 	{
 	  return _r0.head, _r1.head;
 	}
 
 then
 
 	l1.head = l2.head
 
 is actually
 
 	l1._r0.head, l1._r1.head = l2._r0.head, l2._r1.head;
 
 I'm not expecting this to be implemented though.  Other methods, 
 including returning a Tulpe!(), all return a struct.  There's no use in 
 returning references there, even if we could, as long as structs are 
 bit-copied.  All the tricks with reference fields rely substantially on 
 the compiler performing specific actions on a per-field basis.

I figured a deceptively simple(r) solution. With apologies to Abba, I let the code speak: /** Defines a range that moves two given ranges in lockstep. */ struct Lockstep(R0, R1) { struct ElementType { private R0 _r0; private R1 _r1; auto _0() { return _r0.head; } auto _1() { return _r1.head; } } private ElementType _e; this(R0 r0, R1 r1) { _e._r0 = r0; _e._r1 = r1; } bool empty() { return _e._r0.empty || _e._r1.empty; } ref ElementType head() { return _e; } void next() { _e._r0.next; _e._r1.next; } }

Yes it should work. I wish it were less complex though. BTW here's another variant of your solution:
 struct Lockstep(R0, R1)
 {
      private R0 _r0;
      private R1 _r1;

      private alias typeof(this) this_type;

      struct ElementType
      {
          private this_type outer;
          typeof(outer._r0.head) _0() { return outer._r0.head; }
          typeof(outer._r0.head) _1() { return outer._r1.head; }
      }
 
      this(R0 r0, R1 r1)
      {
         _r0 = r0;
         _r1 = r1;
      }
 
      bool empty()
      {
          return _r0.empty || _r1.empty;
      }
 
      ElementType head()
      {
          return ElementType(this);
      }
 
      void next()
      {
          _r0.next;
          _r1.next;
      }
 }

Sep 17 2008
prev sibling parent Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
Bill Baxter wrote:
 On Fri, Sep 12, 2008 at 11:22 PM, Sergey Gromov <snake.scaly gmail.com> wrote:
 I'm actually ok with either "done" or "empty".  "Done" is weird on a
 random access range, but "empty" is weird on file input range, or a
 generator.  Or on the HMM example which is "done" when it reaches an
 end state, but is not really "empty".

That's exactly right. "Empty" is a state, and "done", while also a state, also evokes process. I found it very nice to write stream ranges that become "done" after some point. "Empty" was less clear because it suggested e.g. the underlying file is empty. On the other hand it's odd to receive a fresh new range and ask yourself whether it's "done". As I wrote Walter and Bartosz a while ago, this all reminds me of a funny dialog in Alice in Wonderland (http://www.cs.indiana.edu/metastuff/wonder/ch7.html): `Take some more tea,' the March Hare said to Alice, very earnestly. `I've had nothing yet,' Alice replied in an offended tone, `so I can't take more.' `You mean you can't take LESS,' said the Hatter: `it's very easy to take MORE than nothing.'
 "head to toe" is a perfectly common expression, at least in American
 English.  Not sure why you don't like it.  But tip to toe is kinda
 cool for being 1 less letter!  And no less clear.

I like "tip" too. And as Pablo said, it's less anthropocentric than head+toe. You know what's the name of Sergey's empty range that lies right at the end of a range? Toenail! :o)
 "prev" is horrible.  I still like "retreat" best so far.  We need the
 contrapositive of "next" not the "opposite".  :-)  And since that
 doesn't exist we should just go for a word that sorta means the right
 thing and won't be  confused with being the opposite (or with being
 something else entirely like "reduce").

Retreat is a great word. I replaced reduce with it. That was easy because this time I had the foresight of defining ddoc macros for all primitive names :o). Thanks! Andrei
Sep 12 2008
prev sibling parent Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
Bill Baxter wrote:
 On Fri, Sep 12, 2008 at 2:44 PM, Andrei Alexandrescu
 <SeeWebsiteForEmail erdani.org> wrote:
 In wake of the many excellent comments and suggestions made here, I made one
 more pass through the draft proposal for ranges.

 http://ssli.ee.washington.edu/~aalexand/d/tmp/std_range.html

 There are some comments in red illustrating some uncertainties (not all),
 and the names of the primitives have been updated. Bicycle shed galore! But
 don't forget to comment on the reactor as well :o).

Looking better! I disagree with this: """ User code may pair iterators wrongly to create meaningless ranges. For example, given collections a and b of the same type, a.begin to b.begin makes no sense as a range, yet both the compiler and the runtime are hard-pressed in rejecting such mistakes. Such problems are often (though not always) avoided if range is the primitive. """ You can just as easily create nonsensical ranges using before() and after(). And I don't recall ever making the mistake of mixing up one container's begin() with another's end(). I disagree here too: """ A bidirectional range models the natural way of iterating a doubly-linked list. """ It maybe provides an efficient way to implement many algorithms on a doubly linked list. But to say it implements the natural way of iterating one is a big stretch.

I hear you! I'll amend the doc later.
 r1=r.release  ==> r.transfer?  Release sounds like ref counting (e.g. in COM)
                           Also seems like r.transfer(r1) could make
 implementation more efficient.
                           Or perhaps make it a .swap like STL.  Maybe
 you have something against .swap?

Are you kidding? I wrote the best swap in the world. Check the source of std.algorithm. You'll have to convince Bartosz about dropping the name "release". He held a gun to my head, and five of the six chambers were loaded. Couldn't take the risk. As far as efficiency goes, Walter has RVO down and all so I'm not worried. Andrei
Sep 12 2008
prev sibling next sibling parent reply "Steven Schveighoffer" <schveiguy yahoo.com> writes:
"Andrei Alexandrescu" wrote
 In wake of the many excellent comments and suggestions made here, I made 
 one more pass through the draft proposal for ranges.

 http://ssli.ee.washington.edu/~aalexand/d/tmp/std_range.html

 There are some comments in red illustrating some uncertainties (not all), 
 and the names of the primitives have been updated. Bicycle shed galore! 
 But don't forget to comment on the reactor as well :o).

You know my position ;) But here are some things: 1. I like the new names of things much better. This is a much prettier bicycle shed :) And in this case, I think the bicycle shed is a little closer to the heart of the design than the nuclear reactor. Maybe the core is better described as a bicycle shop :) 2. Saw this typo in the section on input range: e=r.head Returns the element at the current position, which is of type ElementType!(R). In case ElementType!(R) has aliases (such as a reference, pointer, or array type), the **iterator** is free to recycle it it upon the call to r.next... 3. In output iterator (more bicycle shed stuff): writing one object and moving to the next one are an organic operation What's an organic operation? I'm assuming it means 'coupled' like you can't do one without the other? I've never heard that term before. -Steve
Sep 12 2008
parent Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
Steven Schveighoffer wrote:
 "Andrei Alexandrescu" wrote
 In wake of the many excellent comments and suggestions made here, I made 
 one more pass through the draft proposal for ranges.

 http://ssli.ee.washington.edu/~aalexand/d/tmp/std_range.html

 There are some comments in red illustrating some uncertainties (not all), 
 and the names of the primitives have been updated. Bicycle shed galore! 
 But don't forget to comment on the reactor as well :o).

You know my position ;) But here are some things: 1. I like the new names of things much better. This is a much prettier bicycle shed :) And in this case, I think the bicycle shed is a little closer to the heart of the design than the nuclear reactor. Maybe the core is better described as a bicycle shop :) 2. Saw this typo in the section on input range: e=r.head Returns the element at the current position, which is of type ElementType!(R). In case ElementType!(R) has aliases (such as a reference, pointer, or array type), the **iterator** is free to recycle it it upon the call to r.next...

Fixed, thanks. You see, it's your influence :o).
 3. In output iterator (more bicycle shed stuff):
 
 writing one object and moving to the next one are an organic operation
 
 What's an organic operation?  I'm assuming it means 'coupled' like you can't 
 do one without the other?  I've never heard that term before.

http://www.merriam-webster.com/dictionary/organic (2): of, relating to, yielding, or involving the use of food produced with the use of feed or fertilizer of plant or animal origin without employment of chemically formulated fertilizers, growth stimulants, antibiotics, or pesticides Just wanted to throw you off :o). This one: 4 a: forming an integral element of a whole : fundamental <incidental music rather than organic parts of the action Francis Fergusson> b: having systematic coordination of parts : organized <an organic whole> Andrei
Sep 12 2008
prev sibling next sibling parent reply Jason House <jason.james.house gmail.com> writes:
Andrei Alexandrescu Wrote:

 In wake of the many excellent comments and suggestions made here, I made 
 one more pass through the draft proposal for ranges.
 
 http://ssli.ee.washington.edu/~aalexand/d/tmp/std_range.html
 
 There are some comments in red illustrating some uncertainties (not 
 all), and the names of the primitives have been updated. Bicycle shed 
 galore! But don't forget to comment on the reactor as well :o).

The colorful diagram is nice, but it doesn't belong at the top of the article. Is it possible to find a better spot? It may be best following the "Coding with ranges also has disadvatages" paragraph. PS: You're missing an n in disadvantages. I read past it several times, but my spell checker caught it.
Sep 12 2008
parent Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
Jason House wrote:
 Andrei Alexandrescu Wrote:
 
 In wake of the many excellent comments and suggestions made here, I
 made one more pass through the draft proposal for ranges.
 
 http://ssli.ee.washington.edu/~aalexand/d/tmp/std_range.html
 
 There are some comments in red illustrating some uncertainties (not
  all), and the names of the primitives have been updated. Bicycle
 shed galore! But don't forget to comment on the reactor as well
 :o).

The colorful diagram is nice, but it doesn't belong at the top of the article. Is it possible to find a better spot? It may be best following the "Coding with ranges also has disadvatages" paragraph.

I agree. I just plopped it in there in an intermediate revision and left it there. Moved it now.
 PS: You're missing an n in disadvantages.  I read past it several
 times, but my spell checker caught it.

Thanks, fixed. I keep on hoping someone develops a simple way for emacs to spellcheck comments... Andrei
Sep 12 2008
prev sibling next sibling parent "Bill Baxter" <wbaxter gmail.com> writes:
On Fri, Sep 12, 2008 at 11:22 PM, Sergey Gromov <snake.scaly gmail.com> wrote:
 Bill Baxter <wbaxter gmail.com> wrote:
 But I do have some suggestions nonetheless!  Marked with ==> below.

 -- Universal --
 r.done
 r.init

 --- Input : Universal ---
 e=r.head
 e=r.next
 r1=r.release  ==> r.transfer?  Release sounds like ref counting (e.g. in COM)
                           Also seems like r.transfer(r1) could make
 implementation more efficient.
                           Or perhaps make it a .swap like STL.  Maybe
 you have something against .swap?

 -- Output : Universal --
 r.put(e)

 -- Forward : Input, (optional) Output  --
 r1 = r
 r.head = e
 t=r.after(s)

 -- Bidirectional : Forward --
 e = r.toe
 r.toe = e
 r.reduce           ==> r.retreat  -- aka "pull back"
 t = r.before(s)

 -- Random access : Bidirectional --
 l = r.length
 e = r[n]
 r[n] = e
 r1 = r[n1..n2]
 ------------

 Just those two!

I've got a bit of insight! XD -- Common r.empty -- InOut v = r.next; => T R.next(); r.next = v; => T R.next(T v); -- Forward: InOut, copyable v = r.tip r.tip = v r1 = r.before(s) r1 = r.after(s) -- Bidir: Forward v = r.prev r.prev = v v = r.toe r.toe = v -- Random: Bidir no changes prev() seems very misleading, otherwise I like it.

So basically you changed done ==> empty head ==> tip retreat ==> prev ? I'm actually ok with either "done" or "empty". "Done" is weird on a random access range, but "empty" is weird on file input range, or a generator. Or on the HMM example which is "done" when it reaches an end state, but is not really "empty". "head to toe" is a perfectly common expression, at least in American English. Not sure why you don't like it. But tip to toe is kinda cool for being 1 less letter! And no less clear. "prev" is horrible. I still like "retreat" best so far. We need the contrapositive of "next" not the "opposite". :-) And since that doesn't exist we should just go for a word that sorta means the right thing and won't be confused with being the opposite (or with being something else entirely like "reduce"). --bb
Sep 12 2008
prev sibling next sibling parent "Bill Baxter" <wbaxter gmail.com> writes:
On Fri, Sep 12, 2008 at 11:58 PM, Sergey Gromov <snake.scaly gmail.com> wrote:
 So basically you changed
 done ==> empty
 head ==> tip
 retreat ==> prev
 ?

The insight was about get/put ==> next. That's the most significant change, others are merely renames as you rightfully point out. Hence the "prev" which should mean both "get at the end" and "put to the end".

Ah ok. Your switching to declaration syntax instead of usage syntax confused me. :-) That is cute. So r.put(e) ==> r.next = e It would also mean the copy to output idiom would become for(; ! i.done; i.next) o.next = i.head; Would be cooler if it could be just while(!i.done) o.next = i.next; .. oh well. But maybe it's too cute for too little gain? It's pretty darn obvious what a "put" function is for. you can also search for it more easily then a bunch of next's that could be either writes or not writes. --bb
Sep 12 2008
prev sibling next sibling parent reply Sergey Gromov <snake.scaly gmail.com> writes:
Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> wrote:
 In wake of the many excellent comments and suggestions made here, I made 
 one more pass through the draft proposal for ranges.
 
 http://ssli.ee.washington.edu/~aalexand/d/tmp/std_range.html
 
 There are some comments in red illustrating some uncertainties (not 
 all), and the names of the primitives have been updated. Bicycle shed 
 galore! But don't forget to comment on the reactor as well :o).

I've found superdan's post that convinced you. I honestly cannot see why. Suppose I want to allow recycling in empty/next pair. Then I cpecify that: * next() returns stuff; * stuff is guaranteed to survive until the subsequent call to next(); * stuff becomes undefined after next() is called again; * stuff is guaranteed to survive intermediate calls to empty(). The last point can make implementation more complex in some cases but it's definitely possible to implement. If we revert to empty/next, or whateve names you think better, then we can also revert to using first/last for forward/bidirectional range outermost elements and stop that body parts discussion. I really hope this makes sense.
Sep 12 2008
parent Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
Sergey Gromov wrote:
 Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> wrote:
 In wake of the many excellent comments and suggestions made here, I made 
 one more pass through the draft proposal for ranges.

 http://ssli.ee.washington.edu/~aalexand/d/tmp/std_range.html

 There are some comments in red illustrating some uncertainties (not 
 all), and the names of the primitives have been updated. Bicycle shed 
 galore! But don't forget to comment on the reactor as well :o).

I've found superdan's post that convinced you. I honestly cannot see why. Suppose I want to allow recycling in empty/next pair. Then I cpecify that: * next() returns stuff; * stuff is guaranteed to survive until the subsequent call to next(); * stuff becomes undefined after next() is called again; * stuff is guaranteed to survive intermediate calls to empty(). The last point can make implementation more complex in some cases but it's definitely possible to implement. If we revert to empty/next, or whateve names you think better, then we can also revert to using first/last for forward/bidirectional range outermost elements and stop that body parts discussion. I really hope this makes sense.

He did convince me. With your API you can't write efficient algorithms that work with input ranges and forward ranges crawling on actual containers. Andrei
Sep 12 2008
prev sibling next sibling parent "Bill Baxter" <wbaxter gmail.com> writes:
On Sat, Sep 13, 2008 at 12:29 AM, Andrei Alexandrescu
<SeeWebsiteForEmail erdani.org> wrote:
 Bill Baxter wrote:
 r1=r.release  ==> r.transfer?  Release sounds like ref counting (e.g. in
 COM)
                          Also seems like r.transfer(r1) could make
 implementation more efficient.
                          Or perhaps make it a .swap like STL.  Maybe
 you have something against .swap?

Are you kidding? I wrote the best swap in the world. Check the source of std.algorithm.

I mean like stdvector.swap(stdvector0), not the algorithm swap. Or maybe your swap subsumes both kinds?
 You'll have to convince Bartosz about dropping the name "release". He held a
 gun to my head, and five of the six chambers were loaded. Couldn't take the
 risk.

Hmm. I would have thought Walter would veto "release" since he cared enough about COM to write it into the D spec. Release is *the* word used throughout COM to signal that you want to decrement an objects reference counter. So basically you do an obj->Release on a COM object any time you would have done a "delete obj" on a regular object.
 As far as efficiency goes, Walter has RVO down and all so I'm not
 worried.

What if you don't put a return object there? Will that be ok too? Even if there are OS resources and such involved? --bb
Sep 12 2008
prev sibling next sibling parent reply Ary Borenszweig <ary esperanto.org.ar> writes:
Andrei Alexandrescu wrote:
 In wake of the many excellent comments and suggestions made here, I made 
 one more pass through the draft proposal for ranges.
 
 http://ssli.ee.washington.edu/~aalexand/d/tmp/std_range.html
 
 There are some comments in red illustrating some uncertainties (not 
 all), and the names of the primitives have been updated. Bicycle shed 
 galore! But don't forget to comment on the reactor as well :o).
 
 
 Andrei

Two questions: - Is it possible to add elements to a range? Suppose a linked list, you want to traverse it's elements until a condition is met on an element, and then add something after it. Or before it. I see there's "put", but it also says "An output range models a one-pass forward writing iteration.", so I think it's not the same. - The same question applies if you want to delete an element from a range. As for the names, I think it's impossible to find one-word names for the concepts you want to express. I always prefer expressive, easy to remember names instead of shorter but less meaningful names. Maybe: head --> first toe --> last next --> moveFirst / dropFirst / skipFirst retreat -> moveLast / dropLast / skipLast done --> isEmpty (it's strange to think of a range as done) So it's like dropFirst / dropLast shrink the range from either end, until it's empty. Sorry if these things were already discussed, I didn't have time to read the hundreds of posts of the previous discussion, just some and the next proposal.
Sep 12 2008
next sibling parent Sergey Gromov <snake.scaly gmail.com> writes:
Ary Borenszweig <ary esperanto.org.ar> wrote:
 Two questions:
 - Is it possible to add elements to a range? Suppose a linked list, you 
 want to traverse it's elements until a condition is met on an element, 
 and then add something after it. Or before it. I see there's "put", but 
 it also says "An output range models a one-pass forward writing 
 iteration.", so I think it's not the same.
 - The same question applies if you want to delete an element from a range.

The idea is that a container is responsible for mutators, not range. Therefore you'd have list.insertBefore(range, value). Likewise there'd be list.remove(range), list.removeBefore(range) and list.removeFirst (range).
Sep 12 2008
prev sibling parent reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
Ary Borenszweig wrote:
 Andrei Alexandrescu wrote:
 In wake of the many excellent comments and suggestions made here, I 
 made one more pass through the draft proposal for ranges.

 http://ssli.ee.washington.edu/~aalexand/d/tmp/std_range.html

 There are some comments in red illustrating some uncertainties (not 
 all), and the names of the primitives have been updated. Bicycle shed 
 galore! But don't forget to comment on the reactor as well :o).


 Andrei

Two questions: - Is it possible to add elements to a range? Suppose a linked list, you want to traverse it's elements until a condition is met on an element, and then add something after it. Or before it. I see there's "put", but it also says "An output range models a one-pass forward writing iteration.", so I think it's not the same.

Never. Ranges never grow. You need access to the "mother" container, which will offer primitives for insertion and removal of elements.
 - The same question applies if you want to delete an element from a range.

Same.
 As for the names, I think it's impossible to find one-word names for the 
 concepts you want to express. I always prefer expressive, easy to 
 remember names instead of shorter but less meaningful names. Maybe:
 
 head --> first
 toe --> last
 next --> moveFirst / dropFirst / skipFirst
 retreat -> moveLast / dropLast / skipLast
 done --> isEmpty (it's strange to think of a range as done)
 
 So it's like dropFirst / dropLast shrink the range from either end, 
 until it's empty.
 
 Sorry if these things were already discussed, I didn't have time to read 
 the hundreds of posts of the previous discussion, just some and the next 
 proposal.

Yeah it's getting crazy. Andrei
Sep 12 2008
parent reply Walter Bright <newshound1 digitalmars.com> writes:
Andrei Alexandrescu wrote:
 Ary Borenszweig wrote:
 - Is it possible to add elements to a range? Suppose a linked list, 
 you want to traverse it's elements until a condition is met on an 
 element, and then add something after it. Or before it. I see there's 
 "put", but it also says "An output range models a one-pass forward 
 writing iteration.", so I think it's not the same.

Never. Ranges never grow. You need access to the "mother" container, which will offer primitives for insertion and removal of elements.

I agree. I don't think it is ever a good idea to try to add/remove elements of a container while iterating over it. foreach disallows it.
Sep 12 2008
next sibling parent "Steven Schveighoffer" <schveiguy yahoo.com> writes:
"Walter Bright" wrote
 Andrei Alexandrescu wrote:
 Ary Borenszweig wrote:
 - Is it possible to add elements to a range? Suppose a linked list, you 
 want to traverse it's elements until a condition is met on an element, 
 and then add something after it. Or before it. I see there's "put", but 
 it also says "An output range models a one-pass forward writing 
 iteration.", so I think it's not the same.

Never. Ranges never grow. You need access to the "mother" container, which will offer primitives for insertion and removal of elements.

I agree. I don't think it is ever a good idea to try to add/remove elements of a container while iterating over it. foreach disallows it.

I allow removal during foreach in dcollections. However, it is done by the opApply function at the request of the delegate (I pass in a ref boolean). I don't think this method violates the foreach contract. I use this when I'm iterating over a container trying to decide whether elements should be removed. If you don't allow this, then either you must use an iterator/range model, or build a separate list of elements to delete once you have exited the foreach loop. Using this model, I can do things like O(n) traverse and remove from an array. -Steve
Sep 12 2008
prev sibling parent reply Sean Kelly <sean invisibleduck.org> writes:
Walter Bright wrote:
 Andrei Alexandrescu wrote:
 Ary Borenszweig wrote:
 - Is it possible to add elements to a range? Suppose a linked list, 
 you want to traverse it's elements until a condition is met on an 
 element, and then add something after it. Or before it. I see there's 
 "put", but it also says "An output range models a one-pass forward 
 writing iteration.", so I think it's not the same.

Never. Ranges never grow. You need access to the "mother" container, which will offer primitives for insertion and removal of elements.

I agree. I don't think it is ever a good idea to try to add/remove elements of a container while iterating over it. foreach disallows it.

I've found this invaluable at times. And it's actually supported by C++ containers. Something along the lines of: for( auto i = c.begin(); i != c.end(); ) { if( shouldRemove( *i ) ) i = c.remove( i ); else ++i; } In fact, it may not even be possible to remove elements of a container outside the inspection loop. For example, let's say the container is actually a SQL resultset and the iterator is a cursor. It's easy to mark a row as 'deleted' in this instance, but storing information to perform a deletion later often simply can't be done. Though I suppose you could argue that the 'container' abstraction may be inappropriate for such temporal data. Sean
Sep 12 2008
next sibling parent reply Walter Bright <newshound1 digitalmars.com> writes:
Sean Kelly wrote:
 I've found this invaluable at times.  And it's actually supported by C++ 
 containers.

It's a bad idea, as it can screw up optimization. For example, if you are using a change-and-swap method of updating a collection, the collection cached by the foreach may get trashed.
Sep 12 2008
next sibling parent Sean Kelly <sean invisibleduck.org> writes:
Walter Bright wrote:
 Sean Kelly wrote:
 I've found this invaluable at times.  And it's actually supported by 
 C++ containers.

It's a bad idea, as it can screw up optimization. For example, if you are using a change-and-swap method of updating a collection, the collection cached by the foreach may get trashed.

That's fine... the optimizer has to impose certain requirements. For such things I'll simply continue using for loops. Sean
Sep 12 2008
prev sibling parent Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
Walter Bright wrote:
 Sean Kelly wrote:
 I've found this invaluable at times.  And it's actually supported by 
 C++ containers.

It's a bad idea, as it can screw up optimization. For example, if you are using a change-and-swap method of updating a collection, the collection cached by the foreach may get trashed.

I like the erase(remove) idiom used by STL, see http://en.wikibooks.org/wiki/More_C%2B%2B_Idioms/Erase-Remove. Oddly enough, it's a variant of moveToFront, the function that allegedly nobody needs :o). It's efficient and safe. Removing from a collection while iterating runs the risk of becoming quadratic if more than a few elements get removed. Andrei
Sep 12 2008
prev sibling parent Sergey Gromov <snake.scaly gmail.com> writes:
Sean Kelly <sean invisibleduck.org> wrote:
 Walter Bright wrote:
 Andrei Alexandrescu wrote:
 Ary Borenszweig wrote:
 - Is it possible to add elements to a range? Suppose a linked list, 
 you want to traverse it's elements until a condition is met on an 
 element, and then add something after it. Or before it. I see there's 
 "put", but it also says "An output range models a one-pass forward 
 writing iteration.", so I think it's not the same.

Never. Ranges never grow. You need access to the "mother" container, which will offer primitives for insertion and removal of elements.

I agree. I don't think it is ever a good idea to try to add/remove elements of a container while iterating over it. foreach disallows it.

In fact, it may not even be possible to remove elements of a container outside the inspection loop. For example, let's say the container is actually a SQL resultset and the iterator is a cursor. It's easy to mark a row as 'deleted' in this instance, but storing information to perform a deletion later often simply can't be done. Though I suppose you could argue that the 'container' abstraction may be inappropriate for such temporal data.

I think that the Input Range abstraction works fine here. That is, the 'head' of the Select range is actually a cursor object which has a markForDeletion() method. It may be bad to allow deletion in generic case, but you can always have a special case to work around.
Sep 13 2008
prev sibling next sibling parent "Bill Baxter" <wbaxter gmail.com> writes:
On Sun, Sep 14, 2008 at 8:55 PM, Michel Fortin
<michel.fortin michelf.com> wrote:
 On 2008-09-13 12:42:56 -0400, Sergey Gromov <snake.scaly gmail.com> said:

 A nice pair for .retreat() is .advance().  The words are even of the
 same length.


My reasoning was that if you pick any pair of antonyms then its going to suggest that the operation applies to the same thing. It's the opposite direction *and* the opposite thing, which is why I compared it to the "contrapositive" in another message. I like retreat because it's the exact opposite of a word that's similar to "next", but not. Therefore it doesn't lead you to expect that it manipulates the same element. Another way of looking at it is that both ends are actually doing the same thing -- they're both "advancing" or doing "next". Just their notion of next is different. In that sense you could call it more like the "converse" operation. You could use a notation like STL reverse iterators and have "rhead" and "rnext", (where r is for "reverse"). I like that in that it recognizes that. I don't like using made-up words, though.
 But then, why not .next() and .pull()? They're the same length too. :-)

I thought about pull -- I think I discarded it sounds like you're pulling something in from somewhere and as a result your range should become bigger. --bb
Sep 14 2008
prev sibling next sibling parent reply Bruno Medeiros <brunodomedeiros+spam com.gmail> writes:
Andrei Alexandrescu wrote:
 In wake of the many excellent comments and suggestions made here, I made 
 one more pass through the draft proposal for ranges.
 
 http://ssli.ee.washington.edu/~aalexand/d/tmp/std_range.html
 
 There are some comments in red illustrating some uncertainties (not 
 all), and the names of the primitives have been updated. Bicycle shed 
 galore! But don't forget to comment on the reactor as well :o).
 
 
 Andrei

""" All ranges satisfy certain invariants outlined below. (r is an object of a range type R.) """ By "object" you actually mean struct no? Struct instance to be even more precise. Also, some more on important bike shed issues: for (; !src.done; src.next) { tgt.put(src.head); } As a matter of coding style conventions, I would say that using the implicit property function call feature on a function that changes state is *bad* style, and surely hope the community would agree on that. So "src.next" would be must better as "src.next()" as "src.next" really just makes me cringe. -- Bruno Medeiros - Software Developer, MSc. in CS/E graduate http://www.prowiki.org/wiki4d/wiki.cgi?BrunoMedeiros#D
Sep 25 2008
next sibling parent reply Sergey Gromov <snake.scaly gmail.com> writes:
In article <gbgpak$2q10$1 digitalmars.com>, 
brunodomedeiros+spam com.gmail says...
 Also, some more on important bike shed issues:
      for (; !src.done; src.next)
      {
          tgt.put(src.head);
      }
 
 As a matter of coding style conventions, I would say that using the 
 implicit property function call feature on a function that changes state 
 is *bad* style, and surely hope the community would agree on that.
 So "src.next" would be must better as "src.next()" as "src.next" really 
 just makes me cringe.

I think that property function call feature in general adds an unnecessary ambiguity to the language. I'd prefer functions to be callable only with regular function call syntax, and properties be usable only with member access syntax. The same stands for 'unified function call' feature: if you want to inject a method into an 'array of chars' class you do so explicitly, and only the member call syntax is allowed on that method. Otherwise code tends to become ambiguous and unreadable.
Sep 25 2008
next sibling parent reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
Sergey Gromov wrote:
 In article <gbgpak$2q10$1 digitalmars.com>, 
 brunodomedeiros+spam com.gmail says...
 Also, some more on important bike shed issues:
      for (; !src.done; src.next)
      {
          tgt.put(src.head);
      }

 As a matter of coding style conventions, I would say that using the 
 implicit property function call feature on a function that changes state 
 is *bad* style, and surely hope the community would agree on that.
 So "src.next" would be must better as "src.next()" as "src.next" really 
 just makes me cringe.

I think that property function call feature in general adds an unnecessary ambiguity to the language. I'd prefer functions to be callable only with regular function call syntax, and properties be usable only with member access syntax. The same stands for 'unified function call' feature: if you want to inject a method into an 'array of chars' class you do so explicitly, and only the member call syntax is allowed on that method. Otherwise code tends to become ambiguous and unreadable.

Experience with other languages has shown that using identical syntax for genuine member access and member function access helps maintainability because it allows a type implementer to switch back and forth between implementing a property as a direct member or as a function, transparently to that type's use. Two nice examples of the undesirability of distinct notation are the std::pair first and second members in the STL, which made what seemed like an innocuous decision turn into a catastrophe, and also std::numeric_limits<T>::min and max, which partially prompted creation of an entire language feature(!). Andrei
Sep 25 2008
next sibling parent reply Sergey Gromov <snake.scaly gmail.com> writes:
In article <gbgu0h$5sq$1 digitalmars.com>, SeeWebsiteForEmail erdani.org 
says...
 Sergey Gromov wrote:
 I think that property function call feature in general adds an 
 unnecessary ambiguity to the language.  I'd prefer functions to be 
 callable only with regular function call syntax, and properties be 
 usable only with member access syntax.  The same stands for 'unified 
 function call' feature: if you want to inject a method into an 'array of 
 chars' class you do so explicitly, and only the member call syntax is 
 allowed on that method.  Otherwise code tends to become ambiguous and 
 unreadable.

Experience with other languages has shown that using identical syntax for genuine member access and member function access helps maintainability because it allows a type implementer to switch back and forth between implementing a property as a direct member or as a function, transparently to that type's use.

Sorry I may have been unclear. I'm not against interchangeability between properties and property accessor methods. I'm against using property accessors as methods, and against using methods as if they were property accessors. The current situation actually breaks maintainability because after somebody used .length() you cannot replace it with a public variable anymore. And you cannot replace public void delegate(int) foo; with an accessor method because the code 'instance.foo();' will stop working as it used to.
Sep 25 2008
parent reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
Sergey Gromov wrote:
 In article <gbgu0h$5sq$1 digitalmars.com>, SeeWebsiteForEmail erdani.org 
 says...
 Sergey Gromov wrote:
 I think that property function call feature in general adds an 
 unnecessary ambiguity to the language.  I'd prefer functions to be 
 callable only with regular function call syntax, and properties be 
 usable only with member access syntax.  The same stands for 'unified 
 function call' feature: if you want to inject a method into an 'array of 
 chars' class you do so explicitly, and only the member call syntax is 
 allowed on that method.  Otherwise code tends to become ambiguous and 
 unreadable.

for genuine member access and member function access helps maintainability because it allows a type implementer to switch back and forth between implementing a property as a direct member or as a function, transparently to that type's use.

Sorry I may have been unclear. I'm not against interchangeability between properties and property accessor methods. I'm against using property accessors as methods, and against using methods as if they were property accessors. The current situation actually breaks maintainability because after somebody used .length() you cannot replace it with a public variable anymore. And you cannot replace public void delegate(int) foo; with an accessor method because the code 'instance.foo();' will stop working as it used to.

I am a bit confused about terminology. Could you please clarify with examples what you mean, also defining all terms (e.g. what does "property accessor" mean?) Thanks. Andrei
Sep 26 2008
parent reply Sergey Gromov <snake.scaly gmail.com> writes:
In article <gbiqbo$m1e$1 digitalmars.com>, SeeWebsiteForEmail erdani.org 
says...
 Sergey Gromov wrote:
 In article <gbgu0h$5sq$1 digitalmars.com>, SeeWebsiteForEmail erdani.org 
 says...
 Sergey Gromov wrote:
 I think that property function call feature in general adds an 
 unnecessary ambiguity to the language.  I'd prefer functions to be 
 callable only with regular function call syntax, and properties be 
 usable only with member access syntax.  The same stands for 'unified 
 function call' feature: if you want to inject a method into an 'array of 
 chars' class you do so explicitly, and only the member call syntax is 
 allowed on that method.  Otherwise code tends to become ambiguous and 
 unreadable.

for genuine member access and member function access helps maintainability because it allows a type implementer to switch back and forth between implementing a property as a direct member or as a function, transparently to that type's use.

Sorry I may have been unclear. I'm not against interchangeability between properties and property accessor methods. I'm against using property accessors as methods, and against using methods as if they were property accessors. The current situation actually breaks maintainability because after somebody used .length() you cannot replace it with a public variable anymore. And you cannot replace public void delegate(int) foo; with an accessor method because the code 'instance.foo();' will stop working as it used to.

I am a bit confused about terminology. Could you please clarify with examples what you mean, also defining all terms (e.g. what does "property accessor" mean?) Thanks.

Property accessor is a method which is invoked when you syntactically access a data field. They are called getters and setters in many languages. In current D, any function or method with zero or one argument is considered a property accessor: class Foo { int prop() { return field + 1; } int prop(int value) { return field = value + 3; } private int field; } void useFoo(Foo f) { auto x = f.prop; f.prop = 5; auto y = f.prop(); // also allowed f.prop(8); // ditto } The 'prop' family of methods are property accessors in my terminology, and they can be replaced with an actual data field: class Foo { int prop; } void useFoo(Foo f) { auto x = f.prop; // fine! f.prop = 5; // works auto y = f.prop(); // Error: function expected f.prop(8); // Error: function expected } You see, properties and methods are *not* interchangeable in current D. Therefore your correct thesis:
 Experience with other languages has shown that using identical syntax 
 for genuine member access and member function access helps 
 maintainability because it allows a type implementer to switch back and 
 forth between implementing a property as a direct member or as a 
 function, transparently to that type's use.



does not apply to D. Here's another example: class Bar { int delegate() meth; } int useBar(Bar b) { return b.meth(); } Can you replace 'meth' with a getter? class Bar { int delegate() meth() { return {return 5;}; } } int useBar(Bar b) { return b.meth(); // Error: cannot implicitly convert expression // (b.meth()) of type int delegate() to int } No you cannot. But you could be able to do all those nice things if you had an explicit syntax for getters and setters: class Foo { property int prop() {...} property int prop(int value) {...} property int prop(int v, char c) {...} // syntax error, 2 arguments property int delegate() meth() {...} } void useFoo(Foo f) { auto x = f.prop; // OK f.prop = 5; // OK auto y = f.prop(); // syntax error, function expected f.prop(8); // syntax error, function expected int z = f.meth(); // OK, the delegate is called } That being said, I want the same explicit syntax for property and method injection, which are elements of aspect-oriented programming. Let's call it inject: inject T[] reverse(T)(T[] arr) {...} inject property T middle(T)(T[] arr) { return arr[$/2]; } auto s = "abcde"; auto sr = s.reverse(); // "edcba" auto sm = s.middle; // 'c' reverse(s); // undefined identifier With this, you receive an additional freedom which comes from removing the ambiguity. Imagine I want my personal hell: class Hell { private enum random = 666; private inject property size_t realLen(T)(T[] any) { return random; } void tellTheTruth(string s) { writefln("real length of '%s' is %s", s, s.realLen); } } Here I've injected a useful property into all arrays but the injection is private to my hell only so I can pretend there's still paradise around.
Sep 26 2008
parent reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
Sergey Gromov wrote:
 In article <gbiqbo$m1e$1 digitalmars.com>, SeeWebsiteForEmail erdani.org 
 says...
 Sergey Gromov wrote:
 In article <gbgu0h$5sq$1 digitalmars.com>, SeeWebsiteForEmail erdani.org 
 says...
 Sergey Gromov wrote:
 I think that property function call feature in general adds an 
 unnecessary ambiguity to the language.  I'd prefer functions to be 
 callable only with regular function call syntax, and properties be 
 usable only with member access syntax.  The same stands for 'unified 
 function call' feature: if you want to inject a method into an 'array of 
 chars' class you do so explicitly, and only the member call syntax is 
 allowed on that method.  Otherwise code tends to become ambiguous and 
 unreadable.

for genuine member access and member function access helps maintainability because it allows a type implementer to switch back and forth between implementing a property as a direct member or as a function, transparently to that type's use.

between properties and property accessor methods. I'm against using property accessors as methods, and against using methods as if they were property accessors. The current situation actually breaks maintainability because after somebody used .length() you cannot replace it with a public variable anymore. And you cannot replace public void delegate(int) foo; with an accessor method because the code 'instance.foo();' will stop working as it used to.

examples what you mean, also defining all terms (e.g. what does "property accessor" mean?) Thanks.

Property accessor is a method which is invoked when you syntactically access a data field. They are called getters and setters in many languages. In current D, any function or method with zero or one argument is considered a property accessor: class Foo { int prop() { return field + 1; } int prop(int value) { return field = value + 3; } private int field; } void useFoo(Foo f) { auto x = f.prop; f.prop = 5; auto y = f.prop(); // also allowed f.prop(8); // ditto }

Ok, so far so good.
 The 'prop' family of methods are property accessors in my terminology, 
 and they can be replaced with an actual data field:
 
 class Foo
 {
   int prop;
 }
 
 void useFoo(Foo f)
 {
   auto x = f.prop;  // fine!
   f.prop = 5;  // works
 
   auto y = f.prop();  // Error: function expected
   f.prop(8);  // Error: function expected
 }
 
 You see, properties and methods are *not* interchangeable in current D.  
 Therefore your correct thesis:
 
 Experience with other languages has shown that using identical syntax 
 for genuine member access and member function access helps 
 maintainability because it allows a type implementer to switch back and 
 forth between implementing a property as a direct member or as a 
 function, transparently to that type's use.



does not apply to D.

It does apply, just only one direction. From the above, it looks like a good guideline is to always use the syntax: auto x = f.prop; f.prop = x; because it is more general. And indeed, consider something as innocuous as the empty member for a range. Some here said I should write range.empty() throughout. But that, aside from wrist and eye pain, precludes infinite ranges from implementing empty as simple as: struct Generator { ... enum empty = false; } But if that implementation were allowed, that would be a boon for generic code because it can easily detect infinite ranges statically.
 Here's another example:
 
 class Bar
 {
   int delegate() meth;
 }
 
 int useBar(Bar b)
 {
   return b.meth();
 }
 
 Can you replace 'meth' with a getter?
 
 class Bar
 {
   int delegate() meth()
   {
     return {return 5;};
   }
 
 }
 
 int useBar(Bar b)
 {
   return b.meth();  // Error: cannot implicitly convert expression
   // (b.meth()) of type int delegate() to int
 }
 
 No you cannot.  But you could be able to do all those nice things if you 
 had an explicit syntax for getters and setters:
 
 class Foo
 {
   property int prop() {...}
   property int prop(int value) {...}
   property int prop(int v, char c) {...} // syntax error, 2 arguments
   property int delegate() meth() {...}
 }

I agree. But there's a simpler solution: int useBar(Bar crystal) { return (crystal.meth)(); } Looks a bit uneasy on the eye? I agree. But count the lines you use member delegates in; then count the lines you use non-delegate member access; divide the first by the second; multiply by 100; and... is that worth a language feature?
 void useFoo(Foo f)
 {
   auto x = f.prop;  // OK
   f.prop = 5;  // OK
 
   auto y = f.prop();  // syntax error, function expected
   f.prop(8);  // syntax error, function expected
 
   int z = f.meth();  // OK, the delegate is called
 }
 
 That being said, I want the same explicit syntax for property and method 
 injection, which are elements of aspect-oriented programming.  Let's 
 call it inject:
 
 inject T[] reverse(T)(T[] arr) {...}
 inject property T middle(T)(T[] arr)
 {
   return arr[$/2];
 }
 auto s = "abcde";
 auto sr = s.reverse();  // "edcba"
 auto sm = s.middle;  // 'c'
 reverse(s);  // undefined identifier
 
 With this, you receive an additional freedom which comes from removing 
 the ambiguity.  Imagine I want my personal hell:
 
 class Hell
 {
   private enum random = 666;
   private inject property size_t realLen(T)(T[] any)
   {
     return random;
   }
   void tellTheTruth(string s)
   {
     writefln("real length of '%s' is %s", s, s.realLen);
   }
 }
 
 Here I've injected a useful property into all arrays but the injection 
 is private to my hell only so I can pretend there's still paradise 
 around.

I can imagine you want your personal hell, but I hardly can understand its usefulness to you or the rest of us :o). Andrei
Sep 27 2008
parent reply Sergey Gromov <snake.scaly gmail.com> writes:
Sat, 27 Sep 2008 08:56:42 -0500,
Andrei Alexandrescu wrote:
 Sergey Gromov wrote:
 The 'prop' family of methods are property accessors in my terminology, 
 and they can be replaced with an actual data field:
 
 class Foo
 {
   int prop;
 }
 
 void useFoo(Foo f)
 {
   auto x = f.prop;  // fine!
   f.prop = 5;  // works
 
   auto y = f.prop();  // Error: function expected
   f.prop(8);  // Error: function expected
 }
 
 You see, properties and methods are *not* interchangeable in current D.  
 Therefore your correct thesis:
 
 Experience with other languages has shown that using identical syntax 
 for genuine member access and member function access helps 
 maintainability because it allows a type implementer to switch back and 
 forth between implementing a property as a direct member or as a 
 function, transparently to that type's use.



does not apply to D.

It does apply, just only one direction. From the above, it looks like a good guideline is to always use the syntax: auto x = f.prop; f.prop = x; because it is more general.

Now you say that for maintainability to work all users of your library must respect some *guidelines*. In practice this means that if you have a property method you cannot change it to be a variable because everybody *never* respects guidelines---you know, because you can never please him.
 And indeed, consider something as innocuous 
 as the empty member for a range. Some here said I should write 
 range.empty() throughout. But that, aside from wrist and eye pain, 
 precludes infinite ranges from implementing empty as simple as:
 
 struct Generator {
      ...
      enum empty = false;
 }
 
 But if that implementation were allowed, that would be a boon for 
 generic code because it can easily detect infinite ranges statically.

Again, my proposal is not about how you write your code. It's about how others *use* your code and how they limit your freedom in changing your code. With explicit syntax, if you specified 'empty' being a property and someone put parens after it it would be a syntax error. Right now you may yell about it's intended to be interchangeable with a constant but someone *will* put parens after it and *their* code will break after you change *your* library.
 Here's another example:
 
 class Bar
 {
   int delegate() meth;
 }
 
 int useBar(Bar b)
 {
   return b.meth();
 }
 
 Can you replace 'meth' with a getter?
 
 class Bar
 {
   int delegate() meth()
   {
     return {return 5;};
   }
 }
 
 int useBar(Bar b)
 {
   return b.meth();  // Error: cannot implicitly convert expression
   // (b.meth()) of type int delegate() to int
 }
 
 No you cannot.  But you could be able to do all those nice things if you 
 had an explicit syntax for getters and setters:
 
 class Foo
 {
   property int prop() {...}
   property int prop(int value) {...}
   property int prop(int v, char c) {...} // syntax error, 2 arguments
   property int delegate() meth() {...}
 }

I agree. But there's a simpler solution: int useBar(Bar crystal) { return (crystal.meth)(); }

Yes there are ways to write generic code. But there are also ways to write non-generic code, and it may be user code which you mignt have no influence upon, and you may be required to keep the user code working no matter what. Explicit properties grant you such influence, making it impossible for the user to write non-generic code.
 Looks a bit uneasy on the eye? I agree. But count the lines you use 
 member delegates in; then count the lines you use non-delegate member 
 access; divide the first by the second; multiply by 100; and... is that 
 worth a language feature?

Explicit properties solve a much wider range of problems than introduced by naked delegate members. They just happen to solve this issue, too.
 That being said, I want the same explicit syntax for property and method 
 injection, which are elements of aspect-oriented programming.  Let's 
 call it inject:
 
 inject T[] reverse(T)(T[] arr) {...}
 inject property T middle(T)(T[] arr)
 {
   return arr[$/2];
 }
 auto s = "abcde";
 auto sr = s.reverse();  // "edcba"
 auto sm = s.middle;  // 'c'
 reverse(s);  // undefined identifier

I can imagine you want your personal hell, but I hardly can understand its usefulness to you or the rest of us :o).

Do you have a clear understanding of how name resolution should work in 'unified call syntax' as it is now? bool even(char[] a) {...} class Foo { private OptimizedEvenComputer ec; bool even(char[] a) {return ec.even(a);} void foo() { bool even(char[] a) {...} char[] l; auto x = l.even; auto y = even(l); } } What happens here? What *should* happen here? Even now people are confused because the mechanism works against their expectations. And, most importantly, they disagree in their expectations. Explicit injection solves this ambiguity once and for all. And I'm yet to hear *actual* arguments from you. Till now it was that you didn't like to type parentheses, you didn't like a bit of strictness in the language, and you didn't see any problem in not being able to replace methods with vars. Oh wait, you did see the problem. So what's your point then?
Sep 27 2008
next sibling parent Jason House <jason.james.house gmail.com> writes:
Sergey Gromov wrote:

 Do you have a clear understanding of how name resolution should work in
 'unified call syntax' as it is now?
 
 bool even(char[] a) {...}
 class Foo
 {
   private OptimizedEvenComputer ec;
   bool even(char[] a) {return ec.even(a);}
   void foo()
   {
     bool even(char[] a) {...}
     char[] l;
     auto x = l.even;
     auto y = even(l);
   }
 }

I would really hope that the D2 compiler would flag Foo.foo.even as a shadowing violation. I am not a D2 user yet, but I'd hope that Foo.even and Foo.foo.even would not override l.even; it just seems too error prone.
Sep 27 2008
prev sibling next sibling parent reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
Sergey Gromov wrote:
 Sat, 27 Sep 2008 08:56:42 -0500,
 Andrei Alexandrescu wrote:
 Sergey Gromov wrote:
 The 'prop' family of methods are property accessors in my terminology, 
 and they can be replaced with an actual data field:

 class Foo
 {
   int prop;
 }

 void useFoo(Foo f)
 {
   auto x = f.prop;  // fine!
   f.prop = 5;  // works

   auto y = f.prop();  // Error: function expected
   f.prop(8);  // Error: function expected
 }

 You see, properties and methods are *not* interchangeable in current D.  
 Therefore your correct thesis:

 Experience with other languages has shown that using identical syntax 
 for genuine member access and member function access helps 
 maintainability because it allows a type implementer to switch back and 
 forth between implementing a property as a direct member or as a 
 function, transparently to that type's use.




good guideline is to always use the syntax: auto x = f.prop; f.prop = x; because it is more general.

Now you say that for maintainability to work all users of your library must respect some *guidelines*. In practice this means that if you have a property method you cannot change it to be a variable because everybody *never* respects guidelines---you know, because you can never please him.
 And indeed, consider something as innocuous 
 as the empty member for a range. Some here said I should write 
 range.empty() throughout. But that, aside from wrist and eye pain, 
 precludes infinite ranges from implementing empty as simple as:

 struct Generator {
      ...
      enum empty = false;
 }

 But if that implementation were allowed, that would be a boon for 
 generic code because it can easily detect infinite ranges statically.

Again, my proposal is not about how you write your code. It's about how others *use* your code and how they limit your freedom in changing your code. With explicit syntax, if you specified 'empty' being a property and someone put parens after it it would be a syntax error. Right now you may yell about it's intended to be interchangeable with a constant but someone *will* put parens after it and *their* code will break after you change *your* library.
 Here's another example:

 class Bar
 {
   int delegate() meth;
 }

 int useBar(Bar b)
 {
   return b.meth();
 }

 Can you replace 'meth' with a getter?

 class Bar
 {
   int delegate() meth()
   {
     return {return 5;};
   }
 }

 int useBar(Bar b)
 {
   return b.meth();  // Error: cannot implicitly convert expression
   // (b.meth()) of type int delegate() to int
 }

 No you cannot.  But you could be able to do all those nice things if you 
 had an explicit syntax for getters and setters:

 class Foo
 {
   property int prop() {...}
   property int prop(int value) {...}
   property int prop(int v, char c) {...} // syntax error, 2 arguments
   property int delegate() meth() {...}
 }

int useBar(Bar crystal) { return (crystal.meth)(); }

Yes there are ways to write generic code. But there are also ways to write non-generic code, and it may be user code which you mignt have no influence upon, and you may be required to keep the user code working no matter what. Explicit properties grant you such influence, making it impossible for the user to write non-generic code.
 Looks a bit uneasy on the eye? I agree. But count the lines you use 
 member delegates in; then count the lines you use non-delegate member 
 access; divide the first by the second; multiply by 100; and... is that 
 worth a language feature?

Explicit properties solve a much wider range of problems than introduced by naked delegate members. They just happen to solve this issue, too.
 That being said, I want the same explicit syntax for property and method 
 injection, which are elements of aspect-oriented programming.  Let's 
 call it inject:

 inject T[] reverse(T)(T[] arr) {...}
 inject property T middle(T)(T[] arr)
 {
   return arr[$/2];
 }
 auto s = "abcde";
 auto sr = s.reverse();  // "edcba"
 auto sm = s.middle;  // 'c'
 reverse(s);  // undefined identifier

its usefulness to you or the rest of us :o).

Do you have a clear understanding of how name resolution should work in 'unified call syntax' as it is now? bool even(char[] a) {...} class Foo { private OptimizedEvenComputer ec; bool even(char[] a) {return ec.even(a);} void foo() { bool even(char[] a) {...} char[] l; auto x = l.even; auto y = even(l); } } What happens here? What *should* happen here? Even now people are confused because the mechanism works against their expectations. And, most importantly, they disagree in their expectations. Explicit injection solves this ambiguity once and for all. And I'm yet to hear *actual* arguments from you. Till now it was that you didn't like to type parentheses, you didn't like a bit of strictness in the language, and you didn't see any problem in not being able to replace methods with vars. Oh wait, you did see the problem. So what's your point then?

My point is that I agree with all concerns you are raising but I am not sure they warrant adding a language feature. Andrei
Sep 27 2008
parent reply Sergey Gromov <snake.scaly gmail.com> writes:
Sat, 27 Sep 2008 15:19:01 -0500,
Andrei Alexandrescu wrote:
 My point is that I agree with all concerns you are raising but I am not
 sure they warrant adding a language feature.

I hoped for some reason that these features could simplify the compiler. Now when I think about it I conclude that I was probably wrong. Explicit properties is definitely a feature, even though it seems easy to implement. Injectons could help if Walter were forced into supporting different scoping rules for unified call syntax, but if a.f(b) stays strictly a sugar for f(a,b) this feature helps nothing from a compiler standpoint. So I'll probably agree that these features don't add much to the language, as D doesn't add much to C except safety, productivity, maintainability and claritiy. The clarity/maintainability vs genericity is a tradeoff which is completely in Walter's hands.
Sep 27 2008
parent reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
Sergey Gromov wrote:
 Sat, 27 Sep 2008 15:19:01 -0500,
 Andrei Alexandrescu wrote:
 My point is that I agree with all concerns you are raising but I am not
 sure they warrant adding a language feature.

I hoped for some reason that these features could simplify the compiler. Now when I think about it I conclude that I was probably wrong. Explicit properties is definitely a feature, even though it seems easy to implement. Injectons could help if Walter were forced into supporting different scoping rules for unified call syntax, but if a.f(b) stays strictly a sugar for f(a,b) this feature helps nothing from a compiler standpoint. So I'll probably agree that these features don't add much to the language, as D doesn't add much to C except safety, productivity, maintainability and claritiy. The clarity/maintainability vs genericity is a tradeoff which is completely in Walter's hands.

Very wise words. I think we all agree that there are some annoyances related to the whole property business, among which the main one is: writeln = 4; That is quite indefensible :o|. I consider the others rather minor, but that's just a personal opinion. How about this. Maybe if we attacked this annoyance in particular, that would be a large bang for the buck without a landslide change in the compiler. We only need some way to inform the compiler, "yes, it's ok to call a.b(c) as a.b = c". Ideas? Andrei
Sep 27 2008
next sibling parent reply "Chris R. Miller" <lordsauronthegreat gmail.com> writes:
Andrei Alexandrescu wrote:
 Sergey Gromov wrote:
 Sat, 27 Sep 2008 15:19:01 -0500,
 Andrei Alexandrescu wrote:
 My point is that I agree with all concerns you are raising but I am not
 sure they warrant adding a language feature.

I hoped for some reason that these features could simplify the compiler. Now when I think about it I conclude that I was probably wrong. Explicit properties is definitely a feature, even though it seems easy to implement. Injectons could help if Walter were forced into supporting different scoping rules for unified call syntax, but if a.f(b) stays strictly a sugar for f(a,b) this feature helps nothing from a compiler standpoint. So I'll probably agree that these features don't add much to the language, as D doesn't add much to C except safety, productivity, maintainability and claritiy. The clarity/maintainability vs genericity is a tradeoff which is completely in Walter's hands.

Very wise words. I think we all agree that there are some annoyances related to the whole property business, among which the main one is: writeln = 4; That is quite indefensible :o|. I consider the others rather minor, but that's just a personal opinion. How about this. Maybe if we attacked this annoyance in particular, that would be a large bang for the buck without a landslide change in the compiler. We only need some way to inform the compiler, "yes, it's ok to call a.b(c) as a.b = c". Ideas?

Just trying to think of something that could be easily parsed... void foo(char t)!={ /*do something with t*/} void bar(char t) { /*do something with t*/} void main() { foo('t'); // okay foo='t'; // not okay because of the "!=" bar('t'); // okay bar='t'; // okay } My thinking is that it doesn't break existing code. One could change the order to precede the argument list, but I don't like that as much because it becomes ambiguous when used in conjunction with templates. Just my $0.02.
Sep 27 2008
parent reply Sergey Gromov <snake.scaly gmail.com> writes:
Sat, 27 Sep 2008 23:08:43 -0700,
Chris R. Miller wrote:
 Andrei Alexandrescu wrote:
 I think we all agree that there are some annoyances related to the whole 
 property business, among which the main one is:
 
 writeln = 4;
 
 That is quite indefensible :o|. I consider the others rather minor, but 
 that's just a personal opinion.
 
 How about this. Maybe if we attacked this annoyance in particular, that 
 would be a large bang for the buck without a landslide change in the 
 compiler. We only need some way to inform the compiler, "yes, it's ok to 
 call a.b(c) as a.b = c". Ideas?

Just trying to think of something that could be easily parsed... void foo(char t)!={ /*do something with t*/} void bar(char t) { /*do something with t*/} void main() { foo('t'); // okay foo='t'; // not okay because of the "!=" bar('t'); // okay bar='t'; // okay } My thinking is that it doesn't break existing code. One could change the order to precede the argument list, but I don't like that as much because it becomes ambiguous when used in conjunction with templates.

I cannot see how trickery with an existing syntax is less a feature than a new keyword.
Sep 28 2008
parent "Chris R. Miller" <lordsauronthegreat gmail.com> writes:
Sergey Gromov wrote:
 Sat, 27 Sep 2008 23:08:43 -0700,
 Chris R. Miller wrote:
 Andrei Alexandrescu wrote:
 I think we all agree that there are some annoyances related to the whole 
 property business, among which the main one is:

 writeln = 4;

 That is quite indefensible :o|. I consider the others rather minor, but 
 that's just a personal opinion.

 How about this. Maybe if we attacked this annoyance in particular, that 
 would be a large bang for the buck without a landslide change in the 
 compiler. We only need some way to inform the compiler, "yes, it's ok to 
 call a.b(c) as a.b = c". Ideas?

void foo(char t)!={ /*do something with t*/} void bar(char t) { /*do something with t*/} void main() { foo('t'); // okay foo='t'; // not okay because of the "!=" bar('t'); // okay bar='t'; // okay } My thinking is that it doesn't break existing code. One could change the order to precede the argument list, but I don't like that as much because it becomes ambiguous when used in conjunction with templates.

I cannot see how trickery with an existing syntax is less a feature than a new keyword.

Awe, but making the syntax more complex makes the code look so much more unfriendly ;-) My thought process hadn't even touched upon the idea of adding another keyword. But then what would the keyword be? "noeq"? "noprop"? "propless"? "muffin"? (I vote for muffin!) ;-)
Sep 28 2008
prev sibling next sibling parent reply Jason House <jason.james.house gmail.com> writes:
Andrei Alexandrescu wrote:
 
 I think we all agree that there are some annoyances related to the whole
 property business, among which the main one is:
 
 writeln = 4;
 
 That is quite indefensible :o|. I consider the others rather minor, but
 that's just a personal opinion.
 
 How about this. Maybe if we attacked this annoyance in particular, that
 would be a large bang for the buck without a landslide change in the
 compiler. We only need some way to inform the compiler, "yes, it's ok to
 call a.b(c) as a.b = c". Ideas?

That seems like a bad idea if it allows a forgetful/lazy/overworked library writer to cause users to be unable to use property syntax in natural cases. I'd say explicit forbidding of property syntax is a better idea. Based on some people's view of properties, allowing property get syntax for pure functions would make a lot of sense. Others would hate that. I'm not sure if restricting users would be all that popular. It may be better placed in some kind of lint tool.
Sep 28 2008
parent Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
Jason House wrote:
 Andrei Alexandrescu wrote:
  
 I think we all agree that there are some annoyances related to the whole
 property business, among which the main one is:

 writeln = 4;

 That is quite indefensible :o|. I consider the others rather minor, but
 that's just a personal opinion.

 How about this. Maybe if we attacked this annoyance in particular, that
 would be a large bang for the buck without a landslide change in the
 compiler. We only need some way to inform the compiler, "yes, it's ok to
 call a.b(c) as a.b = c". Ideas?

That seems like a bad idea if it allows a forgetful/lazy/overworked library writer to cause users to be unable to use property syntax in natural cases. I'd say explicit forbidding of property syntax is a better idea. Based on some people's view of properties, allowing property get syntax for pure functions would make a lot of sense. Others would hate that. I'm not sure if restricting users would be all that popular. It may be better placed in some kind of lint tool.

But I'm talking about property set syntax, not get. Andrei
Sep 28 2008
prev sibling next sibling parent reply torhu <no spam.invalid> writes:
Andrei Alexandrescu wrote:
 I think we all agree that there are some annoyances related to the whole 
 property business, among which the main one is:
 
 writeln = 4;
 
 That is quite indefensible :o|. I consider the others rather minor, but 
 that's just a personal opinion.
 
 How about this. Maybe if we attacked this annoyance in particular, that 
 would be a large bang for the buck without a landslide change in the 
 compiler. We only need some way to inform the compiler, "yes, it's ok to 
 call a.b(c) as a.b = c". Ideas?
 

Using an equals sign to say that assignment syntax is allowed seems natural: void prop(=int x) { } // assignment syntax ok void prop(= int x) { } // same thing Other cases: void prop(=int x=0) { } // can called as 'prop;' or 'int z = prop;' void prop(=int x, int y) { } // probably syntax error void prop(=int x, int y=0) { } // unusual but ok? Functions with no arguments can still be called without parens.
Sep 28 2008
next sibling parent torhu <no spam.invalid> writes:
torhu wrote:
 Other cases:
 void prop(=int x=0) { }  // can called as 'prop;' or 'int z = prop;'

Oops, I meant "can be called as 'prop;' or 'prop = 7;'
Sep 28 2008
prev sibling parent reply "Chris R. Miller" <lordsauronthegreat gmail.com> writes:
torhu wrote:
 Andrei Alexandrescu wrote:
 I think we all agree that there are some annoyances related to the 
 whole property business, among which the main one is:

 writeln = 4;

 That is quite indefensible :o|. I consider the others rather minor, 
 but that's just a personal opinion.

 How about this. Maybe if we attacked this annoyance in particular, 
 that would be a large bang for the buck without a landslide change in 
 the compiler. We only need some way to inform the compiler, "yes, it's 
 ok to call a.b(c) as a.b = c". Ideas?

Using an equals sign to say that assignment syntax is allowed seems natural: void prop(=int x) { } // assignment syntax ok void prop(= int x) { } // same thing Other cases: void prop(=int x=0) { } // can called as 'prop;' or 'int z = prop;' void prop(=int x, int y) { } // probably syntax error void prop(=int x, int y=0) { } // unusual but ok? Functions with no arguments can still be called without parens.

My concern is that this could break existing code. If the presence of an equals sign allows the use of the property syntax, then suddenly code needs to be updated that is supposed to be able to be used like a property. Perhaps something to explicitly disallow the use of the property syntax?
Sep 28 2008
parent reply torhu <no spam.invalid> writes:
Chris R. Miller wrote:
 torhu wrote:
 Using an equals sign to say that assignment syntax is allowed seems 
 natural:
 
 void prop(=int x) { }  // assignment syntax ok
 void prop(= int x) { }  // same thing
 
 Other cases:
 void prop(=int x=0) { }  // can called as 'prop;' or 'int z = prop;'
 void prop(=int x, int y) { }  // probably syntax error
 void prop(=int x, int y=0) { }  // unusual but ok?
 
 
 Functions with no arguments can still be called without parens.

My concern is that this could break existing code. If the presence of an equals sign allows the use of the property syntax, then suddenly code needs to be updated that is supposed to be able to be used like a property. Perhaps something to explicitly disallow the use of the property syntax?

Well, isn't this just for D 2.0? The implicit property setters could be just deprecated, like volatile recently was, so compiling with -d will make them work again.
Sep 28 2008
parent "Chris R. Miller" <lordsauronthegreat gmail.com> writes:
torhu wrote:
 Chris R. Miller wrote:
 torhu wrote:
 Using an equals sign to say that assignment syntax is allowed seems 
 natural:

 void prop(=int x) { }  // assignment syntax ok
 void prop(= int x) { }  // same thing

 Other cases:
 void prop(=int x=0) { }  // can called as 'prop;' or 'int z = prop;'
 void prop(=int x, int y) { }  // probably syntax error
 void prop(=int x, int y=0) { }  // unusual but ok?


 Functions with no arguments can still be called without parens.

My concern is that this could break existing code. If the presence of an equals sign allows the use of the property syntax, then suddenly code needs to be updated that is supposed to be able to be used like a property. Perhaps something to explicitly disallow the use of the property syntax?

Well, isn't this just for D 2.0? The implicit property setters could be just deprecated, like volatile recently was, so compiling with -d will make them work again.

D 2.0 isn't so different that every line of code will have to be rewritten for it though. Why force people to have to rewrite more code for D2.0 if we aren't sure we have to? Also, are we considering these properties? void doFoo() { } int main() { doFoo; // same as doFoo(); }
Sep 28 2008
prev sibling next sibling parent reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
Denis Koroskin wrote:
 On Sun, 28 Sep 2008 05:45:58 +0400, Andrei Alexandrescu 
 <SeeWebsiteForEmail erdani.org> wrote:
 
 Sergey Gromov wrote:
 Sat, 27 Sep 2008 15:19:01 -0500,
 Andrei Alexandrescu wrote:
 My point is that I agree with all concerns you are raising but I am not
 sure they warrant adding a language feature.

compiler. Now when I think about it I conclude that I was probably wrong. Explicit properties is definitely a feature, even though it seems easy to implement. Injectons could help if Walter were forced into supporting different scoping rules for unified call syntax, but if a.f(b) stays strictly a sugar for f(a,b) this feature helps nothing from a compiler standpoint. So I'll probably agree that these features don't add much to the language, as D doesn't add much to C except safety, productivity, maintainability and claritiy. The clarity/maintainability vs genericity is a tradeoff which is completely in Walter's hands.

Very wise words. I think we all agree that there are some annoyances related to the whole property business, among which the main one is: writeln = 4; That is quite indefensible :o|. I consider the others rather minor, but that's just a personal opinion. How about this. Maybe if we attacked this annoyance in particular, that would be a large bang for the buck without a landslide change in the compiler. We only need some way to inform the compiler, "yes, it's ok to call a.b(c) as a.b = c". Ideas? Andrei

I think property syntax should be disallowed by default. Most of the functions aren't expected to be used as a property, anyway.

By whom aren't they expected?
 It would break the code, yes, but it is very easy to fix.
 
 Solution of my preference:
 
 class Foo
 {
     // allow property syntax:
     property void bar(int baz); // allows "foo.bar = 42;"
 
     // allow omitting parens
     property int bar();         // allows writing "int x = foo.bar;"
 }
 
 Think long term, don't be afraid to break existing code and introduce 
 new features if they improve the language signaficantly. Wrong decision 
 made today may make huge negative impact over time.

Without solid argument, all this is just talk. What is the negative impact over time? Andrei
Sep 28 2008
parent reply "Chris R. Miller" <lordsauronthegreat gmail.com> writes:
Andrei Alexandrescu wrote:
 Denis Koroskin wrote:
 On Sun, 28 Sep 2008 05:45:58 +0400, Andrei Alexandrescu 
 <SeeWebsiteForEmail erdani.org> wrote:

 Sergey Gromov wrote:
 Sat, 27 Sep 2008 15:19:01 -0500,
 Andrei Alexandrescu wrote:
 My point is that I agree with all concerns you are raising but I am 
 not
 sure they warrant adding a language feature.

compiler. Now when I think about it I conclude that I was probably wrong. Explicit properties is definitely a feature, even though it seems easy to implement. Injectons could help if Walter were forced into supporting different scoping rules for unified call syntax, but if a.f(b) stays strictly a sugar for f(a,b) this feature helps nothing from a compiler standpoint. So I'll probably agree that these features don't add much to the language, as D doesn't add much to C except safety, productivity, maintainability and claritiy. The clarity/maintainability vs genericity is a tradeoff which is completely in Walter's hands.

Very wise words. I think we all agree that there are some annoyances related to the whole property business, among which the main one is: writeln = 4; That is quite indefensible :o|. I consider the others rather minor, but that's just a personal opinion. How about this. Maybe if we attacked this annoyance in particular, that would be a large bang for the buck without a landslide change in the compiler. We only need some way to inform the compiler, "yes, it's ok to call a.b(c) as a.b = c". Ideas? Andrei

I think property syntax should be disallowed by default. Most of the functions aren't expected to be used as a property, anyway.

By whom aren't they expected?
 It would break the code, yes, but it is very easy to fix.

 Solution of my preference:

 class Foo
 {
     // allow property syntax:
     property void bar(int baz); // allows "foo.bar = 42;"

     // allow omitting parens
     property int bar();         // allows writing "int x = foo.bar;"
 }

 Think long term, don't be afraid to break existing code and introduce 
 new features if they improve the language signaficantly. Wrong 
 decision made today may make huge negative impact over time.

Without solid argument, all this is just talk. What is the negative impact over time?

Improper use of properties make code that looks ambiguous. Is that a public member or a method? It reduces codebase coherency, which is normally not a huge problem except when new programmers are reading through the code to see how it works. If someone mistook that method call via property as a member, then they might use it as such in code they write. Because it's actually a method, it has the potential to rope in serious amounts of processor work. It's the same reason you don't want to overload simple operators in C++ with potentially large algorithms, since it creates an illusion that the operator is really a simple operation when in fact it could be quite tedious. This is - of course - a matter of practice with C++, and isn't a language requirement, but it does illustrate that it's something to be conscious of. Similarly, you wouldn't want to load up a D invariant with a lot of hard processor work, since that would incur a lot of overhead following each public function call. There's nothing explicitly wrong about doing it, it's just bad practice (according to some). I hope that was coherent. I believe I read about it in /Code Complete/, if you want a source.
Sep 28 2008
next sibling parent Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
Chris R. Miller wrote:
 Improper use of properties make code that looks ambiguous.  Is that a 
 public member or a method?

Under what circumstances is that distinction important?
 It reduces codebase coherency

How? Why?
, which is 
 normally not a huge problem except when new programmers are reading 
 through the code to see how it works.

Why do they need to know whether it's a computed or a state value?
 If someone mistook that method 
 call via property as a member, then they might use it as such in code 
 they write.  Because it's actually a method, it has the potential to 
 rope in serious amounts of processor work.

That is the complexity issue that was discussed a while ago in the digitalmars.d group. I agree that certain interfaces should come with a complexity guarantee. But the line is definitely not drawn at member/computed value. Besides public members are bad style to start with, so essentially the argument is that everybody must litter their code with (). Why? Because there is the implied assumption that what doesn't have the () is a member variable access. I am challenging that assumption.
 It's the same reason you don't want to overload simple operators in C++ 
 with potentially large algorithms, since it creates an illusion that the 
 operator is really a simple operation when in fact it could be quite 
 tedious.

I agree. That is what the STL does.
 This is - of course - a matter of practice with C++, and isn't 
 a language requirement, but it does illustrate that it's something to be 
 conscious of.  Similarly, you wouldn't want to load up a D invariant 
 with a lot of hard processor work, since that would incur a lot of 
 overhead following each public function call.  There's nothing 
 explicitly wrong about doing it, it's just bad practice (according to 
 some).
 
 I hope that was coherent.  I believe I read about it in /Code Complete/, 
 if you want a source.

I don't have the latest, so a quote would be helpful. Andrei
Sep 28 2008
prev sibling next sibling parent reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
Chris R. Miller wrote:
 Improper use of properties make code that looks ambiguous.  Is that a 
 public member or a method?

Under what circumstances is that distinction important?
 It reduces codebase coherency

How? Why?
, which is 
 normally not a huge problem except when new programmers are reading 
 through the code to see how it works.

Why do they need to know whether it's a computed or a state value?
 If someone mistook that method 
 call via property as a member, then they might use it as such in code 
 they write.  Because it's actually a method, it has the potential to 
 rope in serious amounts of processor work.

That is the complexity issue that was discussed a while ago in the digitalmars.d group. I agree that certain interfaces should come with a complexity guarantee. But the line is definitely not drawn at member/computed value. Besides public members are bad style to start with, so essentially the argument is that everybody must litter their code with (). Why? Because there is the implied assumption that what doesn't have the () is a member variable access. I am challenging that assumption.
 It's the same reason you don't want to overload simple operators in C++ 
 with potentially large algorithms, since it creates an illusion that the 
 operator is really a simple operation when in fact it could be quite 
 tedious.

I agree. That is what the STL does.
 This is - of course - a matter of practice with C++, and isn't 
 a language requirement, but it does illustrate that it's something to be 
 conscious of.  Similarly, you wouldn't want to load up a D invariant 
 with a lot of hard processor work, since that would incur a lot of 
 overhead following each public function call.  There's nothing 
 explicitly wrong about doing it, it's just bad practice (according to 
 some).
 
 I hope that was coherent.  I believe I read about it in /Code Complete/, 
 if you want a source.

I don't have the latest, so a quote would be helpful. Andrei
Sep 28 2008
parent reply "Chris R. Miller" <lordsauronthegreat gmail.com> writes:
Andrei Alexandrescu wrote:
 Chris R. Miller wrote:
 Improper use of properties make code that looks ambiguous.  Is that a 
 public member or a method?

Under what circumstances is that distinction important?

My lack of example situations is offset by my fear of one day finding one as a problem.
 It reduces codebase coherency

How? Why?

In Java, if something was called like a function with the () and everything, you could be pretty dang sure it's a method. In D, if it doesn't have a () when you see it, it could still be a method called as a property. The only way to find out is to look it up, which can be tedious and boring. If you don't look it up, you may never know, which reduces the coherency of the code.
 , which is normally not a huge problem except when new programmers are 
 reading through the code to see how it works.

Why do they need to know whether it's a computed or a state value?

I prefer to know what I'm doing when using an API - or any other code, for that matter. I think the more information you have about a system, the better able you are to make use of it. A method call indicates a potential for additional machinery behind the scenes, which can be useful information when deciding how to craft an algorithm that makes use of other code.
 If someone mistook that method call via property as a member, then 
 they might use it as such in code they write.  Because it's actually a 
 method, it has the potential to rope in serious amounts of processor 
 work.

That is the complexity issue that was discussed a while ago in the digitalmars.d group. I agree that certain interfaces should come with a complexity guarantee. But the line is definitely not drawn at member/computed value. Besides public members are bad style to start with, so essentially the argument is that everybody must litter their code with (). Why? Because there is the implied assumption that what doesn't have the () is a member variable access. I am challenging that assumption.

We could just do as Objective-C and use that strange typid foo = [NSObject toString]; syntax, which would negate the need for the whole discussion. ;-)
 It's the same reason you don't want to overload simple operators in 
 C++ with potentially large algorithms, since it creates an illusion 
 that the operator is really a simple operation when in fact it could 
 be quite tedious.

I agree. That is what the STL does.

STL is... strange, and I have not yet been well versed in it. The likes of superdan has convinced me that there is something of substance in it, though until I can really find some time to sink my teeth into it, I can have but no comment.
 This is - of course - a matter of practice with C++, and isn't a 
 language requirement, but it does illustrate that it's something to be 
 conscious of.  Similarly, you wouldn't want to load up a D invariant 
 with a lot of hard processor work, since that would incur a lot of 
 overhead following each public function call.  There's nothing 
 explicitly wrong about doing it, it's just bad practice (according to 
 some).

 I hope that was coherent.  I believe I read about it in /Code 
 Complete/, if you want a source.

I don't have the latest, so a quote would be helpful.

Actually I think it was from "Game Coding Complete" (Mike McShaffery, 2003). I seem to remember it being in proximity to another rant about how polydimensional vector addition using overloaded operators is a Bad Thing (TM) because junior programmers will do it left and right thinking "Eh, it's an operator, therefore fast!" when in fact is was a hugely expensive operation that should be avoided to speed up things. My brain leaks, I may never fully remember where it came from until I find that book again (somewhere in my room, though not sure where).
Sep 28 2008
next sibling parent Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
Chris R. Miller wrote:
 STL is... strange, and I have not yet been well versed in it.  The likes 
 of superdan has convinced me that there is something of substance in it, 
 though until I can really find some time to sink my teeth into it, I can 
 have but no comment.

That was an interesting thread. I am glad you decided to learn STL, you will derive a lot of good stuff from there. Andrei
Sep 28 2008
prev sibling parent reply Leandro Lucarella <llucax gmail.com> writes:
Chris R. Miller, el 28 de septiembre a las 19:50 me escribiste:
It reduces codebase coherency


In Java, if something was called like a function with the () and everything, you could be pretty dang sure it's a method. In D, if it doesn't have a () when you see it, it could still be a method called as a property. The only way to find out is to look it up, which can be tedious and boring. If you don't look it up, you may never know, which reduces the coherency of the code.

In Java you have the exact same problem. Since it doesn't support properties, and people don't want to chage thousands of LOC if they need to change a simple member variable to a function, they use functions just in case. So when you look at a function, for example, getSomething() you still wonder: is this just a simple return something;? Or is *really* a function? The problem is the same, you just have to use uglier syntax =) -- Leandro Lucarella (luca) | Blog colectivo: http://www.mazziblog.com.ar/blog/ ---------------------------------------------------------------------------- GPG Key: 5F5A8D05 (F8CD F9A7 BF00 5431 4145 104C 949E BFB6 5F5A 8D05) ---------------------------------------------------------------------------- CHINO ATRAPA COTORRAS -- Crónica TV
Sep 29 2008
parent reply "Chris R. Miller" <lordsauronthegreat gmail.com> writes:
Leandro Lucarella wrote:
 Chris R. Miller, el 28 de septiembre a las 19:50 me escribiste:
 It reduces codebase coherency


could be pretty dang sure it's a method. In D, if it doesn't have a () when you see it, it could still be a method called as a property. The only way to find out is to look it up, which can be tedious and boring. If you don't look it up, you may never know, which reduces the coherency of the code.

In Java you have the exact same problem. Since it doesn't support properties, and people don't want to chage thousands of LOC if they need to change a simple member variable to a function, they use functions just in case. So when you look at a function, for example, getSomething() you still wonder: is this just a simple return something;? Or is *really* a function? The problem is the same, you just have to use uglier syntax =)

In Java for some reason the "Industry Standard" was that using non-constant public members was a Bad Thing (TM), and that getters and setters were the only acceptable thing. For most things this was more or less just an annoying function set that looked like this: class Foo { private String bar; public void setBar(String bar){ this.bar=bar; } public String getBar(){ return this.bar; } } Other times (rare, I never personally needed it) they were important to allow input checking (what we'd do like this: void setBar(char[] bar) in { assert(bar.length>=3); } body { this.bar=bar; } And some times (such as in certain Swing/AWT code) they would immediately affect something, eg. setting the text on a window would immediately send a message to the rendering engine and make a change happen the next time the window was painted. Overall it wasn't a fault with Java, just with the Java programmers. They were so used to using getFoo/setFoo they just used it all the time.
Sep 29 2008
parent Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
Chris R. Miller wrote:
 Leandro Lucarella wrote:
 Chris R. Miller, el 28 de septiembre a las 19:50 me escribiste:
 It reduces codebase coherency


everything, you could be pretty dang sure it's a method. In D, if it doesn't have a () when you see it, it could still be a method called as a property. The only way to find out is to look it up, which can be tedious and boring. If you don't look it up, you may never know, which reduces the coherency of the code.

In Java you have the exact same problem. Since it doesn't support properties, and people don't want to chage thousands of LOC if they need to change a simple member variable to a function, they use functions just in case. So when you look at a function, for example, getSomething() you still wonder: is this just a simple return something;? Or is *really* a function? The problem is the same, you just have to use uglier syntax =)

In Java for some reason the "Industry Standard" was that using non-constant public members was a Bad Thing (TM), and that getters and setters were the only acceptable thing. For most things this was more or less just an annoying function set that looked like this: class Foo { private String bar; public void setBar(String bar){ this.bar=bar; } public String getBar(){ return this.bar; } } Other times (rare, I never personally needed it) they were important to allow input checking (what we'd do like this: void setBar(char[] bar) in { assert(bar.length>=3); } body { this.bar=bar; } And some times (such as in certain Swing/AWT code) they would immediately affect something, eg. setting the text on a window would immediately send a message to the rendering engine and make a change happen the next time the window was painted. Overall it wasn't a fault with Java, just with the Java programmers. They were so used to using getFoo/setFoo they just used it all the time.

Great point. This getFoo/setFoo crap is so retarded. It is a good illustration of the road to hell paved with good intentions. Yes, it's good to give the object a crack to enforce its invariant upon setting and maybe to compute/adjust a value upon getting. Here things took the wrong turn in Java - instead of unifying fields with properties, they postulated there must be no public fields. Andrei
Sep 29 2008
prev sibling parent Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
Chris R. Miller wrote:
 Andrei Alexandrescu wrote:
 Denis Koroskin wrote:
 On Sun, 28 Sep 2008 05:45:58 +0400, Andrei Alexandrescu 
 <SeeWebsiteForEmail erdani.org> wrote:

 Sergey Gromov wrote:
 Sat, 27 Sep 2008 15:19:01 -0500,
 Andrei Alexandrescu wrote:
 My point is that I agree with all concerns you are raising but I 
 am not
 sure they warrant adding a language feature.

compiler. Now when I think about it I conclude that I was probably wrong. Explicit properties is definitely a feature, even though it seems easy to implement. Injectons could help if Walter were forced into supporting different scoping rules for unified call syntax, but if a.f(b) stays strictly a sugar for f(a,b) this feature helps nothing from a compiler standpoint. So I'll probably agree that these features don't add much to the language, as D doesn't add much to C except safety, productivity, maintainability and claritiy. The clarity/maintainability vs genericity is a tradeoff which is completely in Walter's hands.

Very wise words. I think we all agree that there are some annoyances related to the whole property business, among which the main one is: writeln = 4; That is quite indefensible :o|. I consider the others rather minor, but that's just a personal opinion. How about this. Maybe if we attacked this annoyance in particular, that would be a large bang for the buck without a landslide change in the compiler. We only need some way to inform the compiler, "yes, it's ok to call a.b(c) as a.b = c". Ideas? Andrei

I think property syntax should be disallowed by default. Most of the functions aren't expected to be used as a property, anyway.

By whom aren't they expected?
 It would break the code, yes, but it is very easy to fix.

 Solution of my preference:

 class Foo
 {
     // allow property syntax:
     property void bar(int baz); // allows "foo.bar = 42;"

     // allow omitting parens
     property int bar();         // allows writing "int x = foo.bar;"
 }

 Think long term, don't be afraid to break existing code and introduce 
 new features if they improve the language signaficantly. Wrong 
 decision made today may make huge negative impact over time.

Without solid argument, all this is just talk. What is the negative impact over time?

Improper use of properties make code that looks ambiguous. Is that a public member or a method? It reduces codebase coherency, which is normally not a huge problem except when new programmers are reading through the code to see how it works. If someone mistook that method call via property as a member, then they might use it as such in code they write. Because it's actually a method, it has the potential to rope in serious amounts of processor work. It's the same reason you don't want to overload simple operators in C++ with potentially large algorithms, since it creates an illusion that the operator is really a simple operation when in fact it could be quite tedious. This is - of course - a matter of practice with C++, and isn't a language requirement, but it does illustrate that it's something to be conscious of. Similarly, you wouldn't want to load up a D invariant with a lot of hard processor work, since that would incur a lot of overhead following each public function call. There's nothing explicitly wrong about doing it, it's just bad practice (according to some). I hope that was coherent. I believe I read about it in /Code Complete/, if you want a source.

Oh, besides - if computed properties are allowed, they also can perform arbitrary amounts of work. No? Andrei
Sep 28 2008
prev sibling next sibling parent reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
Andrei Alexandrescu wrote:
 How about this. Maybe if we attacked this annoyance in particular, that 
 would be a large bang for the buck without a landslide change in the 
 compiler. We only need some way to inform the compiler, "yes, it's ok to 
 call a.b(c) as a.b = c". Ideas?

I actually did have something in mind when I wrote this, just didn't want to bias anyone. My thinking is that the syntax "a.b = c" in lieu of a.b(c) for a function a.b(T x) should be allowed if and only if there also exists a function a.b() that returns a value of type T. Example: struct S1 { void prop(int x); } S1 s1; s1 = x; // error, prop is not a property struct S2 { void prop(int x); int prop(); } S2 s2; s2.prop = 42; // fine, prop is a property because it also has a getter This solution does not require any syntactic addition. Its drawback is that it makes it hard to define write-only properties. Are they important? Andrei
Sep 28 2008
next sibling parent Kenny B <funisher gmail.com> writes:
Andrei Alexandrescu wrote:
 Andrei Alexandrescu wrote:
 How about this. Maybe if we attacked this annoyance in particular,
 that would be a large bang for the buck without a landslide change in
 the compiler. We only need some way to inform the compiler, "yes, it's
 ok to call a.b(c) as a.b = c". Ideas?

I actually did have something in mind when I wrote this, just didn't want to bias anyone.

For the record, you have my vote on this idea. it would greatly simplify a lot of my aliases and template usage in particular (though I'm using D1) +1
Sep 28 2008
prev sibling next sibling parent reply Michel Fortin <michel.fortin michelf.com> writes:
On 2008-09-28 19:32:43 -0400, Andrei Alexandrescu 
<SeeWebsiteForEmail erdani.org> said:

 struct S2
 {
      void prop(int x);
      int prop();
 }
 
 S2 s2;
 s2.prop = 42; // fine, prop is a property because it also has a getter
 
 This solution does not require any syntactic addition. Its drawback is 
 that it makes it hard to define write-only properties. Are they 
 important?

I think having a function returing *the same type* is probably too much. What about this case: struct S { string str(); void str(string s); void str(wstring s); void str(dstring s); } In this case, you can only do "str = x;" when x is an UTF-8 string. Setting UTF-16 and UTF-32 would (sadly) require using the parenthesis syntax under your proposal. So I think you should not check for the return type of the getter, just if there is a function of the same name with no parameter, to determine if the "=" syntax can be used. ... Hum, and what do you do with this: struct S2 { void prop(int x); private int prop(); } S2 s2; s2.prop = 42; // legal or not? If it's legal, then I think it'll be used as a hack to do write-only properties. And also, if you keep checking the return type of the getter, what about this: struct S2 { void prop(Object x); const(Object) prop(); } Should "prop = new Object;" be legal or not? What if prop() was returning an invariant(Object) instead (for which there is no allowed implicit conversions)? And what if there are two overloads for the setter: one with an invariant(Object) and one with an Object (mutable)? Are both allowed to be called with "="? Then you should probably disallow "ref" and "out" too in the setter. I'm not sure either about what you'll do once "shared" gets in the picture. (And ain't "scope" comming at some point too?) Also, what if one or the other is a static function? And what if you have an invariant(S2) and prop(Object x) is accessible (because it is invariant) while prop() is not (because not invariant), should the "=" syntax apply anyway? ... Basically what I want to illustrate is that another drawback of this solution is that it will probably not be as straitforward to define, and then understand, as it seem at first glance. The current syntax, with all its drawbacks, has the advantage of being very easy to understand: using "=" works all the time and it's the caller's responsibility to use the syntax which express better what he is doing. Personally, I have no problem with that. -- Michel Fortin michel.fortin michelf.com http://michelf.com/
Sep 28 2008
parent reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
Michel Fortin wrote:
 On 2008-09-28 19:32:43 -0400, Andrei Alexandrescu 
 <SeeWebsiteForEmail erdani.org> said:
 
 struct S2
 {
      void prop(int x);
      int prop();
 }

 S2 s2;
 s2.prop = 42; // fine, prop is a property because it also has a getter

 This solution does not require any syntactic addition. Its drawback is 
 that it makes it hard to define write-only properties. Are they 
 important?

I think having a function returing *the same type* is probably too much. What about this case: struct S { string str(); void str(string s); void str(wstring s); void str(dstring s); } In this case, you can only do "str = x;" when x is an UTF-8 string. Setting UTF-16 and UTF-32 would (sadly) require using the parenthesis syntax under your proposal. So I think you should not check for the return type of the getter, just if there is a function of the same name with no parameter, to determine if the "=" syntax can be used.

Great point. I think the rule suggested in my latest post convers this. The rule was, if you can call s.str(s.str) then str is a property and allows the syntax "=". After than overloading will take care of all else.
 ...
 
 Hum, and what do you do with this:
 
 struct S2
 {
     void prop(int x);
     private int prop();
 }
 
 S2 s2;
 s2.prop = 42; // legal or not?
 
 If it's legal, then I think it'll be used as a hack to do write-only 
 properties.

It is legal because access check is done after syntactic check. (Also Walter wants to introduce the fun() = void syntax to disallow copying; that might be useful in this context too.)
 And also, if you keep checking the return type of the getter, what about 
 this:
 
 struct S2
 {
     void prop(Object x);
     const(Object) prop();
 }
 
 Should "prop = new Object;" be legal or not?

Great question - quite an arrow in the suggestion's Achille's tendon. Since I can't write s.prop(s.prop), that property would be ruled out. This may in fact be a good thing. It would be nice to provide generic code with the guarantee that if the save some property of an object, they can restore it later: void generic(Node)(Node obj) { auto save = obj.indentLevel; scope(exit) obj.indentLevel = save; ... }
 What if prop() was 
 returning an invariant(Object) instead (for which there is no allowed 
 implicit conversions)?

Under the proposal entity.prop(entity.prop), that won't compile. (I will note that invariant(Object) does convert to const(Object)).
 And what if there are two overloads for the 
 setter: one with an invariant(Object) and one with an Object (mutable)? 
 Are both allowed to be called with "="?

Under the proposal entity.prop(entity.prop), that would compile.
 Then you should probably disallow "ref" and "out" too in the setter.

Consider: struct ArrayAppender { ref Array host; void host(ref Array); } That should work as property, ain't it? (Walter has introduced ref returns; I'm already working on an alpha that has the feature. Of course I found bugs, too. :o))
 I'm 
 not sure either about what you'll do once "shared" gets in the picture. 
 (And ain't "scope" comming at some point too?)

Same test entity.prop(entity.prop) rules'em all.
 Also, what if one or the other is a static function?

Depends on how you call it. If you call it with an object in the first place, it works. If not, it doesn't.
 And what if you 
 have an invariant(S2) and prop(Object x) is accessible (because it is 
 invariant) while prop() is not (because not invariant), should the "=" 
 syntax apply anyway?
 
 ...
 
 Basically what I want to illustrate is that another drawback of this 
 solution is that it will probably not be as straitforward to define, and 
 then understand, as it seem at first glance.

I think it is very easy to define and moderately hard to implement.
 The current syntax, with all its drawbacks, has the advantage of being 
 very easy to understand: using "=" works all the time and it's the 
 caller's responsibility to use the syntax which express better what he 
 is doing. Personally, I have no problem with that.

I'm not crazy about the syntax, but if it has notable advantages, sure. Andrei
Sep 28 2008
parent reply Michel Fortin <michel.fortin michelf.com> writes:
On 2008-09-29 00:07:28 -0400, Andrei Alexandrescu 
<SeeWebsiteForEmail erdani.org> said:

 Michel Fortin wrote:
 On 2008-09-28 19:32:43 -0400, Andrei Alexandrescu 
 <SeeWebsiteForEmail erdani.org> said:
 
 struct S2
 {
      void prop(int x);
      int prop();
 }
 
 S2 s2;
 s2.prop = 42; // fine, prop is a property because it also has a getter
 
 This solution does not require any syntactic addition. Its drawback is 
 that it makes it hard to define write-only properties. Are they 
 important?

I think having a function returing *the same type* is probably too much. What about this case: struct S { string str(); void str(string s); void str(wstring s); void str(dstring s); } In this case, you can only do "str = x;" when x is an UTF-8 string. Setting UTF-16 and UTF-32 would (sadly) require using the parenthesis syntax under your proposal. So I think you should not check for the return type of the getter, just if there is a function of the same name with no parameter, to determine if the "=" syntax can be used.

Great point. I think the rule suggested in my latest post convers this. The rule was, if you can call s.str(s.str) then str is a property and allows the syntax "=". After than overloading will take care of all else.
 ...
 
 Hum, and what do you do with this:
 
 struct S2
 {
     void prop(int x);
     private int prop();
 }
 
 S2 s2;
 s2.prop = 42; // legal or not?
 
 If it's legal, then I think it'll be used as a hack to do write-only 
 properties.

It is legal because access check is done after syntactic check.

Ok, so you determine if a function can be called by "=" before checking for access rights.
 (Also Walter wants to introduce the fun() = void syntax to disallow 
 copying; that might be useful in this context too.)

Ok, great. What do you do with this by the way: struct S2 { void prop(int x); deprecated int prop(); } Did the author want to eventually replace "prop()" with "prop() = void" to keep allowing "prop = 1", or does he want to simply remove it and disallow the "prop = 1" syntax? I find it somewhat troubling that removing an unused function (especially if private) could invalidate code just because it is calling another overloaded function with the property syntax. I mean, now to know if you can safely remove a function, you have to check if anyone is using any of corresponding overloaded "setters" too.
 And also, if you keep checking the return type of the getter, what about this:
 
 struct S2
 {
     void prop(Object x);
     const(Object) prop();
 }
 
 Should "prop = new Object;" be legal or not?

Great question - quite an arrow in the suggestion's Achille's tendon. Since I can't write s.prop(s.prop), that property would be ruled out.

 This may in fact be a good thing. It would be nice to provide generic 
 code with the guarantee that if the save some property of an object, 
 they can restore it later:
 
 void generic(Node)(Node obj) {
     auto save = obj.indentLevel;
     scope(exit) obj.indentLevel = save;
     ...
 }

Hum, but if the setter is private, protected, or package, or if it just doesn't exist, that guarenty doesn't exist. Another interesting case: struct S5 { int prop(); void prop(int x) const; } Not sure what to do with it though, but it certainly breaks your guarenty when S5 is invariant. So should the property syntax "s5.prop = x;" works only when S5 is mutable, or not at all, or in all cases? Then what about this strange one: struct S5 { int prop(); string prop() invariant; void prop(int x) const; } Should the property syntax works when for S5, const(S5), invariant(S5) ?
 What if prop() was returning an invariant(Object) instead (for which 
 there is no allowed implicit conversions)?

Under the proposal entity.prop(entity.prop), that won't compile. (I will note that invariant(Object) does convert to const(Object)).
 And what if there are two overloads for the setter: one with an 
 invariant(Object) and one with an Object (mutable)? Are both allowed to 
 be called with "="?

Under the proposal entity.prop(entity.prop), that would compile.

Seems coherant.
 Then you should probably disallow "ref" and "out" too in the setter.

Consider: struct ArrayAppender { ref Array host; void host(ref Array); } That should work as property, ain't it? (Walter has introduced ref returns; I'm already working on an alpha that has the feature. Of course I found bugs, too. :o))

And "out"?
 I'm not sure either about what you'll do once "shared" gets in the 
 picture. (And ain't "scope" comming at some point too?)

Same test entity.prop(entity.prop) rules'em all.

Seems coherant, again.
 Also, what if one or the other is a static function?

Depends on how you call it. If you call it with an object in the first place, it works. If not, it doesn't.

Ok, let's clarify that: struct S1 { int prop(); static void prop(int x); } struct S2 { static int prop(); void prop(int x); } S1 s1; s1.prop = 1; // ok, because s1.prop returns int. S1.prop = 1; // error, since S1.prop does not exist. S2 s2; s2.prop = 1; // ok, because s1.prop returns int. Is that right?
 And what if you have an invariant(S2) and prop(Object x) is accessible 
 (because it is invariant) while prop() is not (because not invariant), 
 should the "=" syntax apply anyway?


No answer to that one? Take this case: struct S3 { int prop() invariant; void prop(int x); } S3 s3; s3.prop = x; // error? struct S4 { int prop(); void prop(int x) invariant; } invariant(S4) s4; s4.prop = x; // error? I guess disallowing this wouldn't be too bad, since it's pretty ridiculous anyway.
 Basically what I want to illustrate is that another drawback of this 
 solution is that it will probably not be as straitforward to define, 
 and then understand, as it seem at first glance.

I think it is very easy to define and moderately hard to implement.

The thing is that you can't explain everything just by checking if you can compile "s.prop(s.prop)". If one of the two is private, you said it would be allowed anyway, it becomes "s.prop(s.prop) disregarding applicable protection attributes". Then you said that it the getter could be "= void", but obviously "s.prop(s.prop) wouldn't compile in that case, so you'd have to change it to "s.prop(s.prop) compiles disregarding applicable protection attributes and whether or the the getter is '= void'". Then you'll need to add something about the constness of the struct or class the function is scoped in, so it becomes: s.prop(s.prop) compiles disregarding applicable protection attributes, disregarding whether or the the getter is '= void', and disregarding the constness of the enclosing scope (class or struct). That doesn't sound so simple, and I probably got it wrong. Can you define it better? -- Michel Fortin michel.fortin michelf.com http://michelf.com/
Sep 29 2008
parent reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
Michel Fortin wrote:
 Ok, great. What do you do with this by the way:
 
 struct S2
 {
     void prop(int x);
     deprecated int prop();
 }
 
 Did the author want to eventually replace "prop()" with "prop() = void" 
 to keep allowing "prop = 1", or does he want to simply remove it and 
 disallow the "prop = 1" syntax?
 
 I find it somewhat troubling that removing an unused function 
 (especially if private) could invalidate code just because it is calling 
 another overloaded function with the property syntax. I mean, now to 
 know if you can safely remove a function, you have to check if anyone is 
 using any of corresponding overloaded "setters" too.

I understand. I find it somewhat troubling too.
 void generic(Node)(Node obj) {
     auto save = obj.indentLevel;
     scope(exit) obj.indentLevel = save;
     ...
 }

Hum, but if the setter is private, protected, or package, or if it just doesn't exist, that guarenty doesn't exist.

That means indentLevel is not a property and consequently "generic" cannot work with that object.
 Another interesting case:
 
 struct S5
 {
     int prop();
     void prop(int x) const;
 }
 
 Not sure what to do with it though, but it certainly breaks your 
 guarenty when S5 is invariant. So should the property syntax "s5.prop = 
 x;" works only when S5 is mutable, or not at all, or in all cases?

It's very simple. If you have "entity.prop = x" first you check for "entity.prop(entity.prop)". Does that compile? If so, you rewrite "entity.prop = x" as entity.prop(x) and continue from there. In your question s5 can be invariant, const, or mutable. In either case the usual judgment applies.
 Then what about this strange one:
 
 struct S5
 {
     int prop();
     string prop() invariant;
     void prop(int x) const;
 }
 
 Should the property syntax works when for S5, const(S5), invariant(S5) ?

Same answer.
 Then you should probably disallow "ref" and "out" too in the setter.

Consider: struct ArrayAppender { ref Array host; void host(ref Array); } That should work as property, ain't it? (Walter has introduced ref returns; I'm already working on an alpha that has the feature. Of course I found bugs, too. :o))

And "out"?

struct S { ref int prop(); void prop(out int); } s.prop(s.prop) does compile, so s.prop = x; should be allowed. However, I agree it does something counterintuitive (modify the right-hand side).
 Ok, let's clarify that:
 
 struct S1
 {
     int prop();
     static void prop(int x);
 }
 
 struct S2
 {
     static int prop();
     void prop(int x);
 }
 
 S1 s1;
 s1.prop = 1; // ok, because s1.prop returns int.
 S1.prop = 1; // error, since S1.prop does not exist.
 
 S2 s2;
 s2.prop = 1; // ok, because s1.prop returns int.
 
 Is that right?

Yah.
 And what if you have an invariant(S2) and prop(Object x) is 
 accessible (because it is invariant) while prop() is not (because not 
 invariant), should the "=" syntax apply anyway?


No answer to that one? Take this case: struct S3 { int prop() invariant; void prop(int x); } S3 s3; s3.prop = x; // error?

s3.prop(s3.prop) would not compile, so neither does the assignment form.
 struct S4
 {
     int prop();
     void prop(int x) invariant;
 }
 
 invariant(S4) s4;
 s4.prop = x; // error?
 
 I guess disallowing this wouldn't be too bad, since it's pretty 
 ridiculous anyway.

This would not compile because s4.prop(s4.prop) does not compile.
 The thing is that you can't explain everything just by checking if you 
 can compile "s.prop(s.prop)". If one of the two is private, you said it 
 would be allowed anyway, it becomes "s.prop(s.prop) disregarding 
 applicable protection attributes". Then you said that it the getter 
 could be "= void", but obviously "s.prop(s.prop) wouldn't compile in 
 that case, so you'd have to change it to "s.prop(s.prop) compiles 
 disregarding applicable protection attributes and whether or the the 
 getter is '= void'". Then you'll need to add something about the 
 constness of the struct or class the function is scoped in, so it becomes:
 
     s.prop(s.prop) compiles disregarding applicable protection attributes,
     disregarding whether or the the getter is '= void', and disregarding
     the constness of the enclosing scope (class or struct).
 
 That doesn't sound so simple, and I probably got it wrong. Can you 
 define it better?

With this part I completely disagree. You artificially create complexity by bringing other rules into play that were in place before this discussion. By the same token you could add "s.prop(s.prop) compiles if s and prop are properly formed identifiers" and claim that that is an issue with properties. The protection attributes do their job. The = void does (will do, actually) its job. Qualifiers do their job. It's business as usual. I did not invent those rules for the sake of properties. Andrei
Sep 29 2008
parent reply Michel Fortin <michel.fortin michelf.com> writes:
On 2008-09-29 09:48:31 -0400, Andrei Alexandrescu 
<SeeWebsiteForEmail erdani.org> said:

     s.prop(s.prop) compiles disregarding applicable protection attributes,
     disregarding whether or the the getter is '= void', and disregarding
     the constness of the enclosing scope (class or struct).
 
 That doesn't sound so simple, and I probably got it wrong. Can you 
 define it better?

With this part I completely disagree. You artificially create complexity by bringing other rules into play that were in place before this discussion. By the same token you could add "s.prop(s.prop) compiles if s and prop are properly formed identifiers" and claim that that is an issue with properties. The protection attributes do their job. The = void does (will do, actually) its job. Qualifiers do their job. It's business as usual. I did not invent those rules for the sake of properties.

Ok, lets look back at what I wrote and what you replied in your previous post:
 struct S2
 {
     void prop(int x);
     private int prop();
 }
 
 S2 s2;
 s2.prop = 42; // legal or not?
 
 If it's legal, then I think it'll be used as a hack to do write-only 
 properties.

It is legal because access check is done after syntactic check. (Also Walter wants to introduce the fun() = void syntax to disallow copying; that might be useful in this context too.)

But "s2.prop(s2.prop)" wouldn't compile in the context where "s2.prop = 42" is called, because s2.prop is private. You say say that it doesn't matter, so I take it there is an exception for protection attributes. Or perhaps you're checking if "s2.prop(s2.prop)" compiles in another context I don't know about. About the "= void" thing, my interpretation is that it's a way to tell the compiler that this function doesn't exist (like making a copy constructor private in C++), but I admit I'm not sure what I'm talking about here. Anyway, my interpretation is that if you have: struct S2 { void prop(int x); int prop() = void; } S2 s2; s2.prop; the last line wouldn't compile since s2.prop can't be called. How can "s2.prop(s2.prop)" compile then? As for the last one, I think I decided myself that it wouldn't be very good for a function to be a property or not depending on the constness of the object. My previous examples were badly written however. Imagine you have this: struct S2 { int prop(); int prop() invariant; void prop(int x) const; } This design is probably a little strange, but lets look at it anyway: S2 s2; const(S2) constS2; invariant(S2) invariantS2; s2.prop = 1; // compiles because s2.prop(s2.prop) compiles. constS2.prop = 1; // does not compile because const(S2).prop isn't defined invariantS2.prop = 1; // compiles because invariantS2.prop(invariantS2.prop) compiles Basically, whether prop(int x) is a property or not depends on the various qualifiers given to prop() and the constness of S2. I think I wrongly assumed you'd want to avoid this since I had in mind you'd not forbid the proprety syntax when given a private accessor. -- Michel Fortin michel.fortin michelf.com http://michelf.com/
Sep 29 2008
parent reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
Michel Fortin wrote:
 On 2008-09-29 09:48:31 -0400, Andrei Alexandrescu 
 <SeeWebsiteForEmail erdani.org> said:
 
     s.prop(s.prop) compiles disregarding applicable protection 
 attributes,
     disregarding whether or the the getter is '= void', and disregarding
     the constness of the enclosing scope (class or struct).

 That doesn't sound so simple, and I probably got it wrong. Can you 
 define it better?

With this part I completely disagree. You artificially create complexity by bringing other rules into play that were in place before this discussion. By the same token you could add "s.prop(s.prop) compiles if s and prop are properly formed identifiers" and claim that that is an issue with properties. The protection attributes do their job. The = void does (will do, actually) its job. Qualifiers do their job. It's business as usual. I did not invent those rules for the sake of properties.

Ok, lets look back at what I wrote and what you replied in your previous post:
 struct S2
 {
     void prop(int x);
     private int prop();
 }

 S2 s2;
 s2.prop = 42; // legal or not?

 If it's legal, then I think it'll be used as a hack to do write-only 
 properties.

It is legal because access check is done after syntactic check. (Also Walter wants to introduce the fun() = void syntax to disallow copying; that might be useful in this context too.)

But "s2.prop(s2.prop)" wouldn't compile in the context where "s2.prop = 42" is called, because s2.prop is private. You say say that it doesn't matter, so I take it there is an exception for protection attributes. Or perhaps you're checking if "s2.prop(s2.prop)" compiles in another context I don't know about.

It's my fault, I misunderstood you. Sorry. This case could go either way, and I agree it does add complexity to the feature. If the feature is thought as a syntactic rewrite (my opinion), then s2.prop = 42 should always rewrite into s2.prop(42), and then whether that works or not should be left to the protection in vigor for S2.prop(int) - public in this case. But it could also be argued that the access check should be done first, in which case the rewrite won't happen unless the code in question has access to S2's privates. Probably the latter is actually the better decision.
 About the "= void" thing, my interpretation is that it's a way to tell 
 the compiler that this function doesn't exist (like making a copy 
 constructor private in C++), but I admit I'm not sure what I'm talking 
 about here.

Yes, that is the intent.
 Anyway, my interpretation is that if you have:
 
 struct S2
 {
     void prop(int x);
     int prop() = void;
 }
 
 S2 s2;
 s2.prop;
 
 the last line wouldn't compile since s2.prop can't be called. How can 
 "s2.prop(s2.prop)" compile then?

It should not compile. Making a function = void actively makes it unreachable and unusable in all contexts.
 As for the last one, I think I decided myself that it wouldn't be very 
 good for a function to be a property or not depending on the constness 
 of the object. My previous examples were badly written however. Imagine 
 you have this:
 
 struct S2
 {
     int prop();
     int prop() invariant;
     void prop(int x) const;
 }
 
 This design is probably a little strange, but lets look at it anyway:
 
 S2 s2;
 const(S2) constS2;
 invariant(S2) invariantS2;
 
 s2.prop = 1; // compiles because s2.prop(s2.prop) compiles.
 constS2.prop = 1; // does not compile because const(S2).prop isn't defined
 invariantS2.prop = 1; // compiles because 
 invariantS2.prop(invariantS2.prop) compiles
 
 Basically, whether prop(int x) is a property or not depends on the 
 various qualifiers given to prop() and the constness of S2. I think I 
 wrongly assumed you'd want to avoid this since I had in mind you'd not 
 forbid the proprety syntax when given a private accessor.

Indeed, whether or not something can be used as a property will depend on the qualifiers in vigor. Andrei
Sep 29 2008
parent reply Michel Fortin <michel.fortin michelf.com> writes:
On 2008-09-29 23:12:44 -0400, Andrei Alexandrescu 
<SeeWebsiteForEmail erdani.org> said:

 Indeed, whether or not something can be used as a property will depend 
 on the qualifiers in vigor.

And I think this is why binding the availability of the proprety syntax of the setter to the existance and visibility of a getter is not a good idea. Basically, doing this makes the "()" syntax safer, and the "=" version more fragile, as the later could become non-functionnal in all kinds of situations where the getter becomes unavailable (removal, deprecation, change in protection, change in constness of the parent, etc.). It means that if I remove or change the visibility of a getter in a program (because it has been found to be erroneous, dangerous, and/or badly used), I will have to change all the calls to the corresponding setter to use the parenthesis form. This can be a nuisance while refactoring. It also means that I can't just remove the getter and attempt to recompile to check if it's called somewhere without inducing compilation errors where the setter is used. And I'm sure it'll encourage some silly ones who want to write write-only properties to add a dummy getter that throws an exception, disregarding static checking. I can already see some people recommanding always using the parenthesis syntax when calling setters because then you know you won't have to change it later when you refactor your code; I know I would consider it if I got biten by this, after all we're already doing this in other languages and it's not that painfull. So I think this idea brings unnecessary coupling between functions, with more drawbacks than benefits. Don't mistake me, I liked your idea at first, especially because I'm not fond of adding a new syntax just for diallowing another, but now I realise don't like its implications of the implicit coupling of the setter with the getter. I believe explicit coupling would be better, and that probably means an explicit property syntax as others have requested. But then again, I don't mind much if things stay like they are. - - - Another funny example: module a; void func(int a); module b; int func(); module c; import a; import b; void main() { func(func); // compiles... so func = 5; // compiles too! } -- Michel Fortin michel.fortin michelf.com http://michelf.com/
Sep 30 2008
parent Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
Michel Fortin wrote:
 On 2008-09-29 23:12:44 -0400, Andrei Alexandrescu 
 <SeeWebsiteForEmail erdani.org> said:
 
 Indeed, whether or not something can be used as a property will depend 
 on the qualifiers in vigor.

And I think this is why binding the availability of the proprety syntax of the setter to the existance and visibility of a getter is not a good idea. Basically, doing this makes the "()" syntax safer, and the "=" version more fragile, as the later could become non-functionnal in all kinds of situations where the getter becomes unavailable (removal, deprecation, change in protection, change in constness of the parent, etc.). It means that if I remove or change the visibility of a getter in a program (because it has been found to be erroneous, dangerous, and/or badly used), I will have to change all the calls to the corresponding setter to use the parenthesis form. This can be a nuisance while refactoring. It also means that I can't just remove the getter and attempt to recompile to check if it's called somewhere without inducing compilation errors where the setter is used. And I'm sure it'll encourage some silly ones who want to write write-only properties to add a dummy getter that throws an exception, disregarding static checking. I can already see some people recommanding always using the parenthesis syntax when calling setters because then you know you won't have to change it later when you refactor your code; I know I would consider it if I got biten by this, after all we're already doing this in other languages and it's not that painfull.

I think the argument is overstated and a corner case is overblown, but I agree there is an issue.
 So I think this idea brings unnecessary coupling between functions, with 
 more drawbacks than benefits. Don't mistake me, I liked your idea at 
 first, especially because I'm not fond of adding a new syntax just for 
 diallowing another, but now I realise don't like its implications of the 
 implicit coupling of the setter with the getter.
 
 I believe explicit coupling would be better, and that probably means an 
 explicit property syntax as others have requested. But then again, I 
 don't mind much if things stay like they are.
 
 - - -
 
 Another funny example:
 
 module a;
 void func(int a);
 
 module b;
 int func();
 
 module c;
 import a;
 import b;
 void main()
 {
     func(func); // compiles... so
     func = 5; // compiles too!
 }
 

The coupling part I agree with, it's unfortunate. Well I guess I'll just drop the matter. Andrei
Sep 30 2008
prev sibling next sibling parent reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
Bill Baxter wrote:
 On Mon, Sep 29, 2008 at 8:32 AM, Andrei Alexandrescu
 <SeeWebsiteForEmail erdani.org> wrote:
 Andrei Alexandrescu wrote:
 How about this. Maybe if we attacked this annoyance in particular, that
 would be a large bang for the buck without a landslide change in the
 compiler. We only need some way to inform the compiler, "yes, it's ok to
 call a.b(c) as a.b = c". Ideas?

bias anyone. My thinking is that the syntax "a.b = c" in lieu of a.b(c) for a function a.b(T x) should be allowed if and only if there also exists a function a.b() that returns a value of type T. Example: struct S1 { void prop(int x); } S1 s1; s1 = x; // error, prop is not a property struct S2 { void prop(int x); int prop(); } S2 s2; s2.prop = 42; // fine, prop is a property because it also has a getter This solution does not require any syntactic addition. Its drawback is that it makes it hard to define write-only properties. Are they important?

Seems a little too subtle to me. Maybe no problem for the compiler, but yet another kooky rule the human has to hold in their brain when trying to read D code. The rule is trivial, you may say. But how about co-variant return types? If FooSub is a subclass of Foo, then is it ok for the getter to return one and setter to take the other? Or vice versa? Or how about implicit conversions. If my getter takes a long, but my setter returns an int, is that ok? How do const variations fit in? Probably there's a way to fit those all neatly in an automatic rule (or just disallow them), but it just makes the rule longer and even harder to keep in mind (or cuts off functionality people may need).

Yah, I was thinking of all these and then some more while I was posting. I think it's another duck typing thing. If you can call: entity.prop(entity.prop); then you can consider prop a property, period. Entity could be anything that allows the dot member access (object, class, struct, union, template, or module). Given that there is no global scope in D, that takes care of everything. I think it's really hard to get any simpler than that.
 Also how about preventing the calling of property setters as functions?
 
 s2.next(42) looks like it will do something quite different from
 s2.next = 42.  I would like for property syntax to also disallow
 function call syntax.

I'd also like plenty many things, but they cost.
 ---
 Somewhat unrelated, but there still exists the annoyance in D that if
 you have to functions with the same name and you want to take the
 address of one of them, you can't.  Furthermore I can't think of a
 reasonable syntax to do that easily.  For that reason,  I really think
 the best getter and setter functionality in D would be something where
 you have distinctly named *functions* getProp and setProp for when you
 want/need functions mirroring samely-named *properties* for which only
 property syntax would work.
 
 Basically there's no convenient way to take the address of one of a
 getter/setter function pair, currently.  I think that should factor in
 the solution here.

Overloading is the issue, and that's quite a different story. If you know the exact type, you can take the address of something. struct S { int foo() {} void foo(int) {} } void main() { S s; void delegate(int) x = &s.foo; int delegate() y = &s.foo; } Andrei
Sep 28 2008
next sibling parent Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
Bill Baxter wrote:
 On Mon, Sep 29, 2008 at 12:53 PM, Andrei Alexandrescu
 <SeeWebsiteForEmail erdani.org> wrote:
 Bill Baxter wrote:
 Basically there's no convenient way to take the address of one of a
 getter/setter function pair, currently.  I think that should factor in
 the solution here.


It's a different, but intertwined story.
 If you know
 the exact type, you can take the address of something.

 struct S
 {
    int foo() {}
    void foo(int) {}
 }

 void main()
 {
    S s;
    void delegate(int) x = &s.foo;
    int delegate() y = &s.foo;
 }

Having to write out "delegate(argumenttype) doesn't qualify as "convenient" in my book.

You first used "can't". That doesn't qualify as "can" in my book :o). The "can't" made me honestly think you didn't know about the ability to take the address of an overloaded function/method into a specifically-typed function or delegate respectively. One more instance of the proverbial no good deed that goes unpunished... :o) Andrei
Sep 28 2008
prev sibling parent reply Sergey Gromov <snake.scaly gmail.com> writes:
Sun, 28 Sep 2008 22:53:13 -0500,
Andrei Alexandrescu wrote:
 Bill Baxter wrote:
 The rule is trivial, you may say.  But how about co-variant return
 types?  If FooSub is a subclass of Foo, then is it ok for the getter
 to return one and setter to take the other?  Or vice versa?  Or how
 about implicit conversions.  If my getter takes a long, but my setter
 returns an int, is that ok?  How do const variations fit in?  Probably
 there's a way to fit those all neatly in an automatic rule (or just
 disallow them), but it just makes the rule longer and even harder to
 keep in mind (or cuts off functionality people may need).

Yah, I was thinking of all these and then some more while I was posting. I think it's another duck typing thing. If you can call: entity.prop(entity.prop); then you can consider prop a property, period. Entity could be anything that allows the dot member access (object, class, struct, union, template, or module). Given that there is no global scope in D, that takes care of everything. I think it's really hard to get any simpler than that.

D has a simple rule for property methods. This rule has side effects. If the side effects are so bad that a hack is required to counter them, then the rule should be replaced with a better one. Otherwise your hack will inevitably introduce new, less obvious side effects than those it were supposed to fight, and will finally require other hacks. struct Range { ref int head() {...} } Range r; r.head = 5; // error
Sep 29 2008
parent reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
Sergey Gromov wrote:
 Sun, 28 Sep 2008 22:53:13 -0500,
 Andrei Alexandrescu wrote:
 Bill Baxter wrote:
 The rule is trivial, you may say.  But how about co-variant return
 types?  If FooSub is a subclass of Foo, then is it ok for the getter
 to return one and setter to take the other?  Or vice versa?  Or how
 about implicit conversions.  If my getter takes a long, but my setter
 returns an int, is that ok?  How do const variations fit in?  Probably
 there's a way to fit those all neatly in an automatic rule (or just
 disallow them), but it just makes the rule longer and even harder to
 keep in mind (or cuts off functionality people may need).

I think it's another duck typing thing. If you can call: entity.prop(entity.prop); then you can consider prop a property, period. Entity could be anything that allows the dot member access (object, class, struct, union, template, or module). Given that there is no global scope in D, that takes care of everything. I think it's really hard to get any simpler than that.

D has a simple rule for property methods. This rule has side effects. If the side effects are so bad that a hack is required to counter them, then the rule should be replaced with a better one. Otherwise your hack will inevitably introduce new, less obvious side effects than those it were supposed to fight, and will finally require other hacks. struct Range { ref int head() {...} } Range r; r.head = 5; // error

A function can return an object that allows assignment even today with opAssign. I said "If you can call: entity.prop(entity.prop); then you can consider prop a property, period." I did not say "If and only if". Andrei
Sep 29 2008
next sibling parent reply Sergey Gromov <snake.scaly gmail.com> writes:
Mon, 29 Sep 2008 12:23:58 -0500,
Andrei Alexandrescu wrote:
 Sergey Gromov wrote:
 D has a simple rule for property methods.  This rule has side effects.  
 If the side effects are so bad that a hack is required to counter them,
 then the rule should be replaced with a better one.  Otherwise your hack 
 will inevitably introduce new, less obvious side effects than those it 
 were supposed to fight, and will finally require other hacks.
 
 struct Range
 {
   ref int head() {...}
 }
 Range r;
 r.head = 5; // error

A function can return an object that allows assignment even today with opAssign.

Today the compiler tries to call Range.head with one argument and fails. Another side effect needs hacking.
 I said "If you can call: entity.prop(entity.prop); then you can consider 
 prop a property, period." I did not say "If and only if".

Sep 29 2008
parent Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
Sergey Gromov wrote:
 Mon, 29 Sep 2008 12:23:58 -0500,
 Andrei Alexandrescu wrote:
 Sergey Gromov wrote:
 D has a simple rule for property methods.  This rule has side effects.  
 If the side effects are so bad that a hack is required to counter them,
 then the rule should be replaced with a better one.  Otherwise your hack 
 will inevitably introduce new, less obvious side effects than those it 
 were supposed to fight, and will finally require other hacks.

 struct Range
 {
   ref int head() {...}
 }
 Range r;
 r.head = 5; // error

opAssign.

Today the compiler tries to call Range.head with one argument and fails. Another side effect needs hacking.

Well I'd rather say, an awkwardness that needs fixing. Andrei
Sep 29 2008
prev sibling parent KennyTM~ <kennytm gmail.com> writes:
Andrei Alexandrescu wrote:
 Sergey Gromov wrote:
 Sun, 28 Sep 2008 22:53:13 -0500,
 Andrei Alexandrescu wrote:
 Bill Baxter wrote:
 The rule is trivial, you may say.  But how about co-variant return
 types?  If FooSub is a subclass of Foo, then is it ok for the getter
 to return one and setter to take the other?  Or vice versa?  Or how
 about implicit conversions.  If my getter takes a long, but my setter
 returns an int, is that ok?  How do const variations fit in?  Probably
 there's a way to fit those all neatly in an automatic rule (or just
 disallow them), but it just makes the rule longer and even harder to
 keep in mind (or cuts off functionality people may need).

I think it's another duck typing thing. If you can call: entity.prop(entity.prop); then you can consider prop a property, period. Entity could be anything that allows the dot member access (object, class, struct, union, template, or module). Given that there is no global scope in D, that takes care of everything. I think it's really hard to get any simpler than that.

D has a simple rule for property methods. This rule has side effects. If the side effects are so bad that a hack is required to counter them, then the rule should be replaced with a better one. Otherwise your hack will inevitably introduce new, less obvious side effects than those it were supposed to fight, and will finally require other hacks. struct Range { ref int head() {...} } Range r; r.head = 5; // error

A function can return an object that allows assignment even today with opAssign. I said "If you can call: entity.prop(entity.prop); then you can consider prop a property, period." I did not say "If and only if". Andrei

Just wondering... What about opCall's? Will it be considered while evaluating "entity.prop(entity.prop)"? (I hope not :p, and an object S won't be evaluated to S() := S.opCall() anyway.) struct A { invariant void opCall(invariant(A) x) { writeln('hi'); } } struct B { invariant(A) a; } b.a(b.a) compiles but b.a = b.a does not make sense (b.a is not mutable).
Sep 29 2008
prev sibling next sibling parent "Steven Schveighoffer" <schveiguy yahoo.com> writes:
"Andrei Alexandrescu" wrote
 Andrei Alexandrescu wrote:
 How about this. Maybe if we attacked this annoyance in particular, that 
 would be a large bang for the buck without a landslide change in the 
 compiler. We only need some way to inform the compiler, "yes, it's ok to 
 call a.b(c) as a.b = c". Ideas?

I actually did have something in mind when I wrote this, just didn't want to bias anyone. My thinking is that the syntax "a.b = c" in lieu of a.b(c) for a function a.b(T x) should be allowed if and only if there also exists a function a.b() that returns a value of type T.

What about functions with default parameters? struct S1 { int prop(int x = 0) {...} } I admit I have no idea how this should be interpreted. The author may have intended for this to compile: s1.prop = 5; -Steve
Sep 30 2008
prev sibling parent reply Bruno Medeiros <brunodomedeiros+spam com.gmail> writes:
Bill Baxter wrote:
 
 ---
 Somewhat unrelated, but there still exists the annoyance in D that if
 you have to functions with the same name and you want to take the
 address of one of them, you can't.  Furthermore I can't think of a
 reasonable syntax to do that easily.  For that reason,  I really think
 the best getter and setter functionality in D would be something where
 you have distinctly named *functions* getProp and setProp for when you
 want/need functions mirroring samely-named *properties* for which only
 property syntax would work.
 

Hum, that reminds me of an idea I once had for properties: not using a keyword, but only convention, just as the op* methods for operator overload. Basicly one writes a property with getter and setter functions, like Java: class Foo { SomeBar getSomeBar(); void setSomeBar(SomeBar someBar); } one can then access those functions normally, like Java, but one would then also be able to use a property with the same name of the getter/setter methods, but without 'get' or 'set', and with the capitalization of the first letter fixed, like this: Foo foo = ...; SomeBar someBar = foo.someBar; //same as: SomeBar someBar = foo.getSomeBar(); foo.someBar = new SomeBar(); //same as: foo.setBar(new SomeBar()); This makes it easy to use Java-style code (for instance when porting Java code, or using libs like DWT, etc.). What I don't like here, is that this solution involves working with the capitalization/CamelCase of the property, which doesn't sound right. And what about properties that start with a capital letter?... :( -- Bruno Medeiros - Software Developer, MSc. in CS/E graduate http://www.prowiki.org/wiki4d/wiki.cgi?BrunoMedeiros#D
Oct 02 2008
parent reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
Bruno Medeiros wrote:
 Bill Baxter wrote:
 ---
 Somewhat unrelated, but there still exists the annoyance in D that if
 you have to functions with the same name and you want to take the
 address of one of them, you can't.  Furthermore I can't think of a
 reasonable syntax to do that easily.  For that reason,  I really think
 the best getter and setter functionality in D would be something where
 you have distinctly named *functions* getProp and setProp for when you
 want/need functions mirroring samely-named *properties* for which only
 property syntax would work.

Hum, that reminds me of an idea I once had for properties: not using a keyword, but only convention, just as the op* methods for operator overload. Basicly one writes a property with getter and setter functions, like Java: class Foo { SomeBar getSomeBar(); void setSomeBar(SomeBar someBar); } one can then access those functions normally, like Java, but one would then also be able to use a property with the same name of the getter/setter methods, but without 'get' or 'set', and with the capitalization of the first letter fixed, like this: Foo foo = ...; SomeBar someBar = foo.someBar; //same as: SomeBar someBar = foo.getSomeBar(); foo.someBar = new SomeBar(); //same as: foo.setBar(new SomeBar()); This makes it easy to use Java-style code (for instance when porting Java code, or using libs like DWT, etc.). What I don't like here, is that this solution involves working with the capitalization/CamelCase of the property, which doesn't sound right. And what about properties that start with a capital letter?... :(

What's going on now is pretty much that except that there's no more symbol changing - it's just someBar() and someBar(SomeBar). Andrei
Oct 02 2008
parent Ary Borenszweig <ary esperanto.org.ar> writes:
Andrei Alexandrescu wrote:
 Bruno Medeiros wrote:
 Bill Baxter wrote:
 ---
 Somewhat unrelated, but there still exists the annoyance in D that if
 you have to functions with the same name and you want to take the
 address of one of them, you can't.  Furthermore I can't think of a
 reasonable syntax to do that easily.  For that reason,  I really think
 the best getter and setter functionality in D would be something where
 you have distinctly named *functions* getProp and setProp for when you
 want/need functions mirroring samely-named *properties* for which only
 property syntax would work.

Hum, that reminds me of an idea I once had for properties: not using a keyword, but only convention, just as the op* methods for operator overload. Basicly one writes a property with getter and setter functions, like Java: class Foo { SomeBar getSomeBar(); void setSomeBar(SomeBar someBar); } one can then access those functions normally, like Java, but one would then also be able to use a property with the same name of the getter/setter methods, but without 'get' or 'set', and with the capitalization of the first letter fixed, like this: Foo foo = ...; SomeBar someBar = foo.someBar; //same as: SomeBar someBar = foo.getSomeBar(); foo.someBar = new SomeBar(); //same as: foo.setBar(new SomeBar()); This makes it easy to use Java-style code (for instance when porting Java code, or using libs like DWT, etc.). What I don't like here, is that this solution involves working with the capitalization/CamelCase of the property, which doesn't sound right. And what about properties that start with a capital letter?... :(

What's going on now is pretty much that except that there's no more symbol changing - it's just someBar() and someBar(SomeBar).

Pretty much, but not exactly the same, because in this way you can't distinguish a property from a normal method call. The idea is to be able to distinguish two different concepts: properties and "procedures".
 
 Andrei
 

Oct 02 2008
prev sibling parent reply KennyTM~ <kennytm gmail.com> writes:
Denis Koroskin wrote:
 On Sun, 28 Sep 2008 05:45:58 +0400, Andrei Alexandrescu 
 <SeeWebsiteForEmail erdani.org> wrote:
 
 Sergey Gromov wrote:
 Sat, 27 Sep 2008 15:19:01 -0500,
 Andrei Alexandrescu wrote:
 My point is that I agree with all concerns you are raising but I am not
 sure they warrant adding a language feature.

compiler. Now when I think about it I conclude that I was probably wrong. Explicit properties is definitely a feature, even though it seems easy to implement. Injectons could help if Walter were forced into supporting different scoping rules for unified call syntax, but if a.f(b) stays strictly a sugar for f(a,b) this feature helps nothing from a compiler standpoint. So I'll probably agree that these features don't add much to the language, as D doesn't add much to C except safety, productivity, maintainability and claritiy. The clarity/maintainability vs genericity is a tradeoff which is completely in Walter's hands.

Very wise words. I think we all agree that there are some annoyances related to the whole property business, among which the main one is: writeln = 4; That is quite indefensible :o|. I consider the others rather minor, but that's just a personal opinion. How about this. Maybe if we attacked this annoyance in particular, that would be a large bang for the buck without a landslide change in the compiler. We only need some way to inform the compiler, "yes, it's ok to call a.b(c) as a.b = c". Ideas? Andrei

I think property syntax should be disallowed by default. Most of the functions aren't expected to be used as a property, anyway. It would break the code, yes, but it is very easy to fix. Solution of my preference: class Foo { // allow property syntax: property void bar(int baz); // allows "foo.bar = 42;" // allow omitting parens property int bar(); // allows writing "int x = foo.bar;" } Think long term, don't be afraid to break existing code and introduce new features if they improve the language signaficantly. Wrong decision made today may make huge negative impact over time.

Or just issue a warning if property syntax is used without the "property" keyword. But hey, since all of you dislike warnings, this is surely not a solution, right?
Sep 29 2008
parent Sergey Gromov <snake.scaly gmail.com> writes:
Mon, 29 Sep 2008 19:49:49 +0800,
KennyTM~ wrote:
 But hey, since all of you dislike warnings, this is surely not a 
 solution, right?

Right now warnings in DMD are either not shown or are considered errors. I don't know if this is 2.019 only, but there are effectively no warnings: either there are less errors or more errors.
Sep 29 2008
prev sibling parent "Lionello Lunesu" <lionello lunesu.remove.com> writes:
 you didn't like to type parentheses,

I think that's a valid concern. C/C++ code is full of "()"-pairs and they make the code harder to type and more difficult to parse (for me, that is). L.
Sep 27 2008
prev sibling next sibling parent reply Yigal Chripun <yigal100 gmail.com> writes:
Andrei Alexandrescu wrote:
 Sergey Gromov wrote:
 In article <gbgpak$2q10$1 digitalmars.com>,
 brunodomedeiros+spam com.gmail says...
 Also, some more on important bike shed issues:
      for (; !src.done; src.next)
      {
          tgt.put(src.head);
      }

 As a matter of coding style conventions, I would say that using the
 implicit property function call feature on a function that changes
 state is *bad* style, and surely hope the community would agree on that.
 So "src.next" would be must better as "src.next()" as "src.next"
 really just makes me cringe.

I think that property function call feature in general adds an unnecessary ambiguity to the language. I'd prefer functions to be callable only with regular function call syntax, and properties be usable only with member access syntax. The same stands for 'unified function call' feature: if you want to inject a method into an 'array of chars' class you do so explicitly, and only the member call syntax is allowed on that method. Otherwise code tends to become ambiguous and unreadable.

Experience with other languages has shown that using identical syntax for genuine member access and member function access helps maintainability because it allows a type implementer to switch back and forth between implementing a property as a direct member or as a function, transparently to that type's use. Two nice examples of the undesirability of distinct notation are the std::pair first and second members in the STL, which made what seemed like an innocuous decision turn into a catastrophe, and also std::numeric_limits<T>::min and max, which partially prompted creation of an entire language feature(!). Andrei

I've got a few questions regarding this: suppose D gets a specific syntax for properties like C# has: for example: class MyClass { private int x; public int X { get { return x; } set { x = value; } } } wouldn't it solve the problem of the implementer switching between a direct member and a function, transparently to that type's use? In the above you could remove the data member and re-implement the get/set by calling suitable functions. my main question is of style: is it better in your opinion to allow all functions with no parameters to be called as "function_name;" (i.e. no parens) and if you need to refer to the function identifier itself you need to do &func (for example if you want to pass it as a parameter to a different function) OR require an operator such as parens to signify you call the function and the name without parens will just signify the function itself (so no need to use &func when func is a parameter) ?
Sep 25 2008
parent Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
Yigal Chripun wrote:
 Andrei Alexandrescu wrote:
 Sergey Gromov wrote:
 In article <gbgpak$2q10$1 digitalmars.com>,
 brunodomedeiros+spam com.gmail says...
 Also, some more on important bike shed issues:
      for (; !src.done; src.next)
      {
          tgt.put(src.head);
      }

 As a matter of coding style conventions, I would say that using the
 implicit property function call feature on a function that changes
 state is *bad* style, and surely hope the community would agree on that.
 So "src.next" would be must better as "src.next()" as "src.next"
 really just makes me cringe.

unnecessary ambiguity to the language. I'd prefer functions to be callable only with regular function call syntax, and properties be usable only with member access syntax. The same stands for 'unified function call' feature: if you want to inject a method into an 'array of chars' class you do so explicitly, and only the member call syntax is allowed on that method. Otherwise code tends to become ambiguous and unreadable.

for genuine member access and member function access helps maintainability because it allows a type implementer to switch back and forth between implementing a property as a direct member or as a function, transparently to that type's use. Two nice examples of the undesirability of distinct notation are the std::pair first and second members in the STL, which made what seemed like an innocuous decision turn into a catastrophe, and also std::numeric_limits<T>::min and max, which partially prompted creation of an entire language feature(!). Andrei

I've got a few questions regarding this: suppose D gets a specific syntax for properties like C# has: for example: class MyClass { private int x; public int X { get { return x; } set { x = value; } } } wouldn't it solve the problem of the implementer switching between a direct member and a function, transparently to that type's use? In the above you could remove the data member and re-implement the get/set by calling suitable functions.

It would solve the problem by adding a language feature. It is unclear to me whether the cost of adding the feature justifies its benefits.
 my main question is of style:
 is it better in your opinion to allow all functions with no parameters
 to be called as "function_name;" (i.e. no parens) and if you need to
 refer to the function identifier itself you need to do &func (for
 example if you want to pass it as a parameter to a different function)
 OR
 require an operator such as parens to signify you call the function and
 the name without parens will just signify the function itself (so no
 need to use &func when func is a parameter) ?

There are a few principles at work here that I consider universal, plus some others that I consider a matter of preference. One principle that I consider universal is that a language should minimize the number of syntactic constructs that are semantically and/or pragmatically meaningless. Another that I also consider universal is that the more frequently-used constructs should be given syntactic priority over the less-used constructs, particularly when the latter are also at risk of breaking the first principle. C's handling of function names royally breaks both of these principles. It makes func; a valid syntactic construct with inoperant semantics and consequently useless pragmatics. Moreover, a = func; gunc(func); both have valid syntax and semantics, but the pragmatics are the infrequently-used manipulations of function addresses in higher-order programming, something C is not quite adept at to start with. (So that makes the state of affairs all the more ironic.) C++ builds on that irony by making obj.func; b = obj.func; gunc(obj.func); still syntactically valid but one order of magnitude less useful because they traffic in references to member functions, a contraption that is defined sufficiently bad and inefficient to be useless in practice, and also of a type with a syntax I'd be glad to remove from my memory. (How many here _do_ know that type's syntax and have the scars to prove it?) So thinking of function call syntax has quite a few deep underpinnings. We shouldn't confuse habit acquired from C and C++ with some fundamental truth or just a matter of preference that can't be decided on an objective basis. One issue with today's function call syntax in D is that people can write code that is of dubious expressiveness: writeln = 3; Properties would solve this problem by confining assignment syntax to themselves only. Andrei
Sep 25 2008
prev sibling parent Bruno Medeiros <brunodomedeiros+spam com.gmail> writes:
Andrei Alexandrescu wrote:
 Sergey Gromov wrote:
 In article <gbgpak$2q10$1 digitalmars.com>, 
 brunodomedeiros+spam com.gmail says...
 Also, some more on important bike shed issues:
      for (; !src.done; src.next)
      {
          tgt.put(src.head);
      }

 As a matter of coding style conventions, I would say that using the 
 implicit property function call feature on a function that changes 
 state is *bad* style, and surely hope the community would agree on that.
 So "src.next" would be must better as "src.next()" as "src.next" 
 really just makes me cringe.

I think that property function call feature in general adds an unnecessary ambiguity to the language. I'd prefer functions to be callable only with regular function call syntax, and properties be usable only with member access syntax. The same stands for 'unified function call' feature: if you want to inject a method into an 'array of chars' class you do so explicitly, and only the member call syntax is allowed on that method. Otherwise code tends to become ambiguous and unreadable.

Experience with other languages has shown that using identical syntax for genuine member access and member function access helps maintainability because it allows a type implementer to switch back and forth between implementing a property as a direct member or as a function, transparently to that type's use.

Yet, as Sergey mentioned, the switching back cannot be made safely if the field is a delegate (or any other callable type). If the delegate requires parameters, then the switch will cause the other code to not compile. But even worse, if the delegate has zero parameters, the switch will cause the code to compile, but not perform what it was supposed to do! I think this is a clear sign the feature as it is currently implemented is inadequate. So either remove the feature (which I see you're not up to, and I understand why), or fix it, by removing such ambiguity. -- Bruno Medeiros - Software Developer, MSc. in CS/E graduate http://www.prowiki.org/wiki4d/wiki.cgi?BrunoMedeiros#D
Sep 26 2008
prev sibling next sibling parent reply Bruno Medeiros <brunodomedeiros+spam com.gmail> writes:
Sergey Gromov wrote:
 In article <gbgpak$2q10$1 digitalmars.com>, 
 brunodomedeiros+spam com.gmail says...
 Also, some more on important bike shed issues:
      for (; !src.done; src.next)
      {
          tgt.put(src.head);
      }

 As a matter of coding style conventions, I would say that using the 
 implicit property function call feature on a function that changes state 
 is *bad* style, and surely hope the community would agree on that.
 So "src.next" would be must better as "src.next()" as "src.next" really 
 just makes me cringe.

I think that property function call feature in general adds an unnecessary ambiguity to the language. I'd prefer functions to be callable only with regular function call syntax, and properties be usable only with member access syntax. The same stands for 'unified function call' feature: if you want to inject a method into an 'array of chars' class you do so explicitly, and only the member call syntax is allowed on that method. Otherwise code tends to become ambiguous and unreadable.

Yes, I full agree, as many others do, as this has been discussed before (the latest discussion: http://www.digitalmars.com/d/archives/digitalmars/D/Omittable_parens_is_an_evil_73881.html) But that didn't seem to convince any of the higher-ups, so I was hopping to at least have the feature used more sensibly (ie, not being used in functions that are not intended to emulate properties). -- Bruno Medeiros - Software Developer, MSc. in CS/E graduate http://www.prowiki.org/wiki4d/wiki.cgi?BrunoMedeiros#D
Sep 26 2008
parent Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
Bruno Medeiros wrote:
 Sergey Gromov wrote:
 In article <gbgpak$2q10$1 digitalmars.com>, 
 brunodomedeiros+spam com.gmail says...
 Also, some more on important bike shed issues:
      for (; !src.done; src.next)
      {
          tgt.put(src.head);
      }

 As a matter of coding style conventions, I would say that using the 
 implicit property function call feature on a function that changes 
 state is *bad* style, and surely hope the community would agree on that.
 So "src.next" would be must better as "src.next()" as "src.next" 
 really just makes me cringe.

I think that property function call feature in general adds an unnecessary ambiguity to the language. I'd prefer functions to be callable only with regular function call syntax, and properties be usable only with member access syntax. The same stands for 'unified function call' feature: if you want to inject a method into an 'array of chars' class you do so explicitly, and only the member call syntax is allowed on that method. Otherwise code tends to become ambiguous and unreadable.

Yes, I full agree, as many others do, as this has been discussed before (the latest discussion: http://www.digitalmars.com/d/archives/digitalmars/D/Omittable_parens_is an_evil_73881.html) But that didn't seem to convince any of the higher-ups, so I was hopping to at least have the feature used more sensibly (ie, not being used in functions that are not intended to emulate properties).

Following that link I see there is a problem with accessing .mangleof. So the compiler sees a.b.mangleof and performs a.b().mangleof. I think that is a compiler bug because built-in properties like mangleof and sizeof should have priority over symbol evaluation. Then I see a problem with new Thread(run). I think that is C++ habit at work. Finally there's a discussion on how various language additions could work. I don't see how Walter could be convinced. Andrei
Sep 26 2008
prev sibling parent reply "Bent Rasmussen" <IncredibleShrinkingSphere Gmail.com> writes:
 I think that property function call feature in general adds an 
 unnecessary ambiguity to the language.  I'd prefer functions to be 
 callable only with regular function call syntax, and properties be 
 usable only with member access syntax.  The same stands for 'unified 
 function call' feature: if you want to inject a method into an 'array of 
 chars' class you do so explicitly, and only the member call syntax is 
 allowed on that method.  Otherwise code tends to become ambiguous and 
 unreadable.

Or abstracted and generic. Bent
Sep 26 2008
parent reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
Bent Rasmussen wrote:
 I think that property function call feature in general adds an 
 unnecessary ambiguity to the language.  I'd prefer functions to be 
 callable only with regular function call syntax, and properties be 
 usable only with member access syntax.  The same stands for 'unified 
 function call' feature: if you want to inject a method into an 'array 
 of chars' class you do so explicitly, and only the member call syntax 
 is allowed on that method.  Otherwise code tends to become ambiguous 
 and unreadable.

Or abstracted and generic.

Well put! Andrei
Sep 27 2008
parent reply sclytrack <sclytrack pi.be> writes:
== Quote from Andrei Alexandrescu (SeeWebsiteForEmail erdani.org)'s article
 Bent Rasmussen wrote:
 I think that property function call feature in general adds an
 unnecessary ambiguity to the language.  I'd prefer functions to be
 callable only with regular function call syntax, and properties be
 usable only with member access syntax.  The same stands for 'unified
 function call' feature: if you want to inject a method into an 'array
 of chars' class you do so explicitly, and only the member call syntax
 is allowed on that method.  Otherwise code tends to become ambiguous
 and unreadable.

Or abstracted and generic.


For me a property is a RAD feature used in Borland Delphi. Published properties are displayed in the object inspector. (You have private, public, published). If you have some getter routine or setter like in C++, to me those are just methods like any other member function in C++, there is no distinction. Look at the code in Delphi. GetValue, and SetValue are just plain methods and Value is the property. procedure SetValue(const Value: string); virtual; function GetValue: string; virtual; property Value: string read GetValue write SetValue; I know D is not a RAD language but a system language, but I think D should consider making the distinction, and providing that distinction with its ru ... well lets say reflection. Without the reflection there's no need for a different syntax or notation. We could just use the // syntax. Like in //setter //getter (lame joke). Basically a property is what made RAD happen. To me a property is only a property when other programs know about it. As for syntax (b = a.dork() or b=a.dork), syntax of how to call a property is of no real value, it's secondary, its, void, no real meaning. It is just the time it takes to type (). I miss Delphi. http://www.drbob42.com/delphi/property.htm
Sep 27 2008
parent sclytrack <sclytrack pi.be> writes:
 For me a property is a RAD feature used in Borland Delphi. Published properties
 are displayed in the object inspector. (You have private, public, published).
If
 you have some getter routine or setter like in C++, to me those are just
methods
 like any other member function in C++, there is no distinction.
 Look at the code in Delphi. GetValue, and SetValue are just plain methods and
 Value is the property.
  procedure SetValue(const Value: string); virtual;
  function GetValue: string; virtual;
  property Value: string read GetValue write SetValue;
 I know D is not a RAD language but a system language, but I think D should
 consider making the distinction, and providing that distinction with its ru ...
 well lets say reflection.
 Without the reflection there's no need for a different
 syntax or notation. We could just use the // syntax.
 Like in //setter //getter (lame joke).
 Basically a property is what made RAD happen. To me a property is
 only a property when other programs know about it.
 As for syntax (b = a.dork() or b=a.dork), syntax of how to call
 a property is of no real value, it's secondary, its, void,
 no real meaning. It is just the time it takes to type ().
 I miss Delphi.
 http://www.drbob42.com/delphi/property.htm

Just in case things got lost in translation, ... I actually want properties, and a different syntax, like the C# one looks good.
Sep 28 2008
prev sibling next sibling parent reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
Bruno Medeiros wrote:
 Andrei Alexandrescu wrote:
 In wake of the many excellent comments and suggestions made here, I 
 made one more pass through the draft proposal for ranges.

 http://ssli.ee.washington.edu/~aalexand/d/tmp/std_range.html

 There are some comments in red illustrating some uncertainties (not 
 all), and the names of the primitives have been updated. Bicycle shed 
 galore! But don't forget to comment on the reactor as well :o).


 Andrei

""" All ranges satisfy certain invariants outlined below. (r is an object of a range type R.) """ By "object" you actually mean struct no? Struct instance to be even more precise. Also, some more on important bike shed issues: for (; !src.done; src.next) { tgt.put(src.head); } As a matter of coding style conventions, I would say that using the implicit property function call feature on a function that changes state is *bad* style, and surely hope the community would agree on that.

I sure hope they won't agree to an unsupported assertion.
 So "src.next" would be must better as "src.next()" as "src.next" really 
 just makes me cringe.

With me it's the opposite, particularly after I've written and stared at a few hundreds of "()"s due to a compiler bug. Andrei
Sep 25 2008
next sibling parent reply Bruno Medeiros <brunodomedeiros+spam com.gmail> writes:
Andrei Alexandrescu wrote:
 Bruno Medeiros wrote:
 Andrei Alexandrescu wrote:
 In wake of the many excellent comments and suggestions made here, I 
 made one more pass through the draft proposal for ranges.

 http://ssli.ee.washington.edu/~aalexand/d/tmp/std_range.html

 There are some comments in red illustrating some uncertainties (not 
 all), and the names of the primitives have been updated. Bicycle shed 
 galore! But don't forget to comment on the reactor as well :o).


 Andrei

""" All ranges satisfy certain invariants outlined below. (r is an object of a range type R.) """ By "object" you actually mean struct no? Struct instance to be even more precise. Also, some more on important bike shed issues: for (; !src.done; src.next) { tgt.put(src.head); } As a matter of coding style conventions, I would say that using the implicit property function call feature on a function that changes state is *bad* style, and surely hope the community would agree on that.

I sure hope they won't agree to an unsupported assertion.

It's unsupported because it is not an assertion, but more of a subjective opinion, like most coding style conventions (indentation, bracing, etc.). I don't like seeing "src.next" if 'next' isn't either a field, or a function which doesn't change any significant state.
 So "src.next" would be must better as "src.next()" as "src.next" 
 really just makes me cringe.

With me it's the opposite, particularly after I've written and stared at a few hundreds of "()"s due to a compiler bug. Andrei

I'm not imagining what that situation would be, could you explain a bit more? -- Bruno Medeiros - Software Developer, MSc. in CS/E graduate http://www.prowiki.org/wiki4d/wiki.cgi?BrunoMedeiros#D
Sep 26 2008
parent reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
Bruno Medeiros wrote:
 Andrei Alexandrescu wrote:
 Bruno Medeiros wrote:
 Andrei Alexandrescu wrote:
 In wake of the many excellent comments and suggestions made here, I 
 made one more pass through the draft proposal for ranges.

 http://ssli.ee.washington.edu/~aalexand/d/tmp/std_range.html

 There are some comments in red illustrating some uncertainties (not 
 all), and the names of the primitives have been updated. Bicycle 
 shed galore! But don't forget to comment on the reactor as well :o).


 Andrei

""" All ranges satisfy certain invariants outlined below. (r is an object of a range type R.) """ By "object" you actually mean struct no? Struct instance to be even more precise. Also, some more on important bike shed issues: for (; !src.done; src.next) { tgt.put(src.head); } As a matter of coding style conventions, I would say that using the implicit property function call feature on a function that changes state is *bad* style, and surely hope the community would agree on that.

I sure hope they won't agree to an unsupported assertion.

It's unsupported because it is not an assertion, but more of a subjective opinion, like most coding style conventions (indentation, bracing, etc.). I don't like seeing "src.next" if 'next' isn't either a field, or a function which doesn't change any significant state.

I also don't like broccoli, but I eat it because there are reasons to do so. Would you agree that given reason, you'd use it even if you subjectively disliked it?
 So "src.next" would be must better as "src.next()" as "src.next" 
 really just makes me cringe.

With me it's the opposite, particularly after I've written and stared at a few hundreds of "()"s due to a compiler bug. Andrei

I'm not imagining what that situation would be, could you explain a bit more?

I phrased things poorly. The compiler bug is that parens for array functions in the postfix notation are required in some places and not in others. So I was forced to write much more parens than were strictly necessary, and all over the place. It gets really jarring. I've always thought required parens were a big annoyance in C++, and I can't believe people want to bring that back in D. Andrei
Sep 26 2008
parent Bruno Medeiros <brunodomedeiros+spam com.gmail> writes:
Agh, got busy in these last days, so I was missing the discussion.


Andrei Alexandrescu wrote:
 Bruno Medeiros wrote:
 Andrei Alexandrescu wrote:
 Bruno Medeiros wrote:
 Andrei Alexandrescu wrote:
 In wake of the many excellent comments and suggestions made here, I 
 made one more pass through the draft proposal for ranges.

 http://ssli.ee.washington.edu/~aalexand/d/tmp/std_range.html

 There are some comments in red illustrating some uncertainties (not 
 all), and the names of the primitives have been updated. Bicycle 
 shed galore! But don't forget to comment on the reactor as well :o).


 Andrei

""" All ranges satisfy certain invariants outlined below. (r is an object of a range type R.) """ By "object" you actually mean struct no? Struct instance to be even more precise. Also, some more on important bike shed issues: for (; !src.done; src.next) { tgt.put(src.head); } As a matter of coding style conventions, I would say that using the implicit property function call feature on a function that changes state is *bad* style, and surely hope the community would agree on that.

I sure hope they won't agree to an unsupported assertion.

It's unsupported because it is not an assertion, but more of a subjective opinion, like most coding style conventions (indentation, bracing, etc.). I don't like seeing "src.next" if 'next' isn't either a field, or a function which doesn't change any significant state.

I also don't like broccoli, but I eat it because there are reasons to do so. Would you agree that given reason, you'd use it even if you subjectively disliked it?

Yes, if there where good reasons, I'd use it. But what are those reasons?
 So "src.next" would be must better as "src.next()" as "src.next" 
 really just makes me cringe.

With me it's the opposite, particularly after I've written and stared at a few hundreds of "()"s due to a compiler bug. Andrei

I'm not imagining what that situation would be, could you explain a bit more?

I phrased things poorly. The compiler bug is that parens for array functions in the postfix notation are required in some places and not in others. So I was forced to write much more parens than were strictly necessary, and all over the place. It gets really jarring. I've always thought required parens were a big annoyance in C++, and I can't believe people want to bring that back in D. Andrei

What I was trying to understand here, is that if the consistent usage of '()' for function calling, instead of omitting them, had lead you to bugs. (bugs in your code, not bugs in the compiler) In other words, were you presenting a reason for not consistently using parenthesis for function calling? The only reasons I've so far seen you present in favor of omitting parenthesis where this ones: """ I _believe_ a language should obey the principles 1 and 2 (economy of syntax and giving syntactic priority to frequent use cases). Based on that belief, I _think_ D should drop the trailing parens. I agree that somebody could _believe_ in some other principles that replace or override mine and then _think_ that keeping trailing parens is a good thing. Sergey did just that by showing us some very good examples when absence of trailing parens leads to ambiguity. Then there is room for meaningful discussion. It is also possible that somebody simply _believes_ the trailing parens should be kept because they make code clearer to them. That's fair too, as long as there is understanding that that belief comes to a certain extent at odds with principles 1 and 2 (which anyone is free to simply not believe). The only place that needs work is when the connection between belief and thought is unclear, or when the belief is simply wrong. """ I will reply in that respective post. -- Bruno Medeiros - Software Developer, MSc. in CS/E graduate http://www.prowiki.org/wiki4d/wiki.cgi?BrunoMedeiros#D
Oct 02 2008
prev sibling parent reply KennyTM~ <kennytm gmail.com> writes:
Andrei Alexandrescu wrote:
 Bruno Medeiros wrote:
 As a matter of coding style conventions, I would say that using the 
 implicit property function call feature on a function that changes 
 state is *bad* style, and surely hope the community would agree on that.

I sure hope they won't agree to an unsupported assertion.

Actually I *do* hate this "feature" ^_^. And this "feature" has been put to challenged before: * Bug 2159. (http://d.puremagic.com/issues/show_bug.cgi?id=2159) * "Omitting Parens is an Evil" (http://www.digitalmars.com/d/archives/digitalmars/D/Omittable_parens_is_an_evil_73881.html) *http://www.digitalmars.com/pnews/read.php?server=news.digitalmars.com&group=D&artnum=17579 But nothing got changed. To the very least, when using next as a property I assume the call won't produce any secondary effect on src from the syntax, since I'm just reading a state! Not so if one calls src.next() as the "()" is an alarming sign that src _may_ change afterwards. This could is just a problem of personal taste since the compiler does not forbid src.next(), but not so if you're reading other's code. P.S. If src.next() is too lengthy, why not just adopt ++src?
 So "src.next" would be must better as "src.next()" as "src.next" 
 really just makes me cringe.

With me it's the opposite, particularly after I've written and stared at a few hundreds of "()"s due to a compiler bug. Andrei

Sep 26 2008
next sibling parent reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
KennyTM~ wrote:
 Andrei Alexandrescu wrote:
 Bruno Medeiros wrote:
 As a matter of coding style conventions, I would say that using the 
 implicit property function call feature on a function that changes 
 state is *bad* style, and surely hope the community would agree on that.

I sure hope they won't agree to an unsupported assertion.

Actually I *do* hate this "feature" ^_^. And this "feature" has been put to challenged before: * Bug 2159. (http://d.puremagic.com/issues/show_bug.cgi?id=2159) * "Omitting Parens is an Evil" (http://www.digitalmars.com/d/archives/digitalmars/D/Omittable_parens_is an_evil_73881.html) *http://www.digitalmars.com/pnews/read.php?server=news.digitalmars.com& roup=D&artnum=17579 But nothing got changed.

I don't think the bug report has much strength.
 To the very least, when using next as a property I assume the call won't 
 produce any secondary effect on src from the syntax, since I'm just 
 reading a state! Not so if one calls src.next() as the "()" is an 
 alarming sign that src _may_ change afterwards. This could is just a 
 problem of personal taste since the compiler does not forbid src.next(), 
 but not so if you're reading other's code.
 
 P.S. If src.next() is too lengthy, why not just adopt ++src?

Because people (in wake of the recently introduced array operations) may legitimately expect that to mean "increment all elements of src". Andrei
Sep 26 2008
next sibling parent reply "Steven Schveighoffer" <schveiguy yahoo.com> writes:
"Andrei Alexandrescu" wrote
 P.S. If src.next() is too lengthy, why not just adopt ++src?

Because people (in wake of the recently introduced array operations) may legitimately expect that to mean "increment all elements of src".

So in one case, you believe people's assumptions aren't important, i.e. an assumption that .next without parens will not change anything on the object, yet in another case you believe people's assumptions are the main argument. This doesn't sound consistent. Not that I care too much :) I am also in the camp of 'defined properties should be a language feature,' but I admit to using D-properties quite often. The two things that bug me the most about D property syntax: stuff like this: x; What the hell does this mean? It looks like it does nothing. But it could be a function call. If explicit properties were required, and x was defined as a function, not a property, then x; would be a syntax error. And stuff like you said: writefln = 3; I've been bitten by this type of weirdness. In Tango, in the TimeSpan struct, I created several properties, one of which was: ulong seconds(); Which converts the entire timespan to seconds. I also had static 'constructors' to build time spans from common time types that looked like this: static TimeSpan seconds(ulong nseconds); Well, someone complained that this didn't set the timespan's value to 3 seconds, in fact the second line does nothing but create a temporary TimeSpan and throw it away: TimeSpan ts = TimeSpan.seconds(5); ts.seconds = 3; And it does seem like it should set ts to equal 3 seconds. So we were forced to abandon the succinct syntax of the static constructors and rename them fromSeconds, etc. If properties were explicit, then I could define 'hey you can only use this static constructor as a function NOT a property'. It's really those cases where you want to disallow one use or the other that are desired but not allowed by the current language. -Steve
Sep 26 2008
next sibling parent reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
Steven Schveighoffer wrote:
 "Andrei Alexandrescu" wrote
 P.S. If src.next() is too lengthy, why not just adopt ++src?

legitimately expect that to mean "increment all elements of src".

So in one case, you believe people's assumptions aren't important, i.e. an assumption that .next without parens will not change anything on the object, yet in another case you believe people's assumptions are the main argument. This doesn't sound consistent.

Of course it is. One expectation has to do with operational consistency (albeit a tad far-fetched), the other has to do with being used to a mistaken decision in the C language design.
 Not that I care too much :)  I am also in the camp of 'defined properties 
 should be a language feature,' but I admit to using D-properties quite 
 often.
 
 The two things that bug me the most about D property syntax:
 
 stuff like this:
 
 x;
 
 What the hell does this mean?  It looks like it does nothing.  But it could 
 be a function call.  If explicit properties were required, and x was defined 
 as a function, not a property, then x; would be a syntax error.

And what would be the advantage? I routinely use writeln; without feeling it makes for inferior style.
 And stuff like you said:
 
 writefln = 3;
 
 I've been bitten by this type of weirdness.  In Tango, in the TimeSpan 
 struct, I created several properties, one of which was:
 
 ulong seconds();
 
 Which converts the entire timespan to seconds.

I agree.
 I also had static 'constructors' to build time spans from common time types 
 that looked like this:
 
 static TimeSpan seconds(ulong nseconds);
 
 Well, someone complained that this didn't set the timespan's value to 3 
 seconds, in fact the second line does nothing but create a temporary 
 TimeSpan and throw it away:
 
 TimeSpan ts = TimeSpan.seconds(5);
 ts.seconds = 3;
 
 And it does seem like it should set ts to equal 3 seconds.
 
 So we were forced to abandon the succinct syntax of the static constructors 
 and rename them fromSeconds, etc.  If properties were explicit, then I could 
 define 'hey you can only use this static constructor as a function NOT a 
 property'.  It's really those cases where you want to disallow one use or 
 the other that are desired but not allowed by the current language.

I agree. Andrei
Sep 26 2008
next sibling parent reply "Steven Schveighoffer" <schveiguy yahoo.com> writes:
"Andrei Alexandrescu" wrote
 Steven Schveighoffer wrote:
 "Andrei Alexandrescu" wrote
 P.S. If src.next() is too lengthy, why not just adopt ++src?

legitimately expect that to mean "increment all elements of src".

So in one case, you believe people's assumptions aren't important, i.e. an assumption that .next without parens will not change anything on the object, yet in another case you believe people's assumptions are the main argument. This doesn't sound consistent.

Of course it is. One expectation has to do with operational consistency (albeit a tad far-fetched), the other has to do with being used to a mistaken decision in the C language design.

You are assuming that the C language decision to require parentheses for all functions was a design mistake. I would argue that the design was on purpose and correctly served that purpose. The purpose was to remove ambiguity when faced with understanding code without all the context. But that isn't even the questioned practice here. C designers didn't even come across the question of whether an accessor-property should imply no changes to the object, because they don't have properties. The real problem is that no matter how you define next(), you can use it in a way which makes it appear like an accessor, not a function which modifies the object. The source of the problem is D's lack of expressiveness, where I cannot define whether a function cannot be used as a property. Even with a true property definition syntax, you cannot prevent someone from changing an object while inside an accessor, that should be defined by the constancy of the object in question. But indicating that it is preferred to use the property-style means to access the next() member function seems to be misleading to some people. The problem I have with your argument is how you used one case to say 'this is misleading to people, so it's not a valid solution', and in another case say 'it's only misleading because you are used to it, that doesn't matter.' The assumption of which people are important to please is the issue. I understand you can't please everyone, but you shouldn't use the 'people won't like it' argument without real evidence or proof.
 Not that I care too much :)  I am also in the camp of 'defined properties 
 should be a language feature,' but I admit to using D-properties quite 
 often.

 The two things that bug me the most about D property syntax:

 stuff like this:

 x;

 What the hell does this mean?  It looks like it does nothing.  But it 
 could be a function call.  If explicit properties were required, and x 
 was defined as a function, not a property, then x; would be a syntax 
 error.

And what would be the advantage? I routinely use writeln; without feeling it makes for inferior style.

The advantage is not to you, it is to the reader of your code. writeln; is a complete misuse of property syntax, and IMO should not be allowed. writeln is not a property, and shouldn't be used like a property. I routinely use Tango's Stdout.newline; which I admit goes against what I am saying, but it's because I know what Stdout.newline does, and that it is a function. I would gladly change everything to Stdout.newline() if it was required. The problem is not that you or anyone might forget that writeln is a function and not a property, the problem is when you start using the property syntax to call functions that aren't so standard, someone reading the code will be confused as to what the code is supposed to do. The advantage is, you are able to define how a symbol behaves, what the rules are for using it. It makes for clearer code. -Steve
Sep 26 2008
next sibling parent reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
Steven Schveighoffer wrote:
 "Andrei Alexandrescu" wrote
 Steven Schveighoffer wrote:
 "Andrei Alexandrescu" wrote
 P.S. If src.next() is too lengthy, why not just adopt ++src?

legitimately expect that to mean "increment all elements of src".

an assumption that .next without parens will not change anything on the object, yet in another case you believe people's assumptions are the main argument. This doesn't sound consistent.

(albeit a tad far-fetched), the other has to do with being used to a mistaken decision in the C language design.

You are assuming that the C language decision to require parentheses for all functions was a design mistake. I would argue that the design was on purpose and correctly served that purpose. The purpose was to remove ambiguity when faced with understanding code without all the context.

I have stated my assumption and its basis. What is the basis of yours?
 But that isn't even the questioned practice here.  C designers didn't even 
 come across the question of whether an accessor-property should imply no 
 changes to the object, because they don't have properties.  The real problem 
 is that no matter how you define next(), you can use it in a way which makes 
 it appear like an accessor, not a function which modifies the object.  The 
 source of the problem is D's lack of expressiveness, where I cannot define 
 whether a function cannot be used as a property.

I understand your argument, but it hinges on its own definitions and assumptions. You define "accessor" and then complain that something looks like one unexpectedly. Well in some languages a.b does not change anything. In others it does. What gives?
 Even with a true property definition syntax, you cannot prevent someone from 
 changing an object while inside an accessor, that should be defined by the 
 constancy of the object in question.  But indicating that it is preferred to 
 use the property-style means to access the next() member function seems to 
 be misleading to some people.

So that further weakens your argument.
 The problem I have with your argument is how you used one case to say 'this 
 is misleading to people, so it's not a valid solution', and in another case 
 say 'it's only misleading because you are used to it, that doesn't matter.' 
 The assumption of which people are important to please is the issue.  I 
 understand you can't please everyone, but you shouldn't use the 'people 
 won't like it' argument without real evidence or proof.

I agree that ++array may not be easily confused with ++array[]. The situation I am trying to help is improve on a mistake in the C language design that we got so used to, we actually think it's the right thing.
 Not that I care too much :)  I am also in the camp of 'defined properties 
 should be a language feature,' but I admit to using D-properties quite 
 often.

 The two things that bug me the most about D property syntax:

 stuff like this:

 x;

 What the hell does this mean?  It looks like it does nothing.  But it 
 could be a function call.  If explicit properties were required, and x 
 was defined as a function, not a property, then x; would be a syntax 
 error.

it makes for inferior style.

The advantage is not to you, it is to the reader of your code. writeln; is a complete misuse of property syntax, and IMO should not be allowed. writeln is not a property, and shouldn't be used like a property.

Aside from just saying it, do you have any substantive argument?
 I 
 routinely use Tango's Stdout.newline; which I admit goes against what I am 
 saying, but it's because I know what Stdout.newline does, and that it is a 
 function.  I would gladly change everything to Stdout.newline() if it was 
 required.

To what benefit? You seem to be using it today, and that makes your own style incongruent with your argument, yet surely you'd change the style in an iffy if you thought it has serious drawbacks.
 The problem is not that you or anyone might forget that writeln is a 
 function and not a property, the problem is when you start using the 
 property syntax to call functions that aren't so standard, someone reading 
 the code will be confused as to what the code is supposed to do.
 
 The advantage is, you are able to define how a symbol behaves, what the 
 rules are for using it.  It makes for clearer code.

I am not sure. Andrei
Sep 26 2008
next sibling parent reply 0ffh <frank youknow.what.todo.interNETz> writes:
Andrei Alexandrescu wrote:
 Steven Schveighoffer wrote:
 You are assuming that the C language decision to require parentheses 
 for all functions was a design mistake.  I would argue that the design 
 was on purpose and correctly served that purpose.  The purpose was to 
 remove ambiguity when faced with understanding code without all the 
 context.

I have stated my assumption and its basis. What is the basis of yours?

Yum. I think the problem is that when C was designed, K&R didn't consider the use of classes with properties using getter and setter methods. Therefore, it made sense to have the naked function name denote the function pointer, and make braces following an identifier the mark of a function call. I initially had some trouble accepting the explicit & in D to get the function pointer myself. I wouldn't go so far as to call that property of C a design mistake in light of the actual intensions of K&R. But I definitely would defend the choice of a language designer to change that feature in light of more modern programming paradigms. regards, frank
Sep 26 2008
next sibling parent reply Sergey Gromov <snake.scaly gmail.com> writes:
In article <gbjihf$2803$1 digitalmars.com>, 
frank youknow.what.todo.interNETz says...
 Andrei Alexandrescu wrote:
 Steven Schveighoffer wrote:
 You are assuming that the C language decision to require parentheses 
 for all functions was a design mistake.  I would argue that the design 
 was on purpose and correctly served that purpose.  The purpose was to 
 remove ambiguity when faced with understanding code without all the 
 context.

I have stated my assumption and its basis. What is the basis of yours?

Yum. I think the problem is that when C was designed, K&R didn't consider the use of classes with properties using getter and setter methods. Therefore, it made sense to have the naked function name denote the function pointer, and make braces following an identifier the mark of a function call. I initially had some trouble accepting the explicit & in D to get the function pointer myself. I wouldn't go so far as to call that property of C a design mistake in light of the actual intensions of K&R. But I definitely would defend the choice of a language designer to change that feature in light of more modern programming paradigms.

Functions in D are first-class values. It's awkward that a first-class value cannot be accessed by its identifier.
Sep 26 2008
next sibling parent reply 0ffh <frank youknow.what.todo.interNETz> writes:
Sergey Gromov wrote:
 In article <gbjihf$2803$1 digitalmars.com>, 
 frank youknow.what.todo.interNETz says...
 [...]
 I wouldn't go so far as to call that property of C a design mistake in
 light of the actual intensions of K&R. But I definitely would defend
 the choice of a language designer to change that feature in light of
 more modern programming paradigms.

Functions in D are first-class values. It's awkward that a first-class value cannot be accessed by its identifier.

Awkward my sed! Why should a parameterless call be *the* natural meaning of function identifier access, as opposed to the reference or content? I don't go for that kind of absolutism, and you don't make sense to me.
Sep 26 2008
parent KennyTM~ <kennytm gmail.com> writes:
0ffh wrote:
 Sergey Gromov wrote:
 In article <gbjihf$2803$1 digitalmars.com>, 
 frank youknow.what.todo.interNETz says...
 [...]
 I wouldn't go so far as to call that property of C a design mistake in
 light of the actual intensions of K&R. But I definitely would defend
 the choice of a language designer to change that feature in light of
 more modern programming paradigms.

Functions in D are first-class values. It's awkward that a first-class value cannot be accessed by its identifier.

Awkward my sed! Why should a parameterless call be *the* natural meaning of function identifier access, as opposed to the reference or content? I don't go for that kind of absolutism, and you don't make sense to me.

It may not be "the" natural thing, but it is confusing. Someone may hate it, but let me compare with other popular languages: Function call Func. ptr. / Func. obj. / delegate Note D func(), func &func, func (as a template argument) C-like func() func, &func [1] Python func() func Perl func, func() "func" (access by &{"func"}()) [2] VB 6 func AddressOf func [2] VB.NET func() AddressOf func [2,3] C# func() new DelegateType(func) [3] Ruby func, func() &:func (??) [2,4] Java func() <<No such thing??>> PHP func() func, "func" ($x="f";$x();) Pascal func func [5] awk func() <<No such thing>> [6] sed b label <<No such thing>> [6] So, from this table, we see that (except Pascal), if the language calls a function by f(a,b,c,d), then only f() will be considered a valid function call, and a function pointer / function object / delegate is mostly accessed using f, &f or "f". If D deviates from the norm, fine, since I'm not a language designer, but from the view of programmers also familiar with, hmm, C, C++, ECMAScript, Python and/or PHP, this decision will very likely introduce obstacles or even bugs in development. Note: 1. "C-like" includes C, C++ and ECMAScript (Javascript). 2. I haven't tried what func will give in VB.NET and C# 3. In VB 6, Perl and Ruby it is acceptable to write "func 4,5" to mean func(4,5), so it may be unfair to compare with D which "func 4,5" is syntax error. 4. The &:func syntax for Ruby never worked for me. But I'm using Ruby 1.8. 5. I haven't tested what func really does in Pascal/Delphi, but from online sources (JFGI) it seems that func will return a function pointer if it is passed to a function pointer type. 6. Since you mentioned AWKward my SED... ;p
Sep 27 2008
prev sibling parent reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
Sergey Gromov wrote:
 In article <gbjihf$2803$1 digitalmars.com>, 
 frank youknow.what.todo.interNETz says...
 Andrei Alexandrescu wrote:
 Steven Schveighoffer wrote:
 You are assuming that the C language decision to require parentheses 
 for all functions was a design mistake.  I would argue that the design 
 was on purpose and correctly served that purpose.  The purpose was to 
 remove ambiguity when faced with understanding code without all the 
 context.


the use of classes with properties using getter and setter methods. Therefore, it made sense to have the naked function name denote the function pointer, and make braces following an identifier the mark of a function call. I initially had some trouble accepting the explicit & in D to get the function pointer myself. I wouldn't go so far as to call that property of C a design mistake in light of the actual intensions of K&R. But I definitely would defend the choice of a language designer to change that feature in light of more modern programming paradigms.

Functions in D are first-class values. It's awkward that a first-class value cannot be accessed by its identifier.

I agree. But then use of functions for invocation dwarfs use of functions as first-class values, so I think requiring & for the latter is a sensible engineering decision. Besides, it's more efficient to use them as alias parameters, so why not encourage that too. And aliases do not need a "&" :o). If you want to discuss language design mistakes, why don't you discuss a real mistake - the infamous "lazy"? Having a storage class change the way a type is used - now that's the perfect example of the tail wagging the dog. Ambiguity, confusion, non-scalability, and sheer nonsense - you can have it all with lazy. Lazy should be either fixed or honorably discharged pronto. Andrei
Sep 27 2008
next sibling parent reply Sergey Gromov <snake.scaly gmail.com> writes:
Sat, 27 Sep 2008 09:13:54 -0500,
Andrei Alexandrescu wrote:
 If you want to discuss language design mistakes, why don't you discuss a 
 real mistake - the infamous "lazy"? Having a storage class change the 
 way a type is used - now that's the perfect example of the tail wagging 
 the dog. Ambiguity, confusion, non-scalability, and sheer nonsense - you 
 can have it all with lazy. Lazy should be either fixed or honorably 
 discharged pronto.

Because I use class and struct fields, I use properties, I use functions, delegates, aliased functions, vararg templates and conditional compilation---but I never had need for lazy yet.
Sep 27 2008
parent Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
Sergey Gromov wrote:
 Sat, 27 Sep 2008 09:13:54 -0500,
 Andrei Alexandrescu wrote:
 If you want to discuss language design mistakes, why don't you discuss a 
 real mistake - the infamous "lazy"? Having a storage class change the 
 way a type is used - now that's the perfect example of the tail wagging 
 the dog. Ambiguity, confusion, non-scalability, and sheer nonsense - you 
 can have it all with lazy. Lazy should be either fixed or honorably 
 discharged pronto.

Because I use class and struct fields, I use properties, I use functions, delegates, aliased functions, vararg templates and conditional compilation---but I never had need for lazy yet.

enforce :o) Andrei
Sep 27 2008
prev sibling next sibling parent Yigal Chripun <yigal100 gmail.com> writes:
Andrei Alexandrescu wrote:
 I agree. But then use of functions for invocation dwarfs use of
 functions as first-class values, so I think requiring & for the latter
 is a sensible engineering decision.

the above POV seems skewed to me. D is not C! instead of relying on old habits we need to form new, better ones. you basically claim here that "functions as first class values" current usage is low therefore the syntax should be as it is, while another POV would be to say: D is relatively a new language that aims to support more functional programming therefore D should support functions as first class values more than C and adjust its syntax accordingly.
 
 Besides, it's more efficient to use them as alias parameters, so why not
 encourage that too. And aliases do not need a "&" :o).

alias parameters have cons too and the choice what to use should be decided according to the problem at hand.
 
 If you want to discuss language design mistakes, why don't you discuss a
 real mistake - the infamous "lazy"? Having a storage class change the
 way a type is used - now that's the perfect example of the tail wagging
 the dog. Ambiguity, confusion, non-scalability, and sheer nonsense - you
 can have it all with lazy. Lazy should be either fixed or honorably
 discharged pronto.
 

could you please explain more about what's so bad about lazy in your opinion?
 Andrei
 

Sep 27 2008
prev sibling next sibling parent reply Don <nospam nospam.com.au> writes:
Andrei Alexandrescu wrote:
 Sergey Gromov wrote:
 In article <gbjihf$2803$1 digitalmars.com>, 
 frank youknow.what.todo.interNETz says...
 Andrei Alexandrescu wrote:
 Steven Schveighoffer wrote:
 You are assuming that the C language decision to require 
 parentheses for all functions was a design mistake.  I would argue 
 that the design was on purpose and correctly served that purpose.  
 The purpose was to remove ambiguity when faced with understanding 
 code without all the context.


consider the use of classes with properties using getter and setter methods. Therefore, it made sense to have the naked function name denote the function pointer, and make braces following an identifier the mark of a function call. I initially had some trouble accepting the explicit & in D to get the function pointer myself. I wouldn't go so far as to call that property of C a design mistake in light of the actual intensions of K&R. But I definitely would defend the choice of a language designer to change that feature in light of more modern programming paradigms.

Functions in D are first-class values. It's awkward that a first-class value cannot be accessed by its identifier.

I agree. But then use of functions for invocation dwarfs use of functions as first-class values, so I think requiring & for the latter is a sensible engineering decision.

By the way, D supports declarations of function type (not function pointer), though it's not documented in the spec. They always struck me as a really odd feature in C, and they're pretty confusing for newbies: typedef void Func(int); Then you get oddities Func * f = &foo; // OK. Func f = foo; // You might expect this to work, but it makes no sense. This is a case where you do not intuitively expect "foo" (with no &) to be a function pointer. You might as well make it a function invocation. It's pretty interesting to fool around with these fellas in an is() expression.
 Besides, it's more efficient to use them as alias parameters, so why not
 encourage that too. And aliases do not need a "&" :o).

Indeed.
Sep 29 2008
parent reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
Don wrote:
 Andrei Alexandrescu wrote:
 Sergey Gromov wrote:
 In article <gbjihf$2803$1 digitalmars.com>, 
 frank youknow.what.todo.interNETz says...
 Andrei Alexandrescu wrote:
 Steven Schveighoffer wrote:
 You are assuming that the C language decision to require 
 parentheses for all functions was a design mistake.  I would argue 
 that the design was on purpose and correctly served that purpose.  
 The purpose was to remove ambiguity when faced with understanding 
 code without all the context.


consider the use of classes with properties using getter and setter methods. Therefore, it made sense to have the naked function name denote the function pointer, and make braces following an identifier the mark of a function call. I initially had some trouble accepting the explicit & in D to get the function pointer myself. I wouldn't go so far as to call that property of C a design mistake in light of the actual intensions of K&R. But I definitely would defend the choice of a language designer to change that feature in light of more modern programming paradigms.

Functions in D are first-class values. It's awkward that a first-class value cannot be accessed by its identifier.

I agree. But then use of functions for invocation dwarfs use of functions as first-class values, so I think requiring & for the latter is a sensible engineering decision.

By the way, D supports declarations of function type (not function pointer), though it's not documented in the spec. They always struck me as a really odd feature in C, and they're pretty confusing for newbies: typedef void Func(int); Then you get oddities Func * f = &foo; // OK. Func f = foo; // You might expect this to work, but it makes no sense. This is a case where you do not intuitively expect "foo" (with no &) to be a function pointer. You might as well make it a function invocation. It's pretty interesting to fool around with these fellas in an is() expression.

Heh. That is eerily similar to the way C handles it - here's an example: // this is C typedef void foo(int); int main() { foo bar; foo baz; bar = baz; } The typedef goes through. The definitions of bar and baz go through (and are simply equivalent to an extern declaration!!!) The assignment won't go through because, bar is not an lvalue. Andrei
Sep 29 2008
parent reply Don <nospam nospam.com.au> writes:
Andrei Alexandrescu wrote:
 Don wrote:
 Andrei Alexandrescu wrote:
 Sergey Gromov wrote:
 In article <gbjihf$2803$1 digitalmars.com>, 
 frank youknow.what.todo.interNETz says...
 Andrei Alexandrescu wrote:
 Steven Schveighoffer wrote:
 You are assuming that the C language decision to require 
 parentheses for all functions was a design mistake.  I would 
 argue that the design was on purpose and correctly served that 
 purpose.  The purpose was to remove ambiguity when faced with 
 understanding code without all the context.

yours?

consider the use of classes with properties using getter and setter methods. Therefore, it made sense to have the naked function name denote the function pointer, and make braces following an identifier the mark of a function call. I initially had some trouble accepting the explicit & in D to get the function pointer myself. I wouldn't go so far as to call that property of C a design mistake in light of the actual intensions of K&R. But I definitely would defend the choice of a language designer to change that feature in light of more modern programming paradigms.

Functions in D are first-class values. It's awkward that a first-class value cannot be accessed by its identifier.

I agree. But then use of functions for invocation dwarfs use of functions as first-class values, so I think requiring & for the latter is a sensible engineering decision.

By the way, D supports declarations of function type (not function pointer), though it's not documented in the spec. They always struck me as a really odd feature in C, and they're pretty confusing for newbies: typedef void Func(int); Then you get oddities Func * f = &foo; // OK. Func f = foo; // You might expect this to work, but it makes no sense. This is a case where you do not intuitively expect "foo" (with no &) to be a function pointer. You might as well make it a function invocation. It's pretty interesting to fool around with these fellas in an is() expression.

Heh. That is eerily similar to the way C handles it - here's an example: // this is C typedef void foo(int); int main() { foo bar; foo baz; bar = baz; } The typedef goes through. The definitions of bar and baz go through (and are simply equivalent to an extern declaration!!!) The assignment won't go through because, bar is not an lvalue.

Is there a use case for this? I've only ever found it to be a nuisance, or a curiosity at best. It seems like a great bit of C baggage to discard.
Sep 29 2008
parent Bruno Medeiros <brunodomedeiros+spam com.gmail> writes:
Don wrote:
 Andrei Alexandrescu wrote:
 Don wrote:
 Andrei Alexandrescu wrote:
 Sergey Gromov wrote:
 In article <gbjihf$2803$1 digitalmars.com>, 
 frank youknow.what.todo.interNETz says...
 Andrei Alexandrescu wrote:
 Steven Schveighoffer wrote:
 You are assuming that the C language decision to require 
 parentheses for all functions was a design mistake.  I would 
 argue that the design was on purpose and correctly served that 
 purpose.  The purpose was to remove ambiguity when faced with 
 understanding code without all the context.

yours?

consider the use of classes with properties using getter and setter methods. Therefore, it made sense to have the naked function name denote the function pointer, and make braces following an identifier the mark of a function call. I initially had some trouble accepting the explicit & in D to get the function pointer myself. I wouldn't go so far as to call that property of C a design mistake in light of the actual intensions of K&R. But I definitely would defend the choice of a language designer to change that feature in light of more modern programming paradigms.

Functions in D are first-class values. It's awkward that a first-class value cannot be accessed by its identifier.

I agree. But then use of functions for invocation dwarfs use of functions as first-class values, so I think requiring & for the latter is a sensible engineering decision.

By the way, D supports declarations of function type (not function pointer), though it's not documented in the spec. They always struck me as a really odd feature in C, and they're pretty confusing for newbies: typedef void Func(int); Then you get oddities Func * f = &foo; // OK. Func f = foo; // You might expect this to work, but it makes no sense. This is a case where you do not intuitively expect "foo" (with no &) to be a function pointer. You might as well make it a function invocation. It's pretty interesting to fool around with these fellas in an is() expression.

Heh. That is eerily similar to the way C handles it - here's an example: // this is C typedef void foo(int); int main() { foo bar; foo baz; bar = baz; } The typedef goes through. The definitions of bar and baz go through (and are simply equivalent to an extern declaration!!!) The assignment won't go through because, bar is not an lvalue.

Is there a use case for this? I've only ever found it to be a nuisance, or a curiosity at best. It seems like a great bit of C baggage to discard.

What exactly are you trying to discard? Just the typedef void foo(int); syntax? Or any typedef of a function type? Like void func(int); typedef typeof(func) foo; ? -- Bruno Medeiros - Software Developer, MSc. in CS/E graduate http://www.prowiki.org/wiki4d/wiki.cgi?BrunoMedeiros#D
Oct 02 2008
prev sibling parent Bruno Medeiros <brunodomedeiros+spam com.gmail> writes:
Andrei Alexandrescu wrote:
 
 If you want to discuss language design mistakes, why don't you discuss a 
 real mistake - the infamous "lazy"? Having a storage class change the 
 way a type is used - now that's the perfect example of the tail wagging 
 the dog. Ambiguity, confusion, non-scalability, and sheer nonsense - you 
 can have it all with lazy. Lazy should be either fixed or honorably 
 discharged pronto.
 
 
 Andrei
 

What's the problem with lazy? I mean, concretely? Are you trying to see lazy as a type modifier, and thus see it as broken type modifier? Perhaps that's the issue, don't see it as a type modifier! I see lazy as a construct in the same category as '...': a syntax that changes the way a function call processes its arguments. Just because 'lazy' is a word and not a series of symbols doesn't mean it should be interpreted as a type constructor. -- Bruno Medeiros - Software Developer, MSc. in CS/E graduate http://www.prowiki.org/wiki4d/wiki.cgi?BrunoMedeiros#D
Oct 03 2008
prev sibling parent Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
0ffh wrote:
 Andrei Alexandrescu wrote:
 Steven Schveighoffer wrote:
 You are assuming that the C language decision to require parentheses 
 for all functions was a design mistake.  I would argue that the 
 design was on purpose and correctly served that purpose.  The purpose 
 was to remove ambiguity when faced with understanding code without 
 all the context.

I have stated my assumption and its basis. What is the basis of yours?

Yum. I think the problem is that when C was designed, K&R didn't consider the use of classes with properties using getter and setter methods. Therefore, it made sense to have the naked function name denote the function pointer, and make braces following an identifier the mark of a function call. I initially had some trouble accepting the explicit & in D to get the function pointer myself.

No, that decision didn't make sense regardless of classes and functions. The problem in C is that the name of a function - the simplest thing to type - is also the least useful. If I remember correctly, initially it actually did not mean anything, so you could not do anything to it. Then a rule was added to make the function name AUTOMATICALLY become a function pointer (I mean implicitly add a "&"!). But not always! (Bonus points: under what circumstances?) Did anyone even know that? And this is C - a simple language!
 I wouldn't go so far as to call that property of C a design mistake in
 light of the actual intensions of K&R. But I definitely would defend
 the choice of a language designer to change that feature in light of
 more modern programming paradigms.

I like C, but there are some things that were simply wrongly designed (definition syntax, malloc, and (lack of) array design also come to mind). Andrei
Sep 27 2008
prev sibling next sibling parent reply "Steven Schveighoffer" <schveiguy yahoo.com> writes:
"Andrei Alexandrescu" wrote
 Steven Schveighoffer wrote:
 "Andrei Alexandrescu" wrote
 Steven Schveighoffer wrote:
 "Andrei Alexandrescu" wrote
 P.S. If src.next() is too lengthy, why not just adopt ++src?

may legitimately expect that to mean "increment all elements of src".

an assumption that .next without parens will not change anything on the object, yet in another case you believe people's assumptions are the main argument. This doesn't sound consistent.

(albeit a tad far-fetched), the other has to do with being used to a mistaken decision in the C language design.

You are assuming that the C language decision to require parentheses for all functions was a design mistake. I would argue that the design was on purpose and correctly served that purpose. The purpose was to remove ambiguity when faced with understanding code without all the context.

I have stated my assumption and its basis.

Forgive me, but I must have missed it. I saw only your assumption (or supposition) that the C designers made a mistake.
 What is the basis of yours?

I don't have any actual proof that they did this on purpose, I wasn't working at Bell labs when C was invented, in fact, I don't even think I was born ;) But it makes logical sense, and still does. If you want to call a function, you have to use parentheses. If you want to access the function address, no parentheses. It follows other design decisions they made: pointers: Access what it's pointing to, use ->, get address it's pointing to, use symbol alone.
 But that isn't even the questioned practice here.  C designers didn't 
 even come across the question of whether an accessor-property should 
 imply no changes to the object, because they don't have properties.  The 
 real problem is that no matter how you define next(), you can use it in a 
 way which makes it appear like an accessor, not a function which modifies 
 the object.  The source of the problem is D's lack of expressiveness, 
 where I cannot define whether a function cannot be used as a property.

I understand your argument, but it hinges on its own definitions and assumptions. You define "accessor" and then complain that something looks like one unexpectedly. Well in some languages a.b does not change anything. In others it does. What gives?

There is no hard-fast rule that an accessor cannot change things. The issue is that since any function can be an accessor you can't even try to adopt a policy in your library that says 'accessors will not change things'. Because you can't define which functions can be used as accessors and which ones can't. I WANT a.b to be able to be a function call, I just want to specify which functions can be called that way, and which ones cannot, so I can define how my library behaves and someone reading code that uses it will know that my rules were followed.
 Even with a true property definition syntax, you cannot prevent someone 
 from changing an object while inside an accessor, that should be defined 
 by the constancy of the object in question.  But indicating that it is 
 preferred to use the property-style means to access the next() member 
 function seems to be misleading to some people.

So that further weakens your argument.

How so? I'm not talking about whether or not the compiler should restrict the developer on being able to make changes in accessors. That is what const is for. I'm talking about being able to specify a coding convention that says 'accessors shall not change things'. Currently, there's no way to prevent any function from being used as an accessor, so a library developer cannot enforce this rule to people using his library.
 The problem I have with your argument is how you used one case to say 
 'this is misleading to people, so it's not a valid solution', and in 
 another case say 'it's only misleading because you are used to it, that 
 doesn't matter.' The assumption of which people are important to please 
 is the issue.  I understand you can't please everyone, but you shouldn't 
 use the 'people won't like it' argument without real evidence or proof.

I agree that ++array may not be easily confused with ++array[]. The situation I am trying to help is improve on a mistake in the C language design that we got so used to, we actually think it's the right thing.

According to comments on this newsgroup, and some quoted bug reports, many believe that it's the D methodology of not requiring parentheses that is a mistake. As far as I can tell, it's a matter of opinion, not fact.
 Not that I care too much :)  I am also in the camp of 'defined 
 properties should be a language feature,' but I admit to using 
 D-properties quite often.

 The two things that bug me the most about D property syntax:

 stuff like this:

 x;

 What the hell does this mean?  It looks like it does nothing.  But it 
 could be a function call.  If explicit properties were required, and x 
 was defined as a function, not a property, then x; would be a syntax 
 error.

feeling it makes for inferior style.

The advantage is not to you, it is to the reader of your code. writeln; is a complete misuse of property syntax, and IMO should not be allowed. writeln is not a property, and shouldn't be used like a property.

Aside from just saying it, do you have any substantive argument?

The arguments have been made. As I stated, it's my opinion.
 I routinely use Tango's Stdout.newline; which I admit goes against what I 
 am saying, but it's because I know what Stdout.newline does, and that it 
 is a function.  I would gladly change everything to Stdout.newline() if 
 it was required.

To what benefit? You seem to be using it today, and that makes your own style incongruent with your argument, yet surely you'd change the style in an iffy if you thought it has serious drawbacks.

The benefit is clarity. Stdout.newline; looks like a property or field of Stdout. By itself, it's somewhat jarring, but it's somewhat clear that newline is a function if you know the rules of D. However, something like this isn't so clear: Stdout.newline.formatln("Hi there {}", var); Is newline a property of Stdout, on which I'm calling formatln, or is newline a function that I'm calling, and then using the result of that function to call formatln? Compare with: Stdout.newline().formatln("Hi there {}", var); There is no ambiguity. newline is clearly a function, not a property. My personal habits are ones I would like to change. I'd prefer to write clearer code, not shorter code. I consider writing Stdout.newline; a bad habit fostered by the lax function calling rules of D ;)
 The problem is not that you or anyone might forget that writeln is a 
 function and not a property, the problem is when you start using the 
 property syntax to call functions that aren't so standard, someone 
 reading the code will be confused as to what the code is supposed to do.

 The advantage is, you are able to define how a symbol behaves, what the 
 rules are for using it.  It makes for clearer code.

I am not sure.

Neither answer is definitely the 'correct' one, there are tradeoffs between both styles. I like the how D properties are so easy to write, and can be accessed like functions when needed, but I don't like how you can have weird looking code to have to decipher. Having written properties in C# quite a bit, I prefer that method of requiring a formal property definition. -Steve
Sep 29 2008
next sibling parent reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
Steven Schveighoffer wrote:
 "Andrei Alexandrescu" wrote
 Steven Schveighoffer wrote:
 "Andrei Alexandrescu" wrote
 Steven Schveighoffer wrote:
 "Andrei Alexandrescu" wrote
 P.S. If src.next() is too lengthy, why not just adopt ++src?

may legitimately expect that to mean "increment all elements of src".

an assumption that .next without parens will not change anything on the object, yet in another case you believe people's assumptions are the main argument. This doesn't sound consistent.

(albeit a tad far-fetched), the other has to do with being used to a mistaken decision in the C language design.

all functions was a design mistake. I would argue that the design was on purpose and correctly served that purpose. The purpose was to remove ambiguity when faced with understanding code without all the context.


Forgive me, but I must have missed it. I saw only your assumption (or supposition) that the C designers made a mistake.

My post on 25 Sep 2008 17:58:52 -0500 mentions: "One principle that I consider universal is that a language should minimize the number of syntactic constructs that are semantically and/or pragmatically meaningless. Another that I also consider universal is that the more frequently-used constructs should be given syntactic priority over the less-used constructs, particularly when the latter are also at risk of breaking the first principle."
 What is the basis of yours?

I don't have any actual proof that they did this on purpose, I wasn't working at Bell labs when C was invented, in fact, I don't even think I was born ;) But it makes logical sense, and still does. If you want to call a function, you have to use parentheses. If you want to access the function address, no parentheses.

This is no logic. Your proof is just stating the conclusion. It's a logical fallacy called proof by assertion. http://en.wikipedia.org/wiki/Proof_by_assertion
 It follows other design decisions they made:
 
 pointers:  Access what it's pointing to, use ->, get address it's pointing 
 to, use symbol alone.

This is wrong too. If you use symbol alone, you are not getting its address. You are referring the pointer, which is an address. It would have been consistent with other design decisions if referring a variable alone would take its address.
 But that isn't even the questioned practice here.  C designers didn't 
 even come across the question of whether an accessor-property should 
 imply no changes to the object, because they don't have properties.  The 
 real problem is that no matter how you define next(), you can use it in a 
 way which makes it appear like an accessor, not a function which modifies 
 the object.  The source of the problem is D's lack of expressiveness, 
 where I cannot define whether a function cannot be used as a property.

assumptions. You define "accessor" and then complain that something looks like one unexpectedly. Well in some languages a.b does not change anything. In others it does. What gives?

There is no hard-fast rule that an accessor cannot change things. The issue is that since any function can be an accessor you can't even try to adopt a policy in your library that says 'accessors will not change things'. Because you can't define which functions can be used as accessors and which ones can't.

Const takes care of that.
 I WANT a.b to be able to be a function call, I just want to specify which 
 functions can be called that way, and which ones cannot, so I can define how 
 my library behaves and someone reading code that uses it will know that my 
 rules were followed.

Why do you want to specify that? What difference does it make? Why would you complicate the language just for a hypothetical convention that's not even compiler-checked?
 Even with a true property definition syntax, you cannot prevent someone 
 from changing an object while inside an accessor, that should be defined 
 by the constancy of the object in question.  But indicating that it is 
 preferred to use the property-style means to access the next() member 
 function seems to be misleading to some people.


How so? I'm not talking about whether or not the compiler should restrict the developer on being able to make changes in accessors. That is what const is for. I'm talking about being able to specify a coding convention that says 'accessors shall not change things'. Currently, there's no way to prevent any function from being used as an accessor, so a library developer cannot enforce this rule to people using his library.

I personally think it's great that any function can be used as an accessor. I'd be convinced otherwise presented a good arguments against it. What problems are you foreseeing with not being able to enforce against not using ()?
 The problem I have with your argument is how you used one case to say 
 'this is misleading to people, so it's not a valid solution', and in 
 another case say 'it's only misleading because you are used to it, that 
 doesn't matter.' The assumption of which people are important to please 
 is the issue.  I understand you can't please everyone, but you shouldn't 
 use the 'people won't like it' argument without real evidence or proof.

situation I am trying to help is improve on a mistake in the C language design that we got so used to, we actually think it's the right thing.

According to comments on this newsgroup, and some quoted bug reports, many believe that it's the D methodology of not requiring parentheses that is a mistake. As far as I can tell, it's a matter of opinion, not fact.
 Not that I care too much :)  I am also in the camp of 'defined 
 properties should be a language feature,' but I admit to using 
 D-properties quite often.

 The two things that bug me the most about D property syntax:

 stuff like this:

 x;

 What the hell does this mean?  It looks like it does nothing.  But it 
 could be a function call.  If explicit properties were required, and x 
 was defined as a function, not a property, then x; would be a syntax 
 error.

feeling it makes for inferior style.

is a complete misuse of property syntax, and IMO should not be allowed. writeln is not a property, and shouldn't be used like a property.


The arguments have been made. As I stated, it's my opinion.
 I routinely use Tango's Stdout.newline; which I admit goes against what I 
 am saying, but it's because I know what Stdout.newline does, and that it 
 is a function.  I would gladly change everything to Stdout.newline() if 
 it was required.

style incongruent with your argument, yet surely you'd change the style in an iffy if you thought it has serious drawbacks.

The benefit is clarity. Stdout.newline; looks like a property or field of Stdout. By itself, it's somewhat jarring, but it's somewhat clear that newline is a function if you know the rules of D.

But you do use Stdout.newline and the alleged loss in clarity doesn't seem to be phasing you.
 However, something like this isn't so clear:
 
 Stdout.newline.formatln("Hi there {}", var);
 
 Is newline a property of Stdout, on which I'm calling formatln, or is 
 newline a function that I'm calling, and then using the result of that 
 function to call formatln?  Compare with:
 
 Stdout.newline().formatln("Hi there {}", var);
 
 There is no ambiguity.  newline is clearly a function, not a property.

There is no ambiguity either case. You evaluate Stdout.newline. The evaluation yields a value of some type. Then you evaluate formatln against that value. What's missing is the unstated assumption that "if it doesn't have trailing parens, it must be a field". I don't think it is useful to be making that assumption.
 My personal habits are ones I would like to change.  I'd prefer to write 
 clearer code, not shorter code.  I consider writing Stdout.newline; a bad 
 habit fostered by the lax function calling rules of D ;)

Giving the choice of shorter vs. clearer is a false choice, another fallacy if I remember correctly. Put another way, wouldn't you at best write shorter and clearer code?
 The problem is not that you or anyone might forget that writeln is a 
 function and not a property, the problem is when you start using the 
 property syntax to call functions that aren't so standard, someone 
 reading the code will be confused as to what the code is supposed to do.

 The advantage is, you are able to define how a symbol behaves, what the 
 rules are for using it.  It makes for clearer code.


Neither answer is definitely the 'correct' one, there are tradeoffs between both styles. I like the how D properties are so easy to write, and can be accessed like functions when needed, but I don't like how you can have weird looking code to have to decipher. Having written properties in C# quite a bit, I prefer that method of requiring a formal property definition.

I am not seeing weird code that is there to be deciphered. There is this "writeln = 3" weirdness that I'd love to weed out, but all this required trailing parens stuff simply doesn't have any good argument working in its favor. Andrei
Sep 29 2008
parent reply "Steven Schveighoffer" <schveiguy yahoo.com> writes:
"Andrei Alexandrescu" wrote
 Steven Schveighoffer wrote:
 "Andrei Alexandrescu" wrote
 Steven Schveighoffer wrote:
 "Andrei Alexandrescu" wrote
 Steven Schveighoffer wrote:
 "Andrei Alexandrescu" wrote
 P.S. If src.next() is too lengthy, why not just adopt ++src?

may legitimately expect that to mean "increment all elements of src".

i.e. an assumption that .next without parens will not change anything on the object, yet in another case you believe people's assumptions are the main argument. This doesn't sound consistent.

consistency (albeit a tad far-fetched), the other has to do with being used to a mistaken decision in the C language design.

for all functions was a design mistake. I would argue that the design was on purpose and correctly served that purpose. The purpose was to remove ambiguity when faced with understanding code without all the context.


Forgive me, but I must have missed it. I saw only your assumption (or supposition) that the C designers made a mistake.

My post on 25 Sep 2008 17:58:52 -0500 mentions: "One principle that I consider universal is that a language should minimize the number of syntactic constructs that are semantically and/or pragmatically meaningless. Another that I also consider universal is that the more frequently-used constructs should be given syntactic priority over the less-used constructs, particularly when the latter are also at risk of breaking the first principle."

Hidden in this statement is that you consider () to be semantically meaningless. I see it not as meaningless, but a distinction between what the symbol is supposed to represent. A property should represent a value. A function should represent an action. I'll also note that your basis/proof is not any more proof than mine ;) You just made statements about what you believe, as did I.
 What is the basis of yours?

I don't have any actual proof that they did this on purpose, I wasn't working at Bell labs when C was invented, in fact, I don't even think I was born ;) But it makes logical sense, and still does. If you want to call a function, you have to use parentheses. If you want to access the function address, no parentheses.

This is no logic. Your proof is just stating the conclusion. It's a logical fallacy called proof by assertion.

Sorry, change my statement to 'But it makes sense and still does'. And I never said it was a proof. It's a statement of my reasons behind believing the () decision was on purpose.
 It follows other design decisions they made:

 pointers:  Access what it's pointing to, use ->, get address it's 
 pointing to, use symbol alone.

This is wrong too. If you use symbol alone, you are not getting its address. You are referring the pointer, which is an address. It would have been consistent with other design decisions if referring a variable alone would take its address.

Not really. A function symbol by itself is the address of the function. A pointer symbol by itself is the address of the data it points to. It's the same. I don't think you understood the detail that I stated, 'get address it's pointing to', not 'get address of the pointer'.
 But that isn't even the questioned practice here.  C designers didn't 
 even come across the question of whether an accessor-property should 
 imply no changes to the object, because they don't have properties. 
 The real problem is that no matter how you define next(), you can use 
 it in a way which makes it appear like an accessor, not a function 
 which modifies the object.  The source of the problem is D's lack of 
 expressiveness, where I cannot define whether a function cannot be used 
 as a property.

assumptions. You define "accessor" and then complain that something looks like one unexpectedly. Well in some languages a.b does not change anything. In others it does. What gives?

There is no hard-fast rule that an accessor cannot change things. The issue is that since any function can be an accessor you can't even try to adopt a policy in your library that says 'accessors will not change things'. Because you can't define which functions can be used as accessors and which ones can't.

Const takes care of that.

If I state that 'all accessors used in my library will not change things,' then how can I enforce this? I can't, if I ever want non-const methods that do not take arguments, because those methods could be called as properties.
 I WANT a.b to be able to be a function call, I just want to specify which 
 functions can be called that way, and which ones cannot, so I can define 
 how my library behaves and someone reading code that uses it will know 
 that my rules were followed.

Why do you want to specify that? What difference does it make? Why would you complicate the language just for a hypothetical convention that's not even compiler-checked?

It allows for (in my opinion) better coding practices that are impossible today. For one, if I restrict a member to being only accessed as a property, it is interchangable with a field later on. I think there have already been many given instances where having a defined property makes it clearer, I'm not going to repeat them all.
 Even with a true property definition syntax, you cannot prevent someone 
 from changing an object while inside an accessor, that should be 
 defined by the constancy of the object in question.  But indicating 
 that it is preferred to use the property-style means to access the 
 next() member function seems to be misleading to some people.


How so? I'm not talking about whether or not the compiler should restrict the developer on being able to make changes in accessors. That is what const is for. I'm talking about being able to specify a coding convention that says 'accessors shall not change things'. Currently, there's no way to prevent any function from being used as an accessor, so a library developer cannot enforce this rule to people using his library.

I personally think it's great that any function can be used as an accessor. I'd be convinced otherwise presented a good arguments against it. What problems are you foreseeing with not being able to enforce against not using ()?

I have given good arguments. You seem to dismiss them as failed proofs, but that is your call.
 The problem I have with your argument is how you used one case to say 
 'this is misleading to people, so it's not a valid solution', and in 
 another case say 'it's only misleading because you are used to it, that 
 doesn't matter.' The assumption of which people are important to please 
 is the issue.  I understand you can't please everyone, but you 
 shouldn't use the 'people won't like it' argument without real evidence 
 or proof.

situation I am trying to help is improve on a mistake in the C language design that we got so used to, we actually think it's the right thing.

According to comments on this newsgroup, and some quoted bug reports, many believe that it's the D methodology of not requiring parentheses that is a mistake. As far as I can tell, it's a matter of opinion, not fact.
 Not that I care too much :)  I am also in the camp of 'defined 
 properties should be a language feature,' but I admit to using 
 D-properties quite often.

 The two things that bug me the most about D property syntax:

 stuff like this:

 x;

 What the hell does this mean?  It looks like it does nothing.  But it 
 could be a function call.  If explicit properties were required, and 
 x was defined as a function, not a property, then x; would be a 
 syntax error.

feeling it makes for inferior style.

writeln; is a complete misuse of property syntax, and IMO should not be allowed. writeln is not a property, and shouldn't be used like a property.


The arguments have been made. As I stated, it's my opinion.
 I routinely use Tango's Stdout.newline; which I admit goes against what 
 I am saying, but it's because I know what Stdout.newline does, and that 
 it is a function.  I would gladly change everything to Stdout.newline() 
 if it was required.

style incongruent with your argument, yet surely you'd change the style in an iffy if you thought it has serious drawbacks.

The benefit is clarity. Stdout.newline; looks like a property or field of Stdout. By itself, it's somewhat jarring, but it's somewhat clear that newline is a function if you know the rules of D.

But you do use Stdout.newline and the alleged loss in clarity doesn't seem to be phasing you.

It does phase me. I sometimes catch myself doing it and add the parentheses.
 However, something like this isn't so clear:

 Stdout.newline.formatln("Hi there {}", var);

 Is newline a property of Stdout, on which I'm calling formatln, or is 
 newline a function that I'm calling, and then using the result of that 
 function to call formatln?  Compare with:

 Stdout.newline().formatln("Hi there {}", var);

 There is no ambiguity.  newline is clearly a function, not a property.

There is no ambiguity either case. You evaluate Stdout.newline. The evaluation yields a value of some type. Then you evaluate formatln against that value.

OK, then tell me what this does: x.y.z(); Is y a property/field of x or a function call with no args? I see a benefit to being able to understand a line of code without requiring lots of extra context. I have to do less lookups of the source of a function or property.
 What's missing is the unstated assumption that "if it doesn't have 
 trailing parens, it must be a field". I don't think it is useful to be 
 making that assumption.

Not in D's current state, which leaves the reader to continually look up what a symbol is until he can hammer into his brain whether a symbol is a function or not.
 My personal habits are ones I would like to change.  I'd prefer to write 
 clearer code, not shorter code.  I consider writing Stdout.newline; a bad 
 habit fostered by the lax function calling rules of D ;)

Giving the choice of shorter vs. clearer is a false choice, another fallacy if I remember correctly. Put another way, wouldn't you at best write shorter and clearer code?

Sure, the shortest clear code I have seen is that with parentheses. There is a point at which shorter becomes less clear, in which case the choice is required. How is that a fallacy?
 The problem is not that you or anyone might forget that writeln is a 
 function and not a property, the problem is when you start using the 
 property syntax to call functions that aren't so standard, someone 
 reading the code will be confused as to what the code is supposed to 
 do.

 The advantage is, you are able to define how a symbol behaves, what the 
 rules are for using it.  It makes for clearer code.


Neither answer is definitely the 'correct' one, there are tradeoffs between both styles. I like the how D properties are so easy to write, and can be accessed like functions when needed, but I don't like how you can have weird looking code to have to decipher. Having written properties in C# quite a bit, I prefer that method of requiring a formal property definition.

I am not seeing weird code that is there to be deciphered. There is this "writeln = 3" weirdness that I'd love to weed out, but all this required trailing parens stuff simply doesn't have any good argument working in its favor.

I think we're all in agreement about writeln = 3. If this is the only thing that gets fixed, it would be a huge step. I'd prefer full properties, but this at least makes things much clearer. 'simply doesn't have any good argument' is your opinion, not a fact. -Steve
Sep 29 2008
next sibling parent reply KennyTM~ <kennytm gmail.com> writes:
Bill Baxter wrote:
 On Tue, Sep 30, 2008 at 3:36 AM, Steven Schveighoffer
 <schveiguy yahoo.com> wrote:
 There is no ambiguity either case. You evaluate Stdout.newline. The
 evaluation yields a value of some type. Then you evaluate formatln against
 that value.

x.y.z(); Is y a property/field of x or a function call with no args? I see a benefit to being able to understand a line of code without requiring lots of extra context. I have to do less lookups of the source of a function or property.

The problem with this argument is that unless you disallow properties altogether, you still won't know whether y is actually a field or a call to a property method without looking it up. --bb

I think the distinction of with and without () is pretty stylistic, because the same argument can even be applied to operator overloading (does a=b means pointing the variable a to b, or calling a.opAssign(b)?) For me, I would use () if the function do modify the object itself.
Sep 29 2008
next sibling parent reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
KennyTM~ wrote:
 Bill Baxter wrote:
 On Tue, Sep 30, 2008 at 3:36 AM, Steven Schveighoffer
 <schveiguy yahoo.com> wrote:
 There is no ambiguity either case. You evaluate Stdout.newline. The
 evaluation yields a value of some type. Then you evaluate formatln 
 against
 that value.

x.y.z(); Is y a property/field of x or a function call with no args? I see a benefit to being able to understand a line of code without requiring lots of extra context. I have to do less lookups of the source of a function or property.

The problem with this argument is that unless you disallow properties altogether, you still won't know whether y is actually a field or a call to a property method without looking it up. --bb

I think the distinction of with and without () is pretty stylistic, because the same argument can even be applied to operator overloading (does a=b means pointing the variable a to b, or calling a.opAssign(b)?) For me, I would use () if the function do modify the object itself.

But range.next does modify the object. On what basis do I need to add the trailing parens? Andrei
Sep 29 2008
parent KennyTM~ <kennytm gmail.com> writes:
Andrei Alexandrescu wrote:
 KennyTM~ wrote:
 Bill Baxter wrote:
 On Tue, Sep 30, 2008 at 3:36 AM, Steven Schveighoffer
 <schveiguy yahoo.com> wrote:
 There is no ambiguity either case. You evaluate Stdout.newline. The
 evaluation yields a value of some type. Then you evaluate formatln 
 against
 that value.

x.y.z(); Is y a property/field of x or a function call with no args? I see a benefit to being able to understand a line of code without requiring lots of extra context. I have to do less lookups of the source of a function or property.

The problem with this argument is that unless you disallow properties altogether, you still won't know whether y is actually a field or a call to a property method without looking it up. --bb

I think the distinction of with and without () is pretty stylistic, because the same argument can even be applied to operator overloading (does a=b means pointing the variable a to b, or calling a.opAssign(b)?) For me, I would use () if the function do modify the object itself.

But range.next does modify the object. On what basis do I need to add the trailing parens? Andrei

Well that's why *I* would use range.next() ;)
Sep 29 2008
prev sibling next sibling parent reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
Bill Baxter wrote:
 On Tue, Sep 30, 2008 at 4:08 AM, KennyTM~ <kennytm gmail.com> wrote:
 Bill Baxter wrote:
 On Tue, Sep 30, 2008 at 3:36 AM, Steven Schveighoffer
 <schveiguy yahoo.com> wrote:
 There is no ambiguity either case. You evaluate Stdout.newline. The
 evaluation yields a value of some type. Then you evaluate formatln
 against
 that value.

x.y.z(); Is y a property/field of x or a function call with no args? I see a benefit to being able to understand a line of code without requiring lots of extra context. I have to do less lookups of the source of a function or property.

altogether, you still won't know whether y is actually a field or a call to a property method without looking it up. --bb

the same argument can even be applied to operator overloading (does a=b means pointing the variable a to b, or calling a.opAssign(b)?) For me, I would use () if the function do modify the object itself.

I think the counter-argument to what I just said is that if properties had to be declared explicitly then at least we would know that .y is either a field or else something the designer *intended* to behave like a property. Which does tell us something. It most likely means that it's not an extremely heavy-weight operation, just a lightweight accessor with maybe a little logic on top. That could be wrong, but in that case it's the class designer's fault for misleading us.

So the logic is: "A field can be accessed with a.b, but not with a.b(). Therefore if someone writes a.b, it may be a field or code, but it someone writes a.b() it will always be code. Given that a field is cheap and code ranges from cheap to expensive, good style is to have a.b always refer to cheap stuff, and a.b() always refer to not-expected-to-be-cheap stuff." I agree with that logic. It makes for a nice convention. (That convention is already broken by the built-in dup and sort, but let past mistakes be past mistakes.) My response to that is that concerns of keeping the language simple and of facilitating generic code are counterarguments that should be weighed in as well. An experience anecdote - when I wrote Loki I went for the convention that if a type template argument only accepts built-in types it should be written "class T", otherwise it should be written "typename T". The convention was a failure: lack of compiler enforcement meant that "bugs" would be in there without being noticed (and readers would subsequently complain about inconsistencies), at times it was even unclear whether a template would accept certain built-in types or not and there was no way to check that, etc. A similar fate could happen for that convention. It could be nice, yes, but it could also become a nuisance ("was I expected to put parens here, or not? Hell, nobody seems to care anyway, at least this version of the library."). Andrei
Sep 29 2008
parent reply Bruno Medeiros <brunodomedeiros+spam com.gmail> writes:
Andrei Alexandrescu wrote:
 Bill Baxter wrote:
 On Tue, Sep 30, 2008 at 4:08 AM, KennyTM~ <kennytm gmail.com> wrote:
 Bill Baxter wrote:
 On Tue, Sep 30, 2008 at 3:36 AM, Steven Schveighoffer
 <schveiguy yahoo.com> wrote:
 There is no ambiguity either case. You evaluate Stdout.newline. The
 evaluation yields a value of some type. Then you evaluate formatln
 against
 that value.

x.y.z(); Is y a property/field of x or a function call with no args? I see a benefit to being able to understand a line of code without requiring lots of extra context. I have to do less lookups of the source of a function or property.

altogether, you still won't know whether y is actually a field or a call to a property method without looking it up. --bb

because the same argument can even be applied to operator overloading (does a=b means pointing the variable a to b, or calling a.opAssign(b)?) For me, I would use () if the function do modify the object itself.

I think the counter-argument to what I just said is that if properties had to be declared explicitly then at least we would know that .y is either a field or else something the designer *intended* to behave like a property. Which does tell us something. It most likely means that it's not an extremely heavy-weight operation, just a lightweight accessor with maybe a little logic on top. That could be wrong, but in that case it's the class designer's fault for misleading us.

So the logic is: "A field can be accessed with a.b, but not with a.b(). Therefore if someone writes a.b, it may be a field or code, but it someone writes a.b() it will always be code. Given that a field is cheap and code ranges from cheap to expensive, good style is to have a.b always refer to cheap stuff, and a.b() always refer to not-expected-to-be-cheap stuff." I agree with that logic. It makes for a nice convention. (That convention is already broken by the built-in dup and sort, but let past mistakes be past mistakes.) My response to that is that concerns of keeping the language simple and of facilitating generic code are counterarguments that should be weighed in as well.

In what way does that convention/style go against facilitating generic code?
 An experience anecdote - when I wrote Loki I went for the convention 
 that if a type template argument only accepts built-in types it should 
 be written "class T", otherwise it should be written "typename T". The 
 convention was a failure: lack of compiler enforcement meant that "bugs" 
 would be in there without being noticed (and readers would subsequently 
 complain about inconsistencies), at times it was even unclear whether a 
 template would accept certain built-in types or not and there was no way 
 to check that, etc. A similar fate could happen for that convention. It 
 could be nice, yes, but it could also become a nuisance ("was I expected 
 to put parens here, or not? Hell, nobody seems to care anyway, at least 
 this version of the library.").
 

Uh, of course, and that's why we want this convention to be enforced by the language, and not be merely a convention. -- Bruno Medeiros - Software Developer, MSc. in CS/E graduate http://www.prowiki.org/wiki4d/wiki.cgi?BrunoMedeiros#D
Oct 02 2008
parent reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
Bruno Medeiros wrote:
 Andrei Alexandrescu wrote:
 Bill Baxter wrote:
 On Tue, Sep 30, 2008 at 4:08 AM, KennyTM~ <kennytm gmail.com> wrote:
 Bill Baxter wrote:
 On Tue, Sep 30, 2008 at 3:36 AM, Steven Schveighoffer
 <schveiguy yahoo.com> wrote:
 There is no ambiguity either case. You evaluate Stdout.newline. The
 evaluation yields a value of some type. Then you evaluate formatln
 against
 that value.

x.y.z(); Is y a property/field of x or a function call with no args? I see a benefit to being able to understand a line of code without requiring lots of extra context. I have to do less lookups of the source of a function or property.

altogether, you still won't know whether y is actually a field or a call to a property method without looking it up. --bb

because the same argument can even be applied to operator overloading (does a=b means pointing the variable a to b, or calling a.opAssign(b)?) For me, I would use () if the function do modify the object itself.

I think the counter-argument to what I just said is that if properties had to be declared explicitly then at least we would know that .y is either a field or else something the designer *intended* to behave like a property. Which does tell us something. It most likely means that it's not an extremely heavy-weight operation, just a lightweight accessor with maybe a little logic on top. That could be wrong, but in that case it's the class designer's fault for misleading us.

So the logic is: "A field can be accessed with a.b, but not with a.b(). Therefore if someone writes a.b, it may be a field or code, but it someone writes a.b() it will always be code. Given that a field is cheap and code ranges from cheap to expensive, good style is to have a.b always refer to cheap stuff, and a.b() always refer to not-expected-to-be-cheap stuff." I agree with that logic. It makes for a nice convention. (That convention is already broken by the built-in dup and sort, but let past mistakes be past mistakes.) My response to that is that concerns of keeping the language simple and of facilitating generic code are counterarguments that should be weighed in as well.

In what way does that convention/style go against facilitating generic code?

A generic function writing obj.symbol cannot work across objects that implement symbol as a computed property vs. a field. Andrei
Oct 02 2008
parent reply Bruno Medeiros <brunodomedeiros+spam com.gmail> writes:
Andrei Alexandrescu wrote:
 Bruno Medeiros wrote:
 Andrei Alexandrescu wrote:
 Bill Baxter wrote:
 On Tue, Sep 30, 2008 at 4:08 AM, KennyTM~ <kennytm gmail.com> wrote:
 I think the distinction of with and without () is pretty stylistic, 
 because
 the same argument can even be applied to operator overloading (does 
 a=b
 means pointing the variable a to b, or calling a.opAssign(b)?)

 For me, I would use () if the function do modify the object itself.

I think the counter-argument to what I just said is that if properties had to be declared explicitly then at least we would know that .y is either a field or else something the designer *intended* to behave like a property. Which does tell us something. It most likely means that it's not an extremely heavy-weight operation, just a lightweight accessor with maybe a little logic on top. That could be wrong, but in that case it's the class designer's fault for misleading us.

So the logic is: "A field can be accessed with a.b, but not with a.b(). Therefore if someone writes a.b, it may be a field or code, but it someone writes a.b() it will always be code. Given that a field is cheap and code ranges from cheap to expensive, good style is to have a.b always refer to cheap stuff, and a.b() always refer to not-expected-to-be-cheap stuff." I agree with that logic. It makes for a nice convention. (That convention is already broken by the built-in dup and sort, but let past mistakes be past mistakes.) My response to that is that concerns of keeping the language simple and of facilitating generic code are counterarguments that should be weighed in as well.

In what way does that convention/style go against facilitating generic code?

A generic function writing obj.symbol cannot work across objects that implement symbol as a computed property vs. a field. Andrei

I'm not following. If 'obj.symbol' is syntactically available and is valid code, shouldn't the generic function work ok regardless of whether 'symbol' is a computed property or a field? I mean, what is even the difference from the status quo? -- Bruno Medeiros - Software Developer, MSc. in CS/E graduate http://www.prowiki.org/wiki4d/wiki.cgi?BrunoMedeiros#D
Oct 02 2008
parent reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
Bruno Medeiros wrote:
 Andrei Alexandrescu wrote:
 Bruno Medeiros wrote:
 Andrei Alexandrescu wrote:
 Bill Baxter wrote:
 On Tue, Sep 30, 2008 at 4:08 AM, KennyTM~ <kennytm gmail.com>
 wrote:
 I think the distinction of with and without () is pretty 
 stylistic, because the same argument can even be applied to
 operator overloading (does a=b means pointing the variable
 a to b, or calling a.opAssign(b)?)
 
 For me, I would use () if the function do modify the object
 itself.

I think the counter-argument to what I just said is that if properties had to be declared explicitly then at least we would know that .y is either a field or else something the designer *intended* to behave like a property. Which does tell us something. It most likely means that it's not an extremely heavy-weight operation, just a lightweight accessor with maybe a little logic on top. That could be wrong, but in that case it's the class designer's fault for misleading us.

So the logic is: "A field can be accessed with a.b, but not with a.b(). Therefore if someone writes a.b, it may be a field or code, but it someone writes a.b() it will always be code. Given that a field is cheap and code ranges from cheap to expensive, good style is to have a.b always refer to cheap stuff, and a.b() always refer to not-expected-to-be-cheap stuff." I agree with that logic. It makes for a nice convention. (That convention is already broken by the built-in dup and sort, but let past mistakes be past mistakes.) My response to that is that concerns of keeping the language simple and of facilitating generic code are counterarguments that should be weighed in as well.

In what way does that convention/style go against facilitating generic code?

A generic function writing obj.symbol cannot work across objects that implement symbol as a computed property vs. a field. Andrei

I'm not following. If 'obj.symbol' is syntactically available and is valid code, shouldn't the generic function work ok regardless of whether 'symbol' is a computed property or a field?

What I meant is very simple. Generic code benefits from unified syntax. Going with one convention across the board would make generic code simpler. Going with the convention that expensive stuff is with "()" and cheap stuff is without "()" differentiates usage.
 I mean, what is even the difference from the status quo?

The discussion was about how things should behave in the future. If you ask me, I can live with the status quo save for the writeln = 3 which could become a poster child of belittling D. Andrei
Oct 02 2008
parent Bruno Medeiros <brunodomedeiros+spam com.gmail> writes:
Andrei Alexandrescu wrote:
 Bruno Medeiros wrote:
 Andrei Alexandrescu wrote:
 Bruno Medeiros wrote:
 Andrei Alexandrescu wrote:
 Bill Baxter wrote:
 On Tue, Sep 30, 2008 at 4:08 AM, KennyTM~ <kennytm gmail.com>
 wrote:
 I think the distinction of with and without () is pretty 
 stylistic, because the same argument can even be applied to
 operator overloading (does a=b means pointing the variable
 a to b, or calling a.opAssign(b)?)

 For me, I would use () if the function do modify the object
 itself.

I think the counter-argument to what I just said is that if properties had to be declared explicitly then at least we would know that .y is either a field or else something the designer *intended* to behave like a property. Which does tell us something. It most likely means that it's not an extremely heavy-weight operation, just a lightweight accessor with maybe a little logic on top. That could be wrong, but in that case it's the class designer's fault for misleading us.

So the logic is: "A field can be accessed with a.b, but not with a.b(). Therefore if someone writes a.b, it may be a field or code, but it someone writes a.b() it will always be code. Given that a field is cheap and code ranges from cheap to expensive, good style is to have a.b always refer to cheap stuff, and a.b() always refer to not-expected-to-be-cheap stuff." I agree with that logic. It makes for a nice convention. (That convention is already broken by the built-in dup and sort, but let past mistakes be past mistakes.) My response to that is that concerns of keeping the language simple and of facilitating generic code are counterarguments that should be weighed in as well.

In what way does that convention/style go against facilitating generic code?

A generic function writing obj.symbol cannot work across objects that implement symbol as a computed property vs. a field. Andrei

I'm not following. If 'obj.symbol' is syntactically available and is valid code, shouldn't the generic function work ok regardless of whether 'symbol' is a computed property or a field?

What I meant is very simple. Generic code benefits from unified syntax. Going with one convention across the board would make generic code simpler. Going with the convention that expensive stuff is with "()" and cheap stuff is without "()" differentiates usage.

Ah, got it now. I misunderstood the original "So the logic is:" post. But I don't think the idea (from Bill and others) was to omit "()" for all cheap stuff functions, but only for those explicitly made to behave as properties/fields. That way would not hurt generic code, it would be the same as the status quo in that regard. -- Bruno Medeiros - Software Developer, MSc. in CS/E graduate http://www.prowiki.org/wiki4d/wiki.cgi?BrunoMedeiros#D
Oct 03 2008
prev sibling parent reply Jason House <jason.james.house gmail.com> writes:
KennyTM~ Wrote:

 Bill Baxter wrote:
 On Tue, Sep 30, 2008 at 3:36 AM, Steven Schveighoffer
 <schveiguy yahoo.com> wrote:
 There is no ambiguity either case. You evaluate Stdout.newline. The
 evaluation yields a value of some type. Then you evaluate formatln against
 that value.

x.y.z(); Is y a property/field of x or a function call with no args? I see a benefit to being able to understand a line of code without requiring lots of extra context. I have to do less lookups of the source of a function or property.

The problem with this argument is that unless you disallow properties altogether, you still won't know whether y is actually a field or a call to a property method without looking it up. --bb

I think the distinction of with and without () is pretty stylistic, because the same argument can even be applied to operator overloading (does a=b means pointing the variable a to b, or calling a.opAssign(b)?) For me, I would use () if the function do modify the object itself.

I assume you'd also want parens if the function modified global state? If so, I believe you'd want parens with all impure member function calls. Obviously, we're talking about property getters and not setters. I was told once already that such things are off topic :) I think it's relevant to the spirit of the conversation.
Sep 29 2008
parent reply KennyTM~ <kennytm gmail.com> writes:
Jason House wrote:
 KennyTM~ Wrote:
 
 Bill Baxter wrote:
 On Tue, Sep 30, 2008 at 3:36 AM, Steven Schveighoffer
 <schveiguy yahoo.com> wrote:
 There is no ambiguity either case. You evaluate Stdout.newline. The
 evaluation yields a value of some type. Then you evaluate formatln against
 that value.

x.y.z(); Is y a property/field of x or a function call with no args? I see a benefit to being able to understand a line of code without requiring lots of extra context. I have to do less lookups of the source of a function or property.

altogether, you still won't know whether y is actually a field or a call to a property method without looking it up. --bb

because the same argument can even be applied to operator overloading (does a=b means pointing the variable a to b, or calling a.opAssign(b)?) For me, I would use () if the function do modify the object itself.

I assume you'd also want parens if the function modified global state? If so, I believe you'd want parens with all impure member function calls.

Right. Therefore if I were to implement property getters I would require it as an immutable (or at least const) method itself. I don't know what's the use of global functions that is pure AND does not take any input.
 Obviously, we're talking about property getters and not setters. I was told
once already that such things are off topic :) I think it's relevant to the
spirit of the conversation.

Sep 30 2008
parent reply Jason House <jason.james.house gmail.com> writes:
KennyTM~ Wrote:

 Jason House wrote:
 KennyTM~ Wrote:
 
 Bill Baxter wrote:
 On Tue, Sep 30, 2008 at 3:36 AM, Steven Schveighoffer
 <schveiguy yahoo.com> wrote:
 There is no ambiguity either case. You evaluate Stdout.newline. The
 evaluation yields a value of some type. Then you evaluate formatln against
 that value.

x.y.z(); Is y a property/field of x or a function call with no args? I see a benefit to being able to understand a line of code without requiring lots of extra context. I have to do less lookups of the source of a function or property.

altogether, you still won't know whether y is actually a field or a call to a property method without looking it up. --bb

because the same argument can even be applied to operator overloading (does a=b means pointing the variable a to b, or calling a.opAssign(b)?) For me, I would use () if the function do modify the object itself.

I assume you'd also want parens if the function modified global state? If so, I believe you'd want parens with all impure member function calls.

Right. Therefore if I were to implement property getters I would require it as an immutable (or at least const) method itself.

Const and immutable functions can modify global state... So I don't think you'd want that. PS: D's const functions don't guarantee they won't modify the object.
 I don't know what's the use of global functions that is pure AND does 
 not take any input.
 
 Obviously, we're talking about property getters and not setters. I was told
once already that such things are off topic :) I think it's relevant to the
spirit of the conversation.


Sep 30 2008
parent reply KennyTM~ <kennytm gmail.com> writes:
Jason House wrote:
 
 
 Const and immutable functions can modify global state... So I don't think
you'd want that.

Does "pure" in an object method takes the object itself as input also? If so, then I mean the parenthesis can be omitted if the method is pure. If not, then it has no solution in current D. The problem is I see no official examples in pure object methods. So far I can only find mentioning of pure global functions, or I've overlooked.
 
 PS: D's const functions don't guarantee they won't modify the object.
 

I still can't quite grasp the invariant-madness in D :p Practically I have only used the "in" storage class in parameter list*, invariant methods and .idup for some string-related manipulations. P.S. *: "in" was "invariant scope". When did it changed to "const scope"??
 
 
 
 I don't know what's the use of global functions that is pure AND does 
 not take any input.

 Obviously, we're talking about property getters and not setters. I was told
once already that such things are off topic :) I think it's relevant to the
spirit of the conversation.



Sep 30 2008
parent Sergey Gromov <snake.scaly gmail.com> writes:
Tue, 30 Sep 2008 23:58:14 +0800,
KennyTM~ wrote:
 P.S. *: "in" was "invariant scope". When did it changed to "const
 scope"??

I don't know whether it ever was invariant, but the current implementation is correct IMO: "in" contract means that you guarantee not to change the received object. Const enforces exactly this guarantee. Invariant is more restrictive in that it also requires a guarantee from the caller that the object will never change in the future, therefore disallowing mutable objects for "in" parameters. This contract doesn't quite associate with an "in" keyword to me.
Oct 01 2008
prev sibling next sibling parent reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
Steven Schveighoffer wrote:
 "Andrei Alexandrescu" wrote
 Steven Schveighoffer wrote:
 "Andrei Alexandrescu" wrote
 Steven Schveighoffer wrote:
 "Andrei Alexandrescu" wrote
 Steven Schveighoffer wrote:
 "Andrei Alexandrescu" wrote
 P.S. If src.next() is too lengthy, why not just adopt ++src?

may legitimately expect that to mean "increment all elements of src".

i.e. an assumption that .next without parens will not change anything on the object, yet in another case you believe people's assumptions are the main argument. This doesn't sound consistent.

consistency (albeit a tad far-fetched), the other has to do with being used to a mistaken decision in the C language design.

for all functions was a design mistake. I would argue that the design was on purpose and correctly served that purpose. The purpose was to remove ambiguity when faced with understanding code without all the context.


supposition) that the C designers made a mistake.

"One principle that I consider universal is that a language should minimize the number of syntactic constructs that are semantically and/or pragmatically meaningless. Another that I also consider universal is that the more frequently-used constructs should be given syntactic priority over the less-used constructs, particularly when the latter are also at risk of breaking the first principle."

Hidden in this statement is that you consider () to be semantically meaningless. I see it not as meaningless, but a distinction between what the symbol is supposed to represent. A property should represent a value. A function should represent an action.

Sheesh, do I really need to spoonfeed all of this? First I mentioned the principles I quoted above. Then I clearly explained how C's handling of function names breaks these principle, with examples. Please refer to that post. There is nothing hidden my statement.
 I'll also note that your basis/proof is not any more proof than mine ;)  You 
 just made statements about what you believe, as did I.

The note is wrong. I provide the principles as basis. Then I provide proof using that basis. If you want to invalidate that, you need to invalidate my basis, i.e., the principles of language design I am invoking. You are of course free to do that since principles of programming language design are to large extent a subjective matter.
 What is the basis of yours?

working at Bell labs when C was invented, in fact, I don't even think I was born ;) But it makes logical sense, and still does. If you want to call a function, you have to use parentheses. If you want to access the function address, no parentheses.

logical fallacy called proof by assertion.

Sorry, change my statement to 'But it makes sense and still does'. And I never said it was a proof. It's a statement of my reasons behind believing the () decision was on purpose.

The decision was on purpose, I am sure it wasn't an oversight. It was a poor decision because it breaks the aforementioned principles.
 It follows other design decisions they made:

 pointers:  Access what it's pointing to, use ->, get address it's 
 pointing to, use symbol alone.

address. You are referring the pointer, which is an address. It would have been consistent with other design decisions if referring a variable alone would take its address.

Not really. A function symbol by itself is the address of the function. A pointer symbol by itself is the address of the data it points to. It's the same. I don't think you understood the detail that I stated, 'get address it's pointing to', not 'get address of the pointer'.

I understood very well. Your point is off by a mile, and getting only farther. Please understand how you are making an elementary mistake. Consider: int x; Then if I only use "x", I am getting x's value (an lvalue in fact), not its address. Now consider: int * x; Then if I only use "x", I am getting x's value, which is the address of an int. In neither case is it possible for x and &x to mean the same thing. For functions some really weird stuff happens: // this is C #include <assert.h> void foo() {} int main() { void (*p1)() = foo; void (*p2)() = &foo; assert(p1 == p2); } So in fact accessing the name of the function and applying the address-of operator to it yield the same thing. That does not happen to pointers and anything else. Accessing any other symbol yields its value, not its address. I hope that clarifies things. Before anyone will point that out, I'll hurry to mention C does have one other instance in which a symbol "decays" to an address: names f fixed-sized arrays decay to the address of their first element. Still for them the address-of operator is not idempotent, and the decision to allow said decaying has good justification given by subtyping. Andrei
Sep 29 2008
next sibling parent reply "Steven Schveighoffer" <schveiguy yahoo.com> writes:
"Andrei Alexandrescu" wrote
 Steven Schveighoffer wrote:
 Hidden in this statement is that you consider () to be semantically 
 meaningless.  I see it not as meaningless, but a distinction between what 
 the symbol is supposed to represent.  A property should represent a 
 value. A function should represent an action.

Sheesh, do I really need to spoonfeed all of this? First I mentioned the principles I quoted above. Then I clearly explained how C's handling of function names breaks these principle, with examples. Please refer to that post. There is nothing hidden my statement.

Sorry, I look at things more simply, and I'm not really into language design theory ;) I didn't realize that calling something a principle meant that it is generally accepted to be fact, my apologies. So I should read your statement as: "a principle that is generally accepted to be universally true by major language design geeks, that I also agree with, is..." I'm not trying to be clever here, I really didn't understand that it was implied. So I agree with your principle, just not that () falls within the category of 'pragmatically or semantically meaningless.' I'd also disagree that the most frequently used construct of calling a function is without parentheses.
 I'll also note that your basis/proof is not any more proof than mine ;) 
 You just made statements about what you believe, as did I.

The note is wrong. I provide the principles as basis. Then I provide proof using that basis. If you want to invalidate that, you need to invalidate my basis, i.e., the principles of language design I am invoking. You are of course free to do that since principles of programming language design are to large extent a subjective matter.

Not really. You provided no proof that your principles apply to this debate, you just stated that they do.
 Sorry, change my statement to 'But it makes sense and still does'.  And I 
 never said it was a proof.  It's a statement of my reasons behind 
 believing the () decision was on purpose.

The decision was on purpose, I am sure it wasn't an oversight. It was a poor decision because it breaks the aforementioned principles.

Respectfully disagree.
 Not really.  A function symbol by itself is the address of the function. 
 A pointer symbol by itself is the address of the data it points to.  It's 
 the same.  I don't think you understood the detail that I stated, 'get 
 address it's pointing to', not 'get address of the pointer'.

I understood very well. Your point is off by a mile, and getting only farther. Please understand how you are making an elementary mistake. Consider: int x; Then if I only use "x", I am getting x's value (an lvalue in fact), not its address. Now consider: int * x; Then if I only use "x", I am getting x's value, which is the address of an int. In neither case is it possible for x and &x to mean the same thing. For functions some really weird stuff happens: // this is C #include <assert.h> void foo() {}

How does this make any difference to whether you can call a function without parentheses or not? You are arguing that for a function defined as foo(), foo; should mean call function foo, and I'm arguing that it should be a syntax error. Neither of us want the C behavior of evaluating to the function address. I think &foo should be the proper method of taking the address of a function, as it is in D. -Steve
Sep 29 2008
next sibling parent reply "Steven Schveighoffer" <schveiguy yahoo.com> writes:
"Steven Schveighoffer" wrote
 "Andrei Alexandrescu" wrote
 Not really.  A function symbol by itself is the address of the function. 
 A pointer symbol by itself is the address of the data it points to. 
 It's the same.  I don't think you understood the detail that I stated, 
 'get address it's pointing to', not 'get address of the pointer'.

I understood very well. Your point is off by a mile, and getting only farther. Please understand how you are making an elementary mistake. Consider: int x; Then if I only use "x", I am getting x's value (an lvalue in fact), not its address. Now consider: int * x; Then if I only use "x", I am getting x's value, which is the address of an int. In neither case is it possible for x and &x to mean the same thing. For functions some really weird stuff happens: // this is C #include <assert.h> void foo() {}

How does this make any difference to whether you can call a function without parentheses or not? You are arguing that for a function defined as foo(), foo; should mean call function foo, and I'm arguing that it should be a syntax error. Neither of us want the C behavior of evaluating to the function address. I think &foo should be the proper method of taking the address of a function, as it is in D.

This is what happens when you can't explain yourself correctly. Stop typing your vehement response about how I am an idiot right now :) I realize my mistake in the statements above. I think the problem you cited with how foo is equivalent to &foo in C is bad. I think we are getting sidetracked here, though. The point I really want to state is that foo; shouldn't mean calling a function named foo. I don't think it should mean the address of the function, although it seems consistent with C pointers (IMO, it seems like &foo shouldn't even compile in C if foo means 'address of foo' already). &foo sounds correct to me, "address of foo." Bottom line, I'm not defending the way C takes addresses of functions, but I think the requirement C has to use parentheses to call functions helps to clarify the intentions of the function author, and makes the code clearer. -Steve
Sep 29 2008
next sibling parent reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
Steven Schveighoffer wrote:
 "Steven Schveighoffer" wrote
 "Andrei Alexandrescu" wrote
 Not really.  A function symbol by itself is the address of the function. 
 A pointer symbol by itself is the address of the data it points to. 
 It's the same.  I don't think you understood the detail that I stated, 
 'get address it's pointing to', not 'get address of the pointer'.

farther. Please understand how you are making an elementary mistake. Consider: int x; Then if I only use "x", I am getting x's value (an lvalue in fact), not its address. Now consider: int * x; Then if I only use "x", I am getting x's value, which is the address of an int. In neither case is it possible for x and &x to mean the same thing. For functions some really weird stuff happens: // this is C #include <assert.h> void foo() {}

without parentheses or not? You are arguing that for a function defined as foo(), foo; should mean call function foo, and I'm arguing that it should be a syntax error. Neither of us want the C behavior of evaluating to the function address. I think &foo should be the proper method of taking the address of a function, as it is in D.

This is what happens when you can't explain yourself correctly. Stop typing your vehement response about how I am an idiot right now :) I realize my mistake in the statements above. I think the problem you cited with how foo is equivalent to &foo in C is bad. I think we are getting sidetracked here, though. The point I really want to state is that foo; shouldn't mean calling a function named foo.

"State" is very accurate, because aside from stating the point you bring nothing to support it.
 I 
 don't think it should mean the address of the function, although it seems 
 consistent with C pointers (IMO, it seems like &foo shouldn't even compile 
 in C if foo means 'address of foo' already).  &foo sounds correct to me, 
 "address of foo."

No. It does NOT seem and is NOT even remotely consistent with C pointers because x is not the same thing as &x for all symbols in C except functions. Please work through this with books and test programs and all, until you fully understand it. It is important.
 Bottom line, I'm not defending the way C takes addresses of functions, but I 
 think the requirement C has to use parentheses to call functions helps to 
 clarify the intentions of the function author, and makes the code clearer.

I agree, and I'd agree even more if you replaced "think" with "believe". I hope you also agree you have brought nothing of significance to support your thought/belief, while you have received plenty against. I also agree that Sergey, Bill, and others did bring up good points in support for the required trailing "()". It would be more fertile to continue discussing those. Andrei
Sep 29 2008
parent reply "Steven Schveighoffer" <schveiguy yahoo.com> writes:
"Andrei Alexandrescu" wrote
 Steven Schveighoffer wrote:
 "Steven Schveighoffer" wrote
 "Andrei Alexandrescu" wrote
 Not really.  A function symbol by itself is the address of the 
 function. A pointer symbol by itself is the address of the data it 
 points to. It's the same.  I don't think you understood the detail 
 that I stated, 'get address it's pointing to', not 'get address of the 
 pointer'.

farther. Please understand how you are making an elementary mistake. Consider: int x; Then if I only use "x", I am getting x's value (an lvalue in fact), not its address. Now consider: int * x; Then if I only use "x", I am getting x's value, which is the address of an int. In neither case is it possible for x and &x to mean the same thing. For functions some really weird stuff happens: // this is C #include <assert.h> void foo() {}

without parentheses or not? You are arguing that for a function defined as foo(), foo; should mean call function foo, and I'm arguing that it should be a syntax error. Neither of us want the C behavior of evaluating to the function address. I think &foo should be the proper method of taking the address of a function, as it is in D.

This is what happens when you can't explain yourself correctly. Stop typing your vehement response about how I am an idiot right now :) I realize my mistake in the statements above. I think the problem you cited with how foo is equivalent to &foo in C is bad. I think we are getting sidetracked here, though. The point I really want to state is that foo; shouldn't mean calling a function named foo.

"State" is very accurate, because aside from stating the point you bring nothing to support it.

Again with the 'nothing to support it'. If you're not going to listen to my arguments, or even acknowledge that I made them, then there really is no point to this discussion.
 I don't think it should mean the address of the function, although it 
 seems consistent with C pointers (IMO, it seems like &foo shouldn't even 
 compile in C if foo means 'address of foo' already).  &foo sounds correct 
 to me, "address of foo."

No. It does NOT seem and is NOT even remotely consistent with C pointers because x is not the same thing as &x for all symbols in C except functions. Please work through this with books and test programs and all, until you fully understand it. It is important.

You are completely misunderstanding the scope of what I'm saying. I'll just leave it at that.
 Bottom line, I'm not defending the way C takes addresses of functions, 
 but I think the requirement C has to use parentheses to call functions 
 helps to clarify the intentions of the function author, and makes the 
 code clearer.

I agree, and I'd agree even more if you replaced "think" with "believe".

Think and believe to me both mean 'This is true in my mind'. If you find one to be more appealing, so be it.
 I hope you also agree you have brought nothing of significance to support 
 your thought/belief, while you have received plenty against.

Again, you are either not paying enough attention, or you refuse to accept my arguments as valid arguments. I'll stop bashing my head against this brick wall of your inability to comprehend or refusal to accept my arguments, and just let this conversation die.
 I also agree that Sergey, Bill, and others did bring up good points in 
 support for the required trailing "()". It would be more fertile to 
 continue discussing those.

If the end result is that it gets fixed, I'm all for it. -Steve
Sep 29 2008
parent reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
Steven Schveighoffer wrote:
 "Andrei Alexandrescu" wrote
 No. It does NOT seem and is NOT even remotely consistent with C pointers 
 because x is not the same thing as &x for all symbols in C except 
 functions. Please work through this with books and test programs and all, 
 until you fully understand it. It is important.

You are completely misunderstanding the scope of what I'm saying. I'll just leave it at that.

You said something wrong. I understood what you said, I understood in what way it was wrong, and then I explained to you why it was wrong. How did I completely misunderstood the scope of what you are saying? In what way are pointers to functions consistent with other pointers in C, because in the way you mentioned, they aren't?
 Bottom line, I'm not defending the way C takes addresses of functions, 
 but I think the requirement C has to use parentheses to call functions 
 helps to clarify the intentions of the function author, and makes the 
 code clearer.


Think and believe to me both mean 'This is true in my mind'. If you find one to be more appealing, so be it.

Thinking evokes process. Someone starts from a belief A, does some thinking, and reaches B. Then A is a belief and B is a thought resulting from that belief. (Euclidean axioms -> thinking -> sum of angles in a triangle is 180.) A philosopher (http://en.wikipedia.org/wiki/Constantin_Noica) wrote a book on logic that clarifies this relationship beautifully. His thesis is that logic essentially does not bring anything new, it's just a rehash of the beliefs stated in the axioms. To him logic was in a sense "conservation" because it only shuffled facts, not discovering new ones. I _believe_ a language should obey the principles 1 and 2 (economy of syntax and giving syntactic priority to frequent use cases). Based on that belief, I _think_ D should drop the trailing parens. I agree that somebody could _believe_ in some other principles that replace or override mine and then _think_ that keeping trailing parens is a good thing. Sergey did just that by showing us some very good examples when absence of trailing parens leads to ambiguity. Then there is room for meaningful discussion. It is also possible that somebody simply _believes_ the trailing parens should be kept because they make code clearer to them. That's fair too, as long as there is understanding that that belief comes to a certain extent at odds with principles 1 and 2 (which anyone is free to simply not believe). The only place that needs work is when the connection between belief and thought is unclear, or when the belief is simply wrong. Andrei
Sep 29 2008
next sibling parent reply Bruno Medeiros <brunodomedeiros+spam com.gmail> writes:
Andrei Alexandrescu wrote:
 
 I _believe_ a language should obey the principles 1 and 2 (economy of 
 syntax and giving syntactic priority to frequent use cases). Based on 
 that belief, I _think_ D should drop the trailing parens.
 
 I agree that somebody could _believe_ in some other principles that 
 replace or override mine and then _think_ that keeping trailing parens 
 is a good thing. Sergey did just that by showing us some very good 
 examples when absence of trailing parens leads to ambiguity. Then there 
 is room for meaningful discussion.
 
 It is also possible that somebody simply _believes_ the trailing parens 
 should be kept because they make code clearer to them. That's fair too, 
 as long as there is understanding that that belief comes to a certain 
 extent at odds with principles 1 and 2 (which anyone is free to simply 
 not believe).
 
 The only place that needs work is when the connection between belief and 
 thought is unclear, or when the belief is simply wrong.
 
 
 Andrei

Good post. Sergey's argument (which is much more objective than subjective) is not strictly against omitting parenthesis, but rather against ambiguity (the dual possible ways to call zero-args functions), which as has been shown, creates various possibilities for bugs when you mix functions that return delegates. This ambiguity problem would also be resolved if omitting parenthesis would be *the only way* of calling functions with zero-args. Anyways, I also agree with principles 1 and 2. But I also agree with some other principles that sometimes conflict with those first ones (which is what is happening here). And in each such scenario I would have to weight in to see which principle I would give priority. These other principles are: 3 - Consistency of syntax - The same language feature should use the same syntax, with as little variations as possible. This principle fails here because when you have functions with parameters you have to use parenthesis, and only when you don't have arguments you omit the parenthesis. But the zero-args case should not be different from the n-args case. Of course, this could be solved by having a syntax for the n-args case that also doesn't use parenthesis (see my OP after Benji's thread) But most importantly: 4 - Common ground with other programming languages. There are several things that are common with many, if not all, of the mainstream programming languages (Java, C#, C/C++, Ruby, Python, etc.). One of them is using parenthesis for calling functions! (See KennyTM's post with the table describing function calling for several languages.) Using parenthesis comes from math. And of that list, only VB 6, Pascal and Perl, have the possibility of omitting parenthesis. And frankly, VB6 and Pascal don't matter since they are pretty much obsolete. And Perl is language with a hack-ish nature, IMO. Even Lisp uses parenthesis for function calling! (even if in a different position, and even if it's pretty much the only syntax it has). This is a subjective principle, I can't tell you to want to like having common ground with other languages, but c'mon... "writeln;"? Seriously?? That's just hideous. -- Bruno Medeiros - Software Developer, MSc. in CS/E graduate http://www.prowiki.org/wiki4d/wiki.cgi?BrunoMedeiros#D
Oct 02 2008
next sibling parent reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
Bruno Medeiros wrote:
 Sergey's argument (which is much more objective than subjective) is not 
 strictly against omitting parenthesis, but rather against ambiguity (the 
 dual possible ways to call zero-args functions), which as has been 
 shown, creates various possibilities for bugs when you mix functions 
 that return delegates. This ambiguity problem would also be resolved if 
 omitting parenthesis would be *the only way* of calling functions with 
 zero-args.

Yah, he made great points. There are ambiguities. The counter-argument is that that would make the syntax of calling functions with no arguments inconsistent with the syntax of calling function with arguments. The "()" is a degenerate case of "(a, b, c)". So it's hard to keep everybody happy.
 Anyways, I also agree with principles 1 and 2. But I also agree with 
 some other principles that sometimes conflict with those first ones 
 (which is what is happening here). And in each such scenario I would 
 have to weight in to see which principle I would give priority.

Yes, there are other principles to follow too. And somehow they often participate in a weighted average in many design decisions.
 These other principles are:
 
 3 - Consistency of syntax - The same language feature should use the 
 same syntax, with as little variations as possible.

 This principle fails here because when you have functions with 
 parameters you have to use parenthesis, and only when you don't have 
 arguments you omit the parenthesis. But the zero-args case should not be 
 different from the n-args case. Of course, this could be solved by 
 having a syntax for the n-args case that also doesn't use parenthesis 
 (see my OP after Benji's thread)

Great. Yes, that is consistency. I swear that all I wrote above was before I saw this :o).
 But most importantly:
 4 - Common ground with other programming languages. There are several 
 things that are common with many, if not all, of the mainstream 
 programming languages (Java, C#, C/C++, Ruby, Python, etc.).
 One of them is using parenthesis for calling functions! (See KennyTM's 
 post with the table describing function calling for several languages.) 
 Using parenthesis comes from math. And of that list, only VB 6, Pascal 
 and Perl, have the possibility of omitting parenthesis. And frankly, VB6 
 and Pascal don't matter since they are pretty much obsolete. And Perl is 
 language with a hack-ish nature, IMO. Even Lisp uses parenthesis for 
 function calling! (even if in a different position, and even if it's 
 pretty much the only syntax it has).
 This is a subjective principle, I can't tell you to want to like having 
 common ground with other languages, but c'mon... "writeln;"? Seriously?? 
 That's just hideous.

I agree that 4 is a good principle. I'd even call it "consistency" - across languages that is. But let's not forget that C# defined an entire feature - properties - to fulfill what was perceived as a need. Also the Cecil language goes as far as requiring accessors for any field (and defining methods implicitly when missing). Eiffel also has properties that are obtained via syntactic conventions (getX and setX implement property x). People want to add properties to Java 7 (unfortunately the google search is clogged by a Java class called Properties so it needs some combing). I use writeln; all over the place, and more importantly obj.method, without finding them hideous. I'm impressed a lot by the analogy with math because it is one extra argument in my favor. In math there's no function without arguments. At most people would say f(.) (with a central dot) when referring to what is akin to the address of the function. Andrei
Oct 02 2008
next sibling parent reply Bruno Medeiros <brunodomedeiros+spam com.gmail> writes:
Andrei Alexandrescu wrote:
 Bruno Medeiros wrote:
 Sergey's argument (which is much more objective than subjective) is 
 not strictly against omitting parenthesis, but rather against 
 ambiguity (the dual possible ways to call zero-args functions), which 
 as has been shown, creates various possibilities for bugs when you mix 
 functions that return delegates. This ambiguity problem would also be 
 resolved if omitting parenthesis would be *the only way* of calling 
 functions with zero-args.

Yah, he made great points. There are ambiguities. The counter-argument is that that would make the syntax of calling functions with no arguments inconsistent with the syntax of calling function with arguments. The "()" is a degenerate case of "(a, b, c)". So it's hard to keep everybody happy.

Yes, if we solved the ambiguity problem that way, we would make a lot of people unhappy (myself included). But if we solved the problem the *other way* (forbidding omittable parenthesis for normal zero-args function), it would make, that I know of, only one person unhappy (you). Note that this would not preclude that we implement another way to have properties (see below).
 
 These other principles are:

 3 - Consistency of syntax - The same language feature should use the 
 same syntax, with as little variations as possible.

 This principle fails here because when you have functions with 
 parameters you have to use parenthesis, and only when you don't have 
 arguments you omit the parenthesis. But the zero-args case should not 
 be different from the n-args case. Of course, this could be solved by 
 having a syntax for the n-args case that also doesn't use parenthesis 
 (see my OP after Benji's thread)

Great. Yes, that is consistency. I swear that all I wrote above was before I saw this :o).

Good, at least we agree on that after all.
 But most importantly:
 4 - Common ground with other programming languages. There are several 
 things that are common with many, if not all, of the mainstream 
 programming languages (Java, C#, C/C++, Ruby, Python, etc.).
 One of them is using parenthesis for calling functions! (See KennyTM's 
 post with the table describing function calling for several 
 languages.) Using parenthesis comes from math. And of that list, only 
 VB 6, Pascal and Perl, have the possibility of omitting parenthesis. 
 And frankly, VB6 and Pascal don't matter since they are pretty much 
 obsolete. And Perl is language with a hack-ish nature, IMO. Even Lisp 
 uses parenthesis for function calling! (even if in a different 
 position, and even if it's pretty much the only syntax it has).
 This is a subjective principle, I can't tell you to want to like 
 having common ground with other languages, but c'mon... "writeln;"? 
 Seriously?? That's just hideous.

I agree that 4 is a good principle. I'd even call it "consistency" - across languages that is. But let's not forget that C# defined an entire feature - properties - to fulfill what was perceived as a need. Also the Cecil language goes as far as requiring accessors for any field (and defining methods implicitly when missing). Eiffel also has properties that are obtained via syntactic conventions (getX and setX implement property x). People want to add properties to Java 7 (unfortunately the google search is clogged by a Java class called Properties so it needs some combing).

Yes, I was going to call it "consistency with other programming languages", but renamed it so as to avoid confusion. Anyways, we're not saying a properties feature is not useful. We're not saying to just remove the implicit property function call and be done with that. The suggestion is to *replace* the implicit property function call with some other way for implementing properties, so that we don't have the disadvantages of the implicit property function call.
 Eiffel also has properties
 that are obtained via syntactic conventions (getX and setX implement
 property x)

Hum, interesting, I just replied in another part of the thread with a suggestion that is just like that (and didn't know about it). -- Bruno Medeiros - Software Developer, MSc. in CS/E graduate http://www.prowiki.org/wiki4d/wiki.cgi?BrunoMedeiros#D
Oct 02 2008
next sibling parent reply "Steven Schveighoffer" <schveiguy yahoo.com> writes:
"Bruno Medeiros" wrote
 Andrei Alexandrescu wrote:
 Eiffel also has properties
 that are obtained via syntactic conventions (getX and setX implement
 property x)

Hum, interesting, I just replied in another part of the thread with a suggestion that is just like that (and didn't know about it).

In fact, C++.net did something similar in that if you defined a property x in C#, it really defined two hidden functions, get_x() and set_x() that could be called from C++. But the new C++.net is much better, you can actually use and define real properties. I consider this type of thing a hack, that probably shouldn't be used since we are talking about a developing language and not something that has decades of real-world use. I personally think it was one of those Microsoft marketing things where they made C++.net so it could interact with other languages, but made it so ugly that you perceived C# as being soo much better ;) But that's just the cynic in me. All that is needed is a keyword to signify that a function is really a property, kind of like 'pure', and enforce the rules that have been discussed. Since D2 is already getting new keywords and is almost backwards-incompatible with existing D1 code, I don't see this as a huge issue. The other option is to do something funky with the syntax, like C# does (note that get, set, and value are contextual keywords in C#, so their keyword status is not universal): int myproperty { get { return _myprivateproperty; } set { return _myprivateproperty = value; } } In light of some other posts about overloading the set operators, I think actually it might be nicer to define set as a contextual function inside a property: int myproperty { get { return _myprivateproperty; } set(int value) { return _myprivateproperty = value; } set(string value) { return _myprivateproperty = to!(int)(value); } } I'm not a language designer, so I don't know if this falls within the rules of D grammar, but I definitely think this is one of the gems of C# syntax (note, no idea if C# invented this, but it's the first time I saw it). The downside is that you can't really take addresses of the set/get functions, or could you with something like &myproperty.set? That would be one reason to make the set/get symbols keywords. -Steve
Oct 02 2008
parent Bruno Medeiros <brunodomedeiros+spam com.gmail> writes:
Steven Schveighoffer wrote:
 "Bruno Medeiros" wrote
 Andrei Alexandrescu wrote:
 Eiffel also has properties
 that are obtained via syntactic conventions (getX and setX implement
 property x)

suggestion that is just like that (and didn't know about it).

In fact, C++.net did something similar in that if you defined a property x in C#, it really defined two hidden functions, get_x() and set_x() that could be called from C++. But the new C++.net is much better, you can actually use and define real properties. I consider this type of thing a hack, that probably shouldn't be used since we are talking about a developing language and not something that has decades of real-world use. I personally think it was one of those Microsoft marketing things where they made C++.net so it could interact with other languages, but made it so ugly that you perceived C# as being soo much better ;) But that's just the cynic in me. All that is needed is a keyword to signify that a function is really a property, kind of like 'pure', and enforce the rules that have been discussed. Since D2 is already getting new keywords and is almost backwards-incompatible with existing D1 code, I don't see this as a huge issue.

Just for the record, I didn't present that suggestion because it didn't require a new keyword. I think that's a very weak concern, and as I've said before, I find Walter's current obsession with keyword economy outdated and silly (example: "enum", among others). -- Bruno Medeiros - Software Developer, MSc. in CS/E graduate http://www.prowiki.org/wiki4d/wiki.cgi?BrunoMedeiros#D
Oct 03 2008
prev sibling next sibling parent reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
Bruno Medeiros wrote:
 Andrei Alexandrescu wrote:
 Bruno Medeiros wrote:
 Sergey's argument (which is much more objective than subjective) is 
 not strictly against omitting parenthesis, but rather against 
 ambiguity (the dual possible ways to call zero-args functions), which 
 as has been shown, creates various possibilities for bugs when you 
 mix functions that return delegates. This ambiguity problem would 
 also be resolved if omitting parenthesis would be *the only way* of 
 calling functions with zero-args.

Yah, he made great points. There are ambiguities. The counter-argument is that that would make the syntax of calling functions with no arguments inconsistent with the syntax of calling function with arguments. The "()" is a degenerate case of "(a, b, c)". So it's hard to keep everybody happy.

Yes, if we solved the ambiguity problem that way, we would make a lot of people unhappy (myself included). But if we solved the problem the *other way* (forbidding omittable parenthesis for normal zero-args function), it would make, that I know of, only one person unhappy (you).

So there is a cabal. I knew it! :o) I'm not that sure about "lot" and "one", and numbers-based arguments aren't that strong to begin with. Language design is complex and full of tradeoffs. In each decision there are consequences. If we require "()" then we must come with a separate thing for properties - an addition to the language. Then many people (maybe even including some the "lot" of people you mentioned) will cry foul: "where's my simple language?" etc. Maybe some people will say, hey, you were already there with the omittable "()" and the "=" thing, why didn't you just make the latter less trigger happy and we'd be home free? One thing that often is a casualty of these discussion is keeping the eye on the big picture. Andrei
Oct 02 2008
next sibling parent reply Fawzi Mohamed <fmohamed mac.com> writes:
Here comes another person that would be unhappy without omittable parenthesis.

Having properties is a very useful thing, one should be able to make a 
public field of a structure private and controlling access via methods 
with minimal or no changes to the external code.

This is a very nice feature to have.
D reaches this with a minimal change to the language:
optional parenthesis, and setter functions.

It might take a little to get used coming from other languages, but it 
works well.

It does have some limitation (no +=,-=,...) but is very compact, and 
simple to explain.

I like it very much.

Removing it would be very bad.
The other option is to replace it with a full fledged property mechanism.
This might overcome some of the limitations of the current approach, 
but would it be economical?
A language should not be perfect in all things, compactness is also an 
advantage.

Fawzi

On 2008-10-02 19:26:55 +0200, Andrei Alexandrescu 
<SeeWebsiteForEmail erdani.org> said:

 Bruno Medeiros wrote:
 Andrei Alexandrescu wrote:
 Bruno Medeiros wrote:
 Sergey's argument (which is much more objective than subjective) is not 
 strictly against omitting parenthesis, but rather against ambiguity 
 (the dual possible ways to call zero-args functions), which as has been 
 shown, creates various possibilities for bugs when you mix functions 
 that return delegates. This ambiguity problem would also be resolved if 
 omitting parenthesis would be *the only way* of calling functions with 
 zero-args.

Yah, he made great points. There are ambiguities. The counter-argument is that that would make the syntax of calling functions with no arguments inconsistent with the syntax of calling function with arguments. The "()" is a degenerate case of "(a, b, c)". So it's hard to keep everybody happy.

Yes, if we solved the ambiguity problem that way, we would make a lot of people unhappy (myself included). But if we solved the problem the *other way* (forbidding omittable parenthesis for normal zero-args function), it would make, that I know of, only one person unhappy (you).

So there is a cabal. I knew it! :o) I'm not that sure about "lot" and "one", and numbers-based arguments aren't that strong to begin with. Language design is complex and full of tradeoffs. In each decision there are consequences. If we require "()" then we must come with a separate thing for properties - an addition to the language. Then many people (maybe even including some the "lot" of people you mentioned) will cry foul: "where's my simple language?" etc. Maybe some people will say, hey, you were already there with the omittable "()" and the "=" thing, why didn't you just make the latter less trigger happy and we'd be home free? One thing that often is a casualty of these discussion is keeping the eye on the big picture. Andrei

Oct 02 2008
next sibling parent reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
Bill Baxter wrote:
 On Fri, Oct 3, 2008 at 3:16 AM, Fawzi Mohamed <fmohamed mac.com> wrote:
 
 This is a very nice feature to have.
 D reaches this with a minimal change to the language:
 optional parenthesis, and setter functions.

 ...
 It does have some limitation (no +=,-=,...) but is very compact, and simple
 to explain.

Fortunately this is an orthogonal issue. This could (and should IMO) be fixed independent of the current discussion.

Yah, overloaded ops are due for an overhaul. I'm almost afraid to ask... any ideas? :o) One goal is to fix opIndexAssign and make it work similar to the way it works in arrays, e.g. a[b] += c. Indexing into hash tables is a good test bed. Andrei
Oct 02 2008
next sibling parent reply Sergey Gromov <snake.scaly gmail.com> writes:
Thu, 02 Oct 2008 15:03:42 -0500,
Andrei Alexandrescu wrote:
 Yah, overloaded ops are due for an overhaul. I'm almost afraid to ask... 
 any ideas? :o)
 
 One goal is to fix opIndexAssign and make it work similar to the way it 
 works in arrays, e.g. a[b] += c. Indexing into hash tables is a good 
 test bed.

What's wrong with a.opIndexAssign(b, a.opIndex(b) + c)?
Oct 02 2008
next sibling parent reply Sergey Gromov <snake.scaly gmail.com> writes:
Fri, 3 Oct 2008 09:37:46 +0900,
Bill Baxter wrote:
 On Fri, Oct 3, 2008 at 9:32 AM, Bill Baxter <wbaxter gmail.com> wrote:
 On Fri, Oct 3, 2008 at 8:04 AM, Sergey Gromov <snake.scaly gmail.com> wrote:
 Thu, 02 Oct 2008 15:03:42 -0500,
 Andrei Alexandrescu wrote:
 Yah, overloaded ops are due for an overhaul. I'm almost afraid to ask...
 any ideas? :o)

 One goal is to fix opIndexAssign and make it work similar to the way it
 works in arrays, e.g. a[b] += c. Indexing into hash tables is a good
 test bed.

What's wrong with a.opIndexAssign(b, a.opIndex(b) + c)?

Indeed. I thought there wasn't a lot of debate needed on this, just action.

... except these extras do have the same issue that plain property assignment does. They would open up a new class of things that are valid code but don't behave as expected. writefln += 5. And also, a[b] += c should probably be rewritten as "a.opIndex(b) += c" *if* a returns a reference of some sort. Ok, so maybe there is a little to talk about. :-)

I think that any expression "a = b" should first check whether the expression on the left is an lvalue. If yes then use it as any other lvalue, otherwise try to re-write an expression using op Assign. This works in many scenarios, like opIndex returning a reference.
Oct 02 2008
parent reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
Sergey Gromov wrote:
 Fri, 3 Oct 2008 09:37:46 +0900,
 Bill Baxter wrote:
 On Fri, Oct 3, 2008 at 9:32 AM, Bill Baxter <wbaxter gmail.com> wrote:
 On Fri, Oct 3, 2008 at 8:04 AM, Sergey Gromov <snake.scaly gmail.com> wrote:
 Thu, 02 Oct 2008 15:03:42 -0500,
 Andrei Alexandrescu wrote:
 Yah, overloaded ops are due for an overhaul. I'm almost afraid to ask...
 any ideas? :o)

 One goal is to fix opIndexAssign and make it work similar to the way it
 works in arrays, e.g. a[b] += c. Indexing into hash tables is a good
 test bed.



... except these extras do have the same issue that plain property assignment does. They would open up a new class of things that are valid code but don't behave as expected. writefln += 5. And also, a[b] += c should probably be rewritten as "a.opIndex(b) += c" *if* a returns a reference of some sort. Ok, so maybe there is a little to talk about. :-)

I think that any expression "a = b" should first check whether the expression on the left is an lvalue. If yes then use it as any other lvalue, otherwise try to re-write an expression using op Assign. This works in many scenarios, like opIndex returning a reference.

That's a good rule, but a[b] = c on a hash is not helped by it. Andrei
Oct 03 2008
parent reply "Steven Schveighoffer" <schveiguy yahoo.com> writes:
"Andrei Alexandrescu" wrote
 Sergey Gromov wrote:
 Fri, 3 Oct 2008 09:37:46 +0900,
 Bill Baxter wrote:
 On Fri, Oct 3, 2008 at 9:32 AM, Bill Baxter <wbaxter gmail.com> wrote:
 On Fri, Oct 3, 2008 at 8:04 AM, Sergey Gromov <snake.scaly gmail.com> 
 wrote:
 Thu, 02 Oct 2008 15:03:42 -0500,
 Andrei Alexandrescu wrote:
 Yah, overloaded ops are due for an overhaul. I'm almost afraid to 
 ask...
 any ideas? :o)

 One goal is to fix opIndexAssign and make it work similar to the way 
 it
 works in arrays, e.g. a[b] += c. Indexing into hash tables is a good
 test bed.


action.

... except these extras do have the same issue that plain property assignment does. They would open up a new class of things that are valid code but don't behave as expected. writefln += 5. And also, a[b] += c should probably be rewritten as "a.opIndex(b) += c" *if* a returns a reference of some sort. Ok, so maybe there is a little to talk about. :-)

I think that any expression "a = b" should first check whether the expression on the left is an lvalue. If yes then use it as any other lvalue, otherwise try to re-write an expression using op Assign. This works in many scenarios, like opIndex returning a reference.

That's a good rule, but a[b] = c on a hash is not helped by it.

I think Sergey didn't write his rules correctly. If a is an lvalue, use it as any other lvalue, which *includes* trying op Assign. If a is not an lvalue, then rewrite the expression as a = a b, without expanding operators, then use normal operator expansion to compile the statement. If neither works, then it is a syntax error. This should be valid for all cases. For example a hash that returns a reference for opIndex: a[b] += c; a.opIndex(b) is an lvalue, so try: a.opIndex(b).opAddAssign(c). If that doesn't work, then syntax error. case 2, a[b] is an rvalue: rewrite as a[b] = a[b] + c; expand operators: a.opIndexAssign(a[b] + c, b); a.opIndexAssign(a.opIndex(b) + c, b); An example of a failure: int foo(int x) {...} foo(5) += 6; left side is an rvalue, so try rewriting: foo(5) = foo(5) + 6; syntax error, foo(5) is still an rvalue Any cases that break with these rules? -Steve
Oct 03 2008
parent "Steven Schveighoffer" <schveiguy yahoo.com> writes:
"Steven Schveighoffer" wrote
 "Andrei Alexandrescu" wrote
 Sergey Gromov wrote:
 Fri, 3 Oct 2008 09:37:46 +0900,
 Bill Baxter wrote:
 On Fri, Oct 3, 2008 at 9:32 AM, Bill Baxter <wbaxter gmail.com> wrote:
 On Fri, Oct 3, 2008 at 8:04 AM, Sergey Gromov <snake.scaly gmail.com> 
 wrote:
 Thu, 02 Oct 2008 15:03:42 -0500,
 Andrei Alexandrescu wrote:
 Yah, overloaded ops are due for an overhaul. I'm almost afraid to 
 ask...
 any ideas? :o)

 One goal is to fix opIndexAssign and make it work similar to the way 
 it
 works in arrays, e.g. a[b] += c. Indexing into hash tables is a good
 test bed.


action.

... except these extras do have the same issue that plain property assignment does. They would open up a new class of things that are valid code but don't behave as expected. writefln += 5. And also, a[b] += c should probably be rewritten as "a.opIndex(b) += c" *if* a returns a reference of some sort. Ok, so maybe there is a little to talk about. :-)

I think that any expression "a = b" should first check whether the expression on the left is an lvalue. If yes then use it as any other lvalue, otherwise try to re-write an expression using op Assign. This works in many scenarios, like opIndex returning a reference.

That's a good rule, but a[b] = c on a hash is not helped by it.

I think Sergey didn't write his rules correctly. If a is an lvalue, use it as any other lvalue, which *includes* trying op Assign. If a is not an lvalue, then rewrite the expression as a = a b, without expanding operators, then use normal operator expansion to compile the statement. If neither works, then it is a syntax error. This should be valid for all cases. For example a hash that returns a reference for opIndex: a[b] += c; a.opIndex(b) is an lvalue, so try: a.opIndex(b).opAddAssign(c). If that doesn't work, then syntax error. case 2, a[b] is an rvalue: rewrite as a[b] = a[b] + c; expand operators: a.opIndexAssign(a[b] + c, b); a.opIndexAssign(a.opIndex(b) + c, b); An example of a failure: int foo(int x) {...} foo(5) += 6; left side is an rvalue, so try rewriting: foo(5) = foo(5) + 6; syntax error, foo(5) is still an rvalue Any cases that break with these rules?

After thinking about this some more, I don't even thing the rvalue/lvalue check is necessary. Just try: a.op Assign(b); If that doesn't compile under the current rules, try rewriting as: a = a b; If that doesn't compile under the current rules, then it's a syntax error. -Steve
Oct 03 2008
prev sibling next sibling parent Christopher Wright <dhasenan gmail.com> writes:
Bill Baxter wrote:
 On Fri, Oct 3, 2008 at 9:37 AM, Bill Baxter <wbaxter gmail.com> wrote:
 ... except these extras do have the same issue that plain property
 assignment does.  They would open up a new class of things that are
 valid code but don't behave as expected.  writefln += 5.

Doh, obviously writefln is a bad example there. Imagine a function that takes an int, tries to do something that many times, then returns an int indicating how many times it actually managed to do it. --bb

How about this? Start trusting programmers not to do nonsensical things. With the possible exception of downs, whose code is delightfully opaque while being wonderfully readable.
Oct 02 2008
prev sibling next sibling parent reply KennyTM~ <kennytm gmail.com> writes:
Sergey Gromov wrote:
 Thu, 02 Oct 2008 15:03:42 -0500,
 Andrei Alexandrescu wrote:
 Yah, overloaded ops are due for an overhaul. I'm almost afraid to ask... 
 any ideas? :o)

 One goal is to fix opIndexAssign and make it work similar to the way it 
 works in arrays, e.g. a[b] += c. Indexing into hash tables is a good 
 test bed.

What's wrong with a.opIndexAssign(b, a.opIndex(b) + c)?

Probably performance. Consider seeking to the end of a 100M-node single-linked list, and increase its content by 1. But I agree that if something like .opIndexAddAssign() is not defined, the compiler should fall back to use a.opIndexAssign(b, a.opIndex(b)+c). (The same idea can be extended to properties and .opSlice() )
Oct 03 2008
next sibling parent reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
KennyTM~ wrote:
 Sergey Gromov wrote:
 Thu, 02 Oct 2008 15:03:42 -0500,
 Andrei Alexandrescu wrote:
 Yah, overloaded ops are due for an overhaul. I'm almost afraid to 
 ask... any ideas? :o)

 One goal is to fix opIndexAssign and make it work similar to the way 
 it works in arrays, e.g. a[b] += c. Indexing into hash tables is a 
 good test bed.

What's wrong with a.opIndexAssign(b, a.opIndex(b) + c)?

Probably performance. Consider seeking to the end of a 100M-node single-linked list, and increase its content by 1. But I agree that if something like .opIndexAddAssign() is not defined, the compiler should fall back to use a.opIndexAssign(b, a.opIndex(b)+c). (The same idea can be extended to properties and .opSlice() )

Glad you brought opIndexAddAssign up. I think a good solution would avoid adding all opIndexXxxAssign functions. I think even opXxxAssign are undesirable. Andrei
Oct 03 2008
parent reply KennyTM~ <kennytm gmail.com> writes:
Andrei Alexandrescu wrote:
 KennyTM~ wrote:
 Sergey Gromov wrote:
 Thu, 02 Oct 2008 15:03:42 -0500,
 Andrei Alexandrescu wrote:
 Yah, overloaded ops are due for an overhaul. I'm almost afraid to 
 ask... any ideas? :o)

 One goal is to fix opIndexAssign and make it work similar to the way 
 it works in arrays, e.g. a[b] += c. Indexing into hash tables is a 
 good test bed.

What's wrong with a.opIndexAssign(b, a.opIndex(b) + c)?

Probably performance. Consider seeking to the end of a 100M-node single-linked list, and increase its content by 1. But I agree that if something like .opIndexAddAssign() is not defined, the compiler should fall back to use a.opIndexAssign(b, a.opIndex(b)+c). (The same idea can be extended to properties and .opSlice() )

Glad you brought opIndexAddAssign up. I think a good solution would avoid adding all opIndexXxxAssign functions. I think even opXxxAssign are undesirable. Andrei

Of course I don't want opIndexAddAssign either ;p.
Oct 03 2008
parent Christopher Wright <dhasenan gmail.com> writes:
KennyTM~ wrote:
 Andrei Alexandrescu wrote:
 KennyTM~ wrote:
 Sergey Gromov wrote:
 Thu, 02 Oct 2008 15:03:42 -0500,
 Andrei Alexandrescu wrote:
 Yah, overloaded ops are due for an overhaul. I'm almost afraid to 
 ask... any ideas? :o)

 One goal is to fix opIndexAssign and make it work similar to the 
 way it works in arrays, e.g. a[b] += c. Indexing into hash tables 
 is a good test bed.

What's wrong with a.opIndexAssign(b, a.opIndex(b) + c)?

Probably performance. Consider seeking to the end of a 100M-node single-linked list, and increase its content by 1. But I agree that if something like .opIndexAddAssign() is not defined, the compiler should fall back to use a.opIndexAssign(b, a.opIndex(b)+c). (The same idea can be extended to properties and .opSlice() )

Glad you brought opIndexAddAssign up. I think a good solution would avoid adding all opIndexXxxAssign functions. I think even opXxxAssign are undesirable. Andrei

Of course I don't want opIndexAddAssign either ;p.

That would be hell on generic programming, too. These would only be useful for collections, which are usually templated for the element type. Then your collection class would look like: static if (SupportsAddition!(T)) { void opIndexAddAssign(T value) { ... } } And then I define a class: class BigInt { void opAddAssign (long i) { ... } } And it all goes to hell. That means you have to do: mixin (AdditionSupport!(T)()); string AdditionSupport(T)() { foreach (method; __traits(getVirtualMethods, T, "opAddAssign")) { // codegen here } } And that only works because of a bug in __traits(getVirtualMethods) that returns final methods. opIndex*Assign is just an unreasonable solution for generic programming.
Oct 04 2008
prev sibling next sibling parent reply Sergey Gromov <snake.scaly gmail.com> writes:
Fri, 03 Oct 2008 20:59:54 +0800,
KennyTM~ wrote:
 Sergey Gromov wrote:
 What's wrong with a.opIndexAssign(b, a.opIndex(b) + c)?

Probably performance. Consider seeking to the end of a 100M-node single-linked list, and increase its content by 1. But I agree that if something like .opIndexAddAssign() is not defined, the compiler should fall back to use a.opIndexAssign(b, a.opIndex(b)+c). (The same idea can be extended to properties and .opSlice() )

No, if you want performance in this particular case you define ref int opIndex() because I think whenever compiler encounters a[x]++ it should first test whether a[x] is an lvalue and if yes use it accordingly. And only if it is not it should fall back to .opIndexAddButProbablyNotAssign() special overloads.
Oct 03 2008
parent Sergey Gromov <snake.scaly gmail.com> writes:
Sat, 4 Oct 2008 04:34:46 +0900,
Bill Baxter wrote:
 On Fri, Oct 3, 2008 at 11:43 PM, Sergey Gromov <snake.scaly gmail.com> wrote:
 Fri, 03 Oct 2008 20:59:54 +0800,
 KennyTM~ wrote:
 Sergey Gromov wrote:
 What's wrong with a.opIndexAssign(b, a.opIndex(b) + c)?

Probably performance. Consider seeking to the end of a 100M-node single-linked list, and increase its content by 1. But I agree that if something like .opIndexAddAssign() is not defined, the compiler should fall back to use a.opIndexAssign(b, a.opIndex(b)+c). (The same idea can be extended to properties and .opSlice() )

No, if you want performance in this particular case you define ref int opIndex() because I think whenever compiler encounters a[x]++ it should first test whether a[x] is an lvalue and if yes use it accordingly. And only if it is not it should fall back to .opIndexAddButProbablyNotAssign() special overloads.

Yeh, but then you lose encapsulation. That's fine for some cases, like a vector, but in other cases it can be very handy to be able to get a notification any time one of your values changes. You lose that ability once you start handing out pointers to whoever asks for one.

Your two last posts are very close to what I have on my mind myself, and I agree with your concerns. The 'notification' thing is exactly what's missing. My proposal is quite flexible overall in that opIndex or opIndexLvalue can return a struct implementing all and every op for the underlying type. But it's tedious, bug-prone and not forward-compatible. What is needed is an ability to easily wrap one type with another, exposing the underlying type's functionality as much as possible, but still receiving notifications whenever the underlying object is updated. Now that I write this it doesn't look as smooth as I thought. But anyway, here's how it works: struct TypeWrapper(T) { private T contents; ref T opRef() { return contents; } void postModify() {...} } Here opIndex of your sparse matrix returns this wrapper. The wrapper knows whether it wraps a real element or a zero. It can detect that a real element became zero and remove it from matrix. The opRef works exactly like opCast right now in that it exposes exactly one underlying type. But I hope we'll get a polymorphic opCast at some point so I'm using a different name for this. The postModify is a weird callback which is called after the value received via opRef is modified. Your delegate idea is great in that all the op Assign can be replaced with opModify(T delegate(T) modify), giving you the ultimate notification. Even more than that, the minimal type wrapper could look like this: struct TypeWrapper(T) { private T value; T opCast() { return value; } T opAssign(T v) { value = v; return value; } T opModify(T delegate(T v) mod) { value = mod(value); return value; } T opProduct(T delegate(T v) prod) { return prod(value); } } where opCast is for rvalue usage, opAssign is for pure '=', opModify is for ' =' and opProduct is for any regular unary or binary operators.
Oct 03 2008
prev sibling parent reply Charles Hixson <charleshixsn earthlink.net> writes:
KennyTM~ wrote:
 Sergey Gromov wrote:
 Thu, 02 Oct 2008 15:03:42 -0500,
 Andrei Alexandrescu wrote:
 Yah, overloaded ops are due for an overhaul. I'm almost afraid to 
 ask... any ideas? :o)

 One goal is to fix opIndexAssign and make it work similar to the way 
 it works in arrays, e.g. a[b] += c. Indexing into hash tables is a 
 good test bed.

What's wrong with a.opIndexAssign(b, a.opIndex(b) + c)?

Probably performance. Consider seeking to the end of a 100M-node single-linked list, and increase its content by 1. But I agree that if something like .opIndexAddAssign() is not defined, the compiler should fall back to use a.opIndexAssign(b, a.opIndex(b)+c). (The same idea can be extended to properties and .opSlice() )

a.opIndexAssign(b, a.opIndex(b) + c) , then actual implementation for performance can be considered a compiler optimization feature. Just because one piece of code means the same thing as another doesn't mean it has to be implemented the same way, or can't be optimized.
Oct 03 2008
parent Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
Charles Hixson wrote:
 KennyTM~ wrote:
 Sergey Gromov wrote:
 Thu, 02 Oct 2008 15:03:42 -0500,
 Andrei Alexandrescu wrote:
 Yah, overloaded ops are due for an overhaul. I'm almost afraid to 
 ask... any ideas? :o)

 One goal is to fix opIndexAssign and make it work similar to the way 
 it works in arrays, e.g. a[b] += c. Indexing into hash tables is a 
 good test bed.

What's wrong with a.opIndexAssign(b, a.opIndex(b) + c)?

Probably performance. Consider seeking to the end of a 100M-node single-linked list, and increase its content by 1. But I agree that if something like .opIndexAddAssign() is not defined, the compiler should fall back to use a.opIndexAssign(b, a.opIndex(b)+c). (The same idea can be extended to properties and .opSlice() )

a.opIndexAssign(b, a.opIndex(b) + c) , then actual implementation for performance can be considered a compiler optimization feature. Just because one piece of code means the same thing as another doesn't mean it has to be implemented the same way, or can't be optimized.

That's a good point. Compiler magic can make sure stuff like that gets optimized, but user-defined object cannot benefit of such equivalence. Andrei
Oct 03 2008
prev sibling parent reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
Sergey Gromov wrote:
 Thu, 02 Oct 2008 15:03:42 -0500,
 Andrei Alexandrescu wrote:
 Yah, overloaded ops are due for an overhaul. I'm almost afraid to ask... 
 any ideas? :o)

 One goal is to fix opIndexAssign and make it work similar to the way it 
 works in arrays, e.g. a[b] += c. Indexing into hash tables is a good 
 test bed.

What's wrong with a.opIndexAssign(b, a.opIndex(b) + c)?

One problem is that for a hashtable that does not have b yet, opIndex will throw an exception. Another problem (assuming the above is fixed) is that b will be looked up twice in the hash. Andrei
Oct 03 2008
next sibling parent reply Sergey Gromov <snake.scaly gmail.com> writes:
Fri, 03 Oct 2008 09:02:07 -0500,
Andrei Alexandrescu wrote:
 Sergey Gromov wrote:
 Thu, 02 Oct 2008 15:03:42 -0500,
 Andrei Alexandrescu wrote:
 Yah, overloaded ops are due for an overhaul. I'm almost afraid to ask... 
 any ideas? :o)

 One goal is to fix opIndexAssign and make it work similar to the way it 
 works in arrays, e.g. a[b] += c. Indexing into hash tables is a good 
 test bed.

What's wrong with a.opIndexAssign(b, a.opIndex(b) + c)?

One problem is that for a hashtable that does not have b yet, opIndex will throw an exception. Another problem (assuming the above is fixed) is that b will be looked up twice in the hash.

The latter is not a problem if you always try to use a[b] as an lvalue before trying anything more specific. Though it makes the former even harder to fix. Probably there should be another opIndex for a context where the user expects a non-existent element to be created: ref T opIndexCreate(size_t i) When compiler sees "a[b] += c" it first calculates the type of "a[b]" in an assignment context. In case of indexing it means considering a.opIndexCreate, then a.opIndex, and finally the built-in indexing. Then it checks whether that type is an lvalue or implements the requested assignment operation. If neither is true then compiler falls back to a.opIndexAssign(b, ...) as a special backward-compatibility case.
Oct 03 2008
parent Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
Sergey Gromov wrote:
 Fri, 03 Oct 2008 09:02:07 -0500,
 Andrei Alexandrescu wrote:
 Sergey Gromov wrote:
 Thu, 02 Oct 2008 15:03:42 -0500,
 Andrei Alexandrescu wrote:
 Yah, overloaded ops are due for an overhaul. I'm almost afraid to ask... 
 any ideas? :o)

 One goal is to fix opIndexAssign and make it work similar to the way it 
 works in arrays, e.g. a[b] += c. Indexing into hash tables is a good 
 test bed.


will throw an exception. Another problem (assuming the above is fixed) is that b will be looked up twice in the hash.

The latter is not a problem if you always try to use a[b] as an lvalue before trying anything more specific. Though it makes the former even harder to fix. Probably there should be another opIndex for a context where the user expects a non-existent element to be created: ref T opIndexCreate(size_t i) When compiler sees "a[b] += c" it first calculates the type of "a[b]" in an assignment context. In case of indexing it means considering a.opIndexCreate, then a.opIndex, and finally the built-in indexing. Then it checks whether that type is an lvalue or implements the requested assignment operation. If neither is true then compiler falls back to a.opIndexAssign(b, ...) as a special backward-compatibility case.

Yah, Walter, Bartosz and I discussed this (under the name opIndexLvalue). It does have a problem with sparse arrays. In a sparse array, a[b] = 0 means the array should actually erase slot at position b (or not insert it if it was missing in the first place). This suggests that a more flexible mechanism would be to have a place where the container, the index, and the assigned value are all available to the same function. Andrei
Oct 03 2008
prev sibling next sibling parent Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
Bill Baxter wrote:
 On Fri, Oct 3, 2008 at 11:02 PM, Andrei Alexandrescu
 <SeeWebsiteForEmail erdani.org> wrote:
 Sergey Gromov wrote:
 Thu, 02 Oct 2008 15:03:42 -0500,
 Andrei Alexandrescu wrote:
 Yah, overloaded ops are due for an overhaul. I'm almost afraid to ask...
 any ideas? :o)

 One goal is to fix opIndexAssign and make it work similar to the way it
 works in arrays, e.g. a[b] += c. Indexing into hash tables is a good test
 bed.


throw an exception.

I don't see why you expect a[b] += c to work on a key for which a[b] is undefined. If it's undefined how can you increment it?

Because accessing it as an rvalue is different than accessing it as an lvalue. You just wrote a very sensible post in the same vein!
 Unless you've defined your accessor for undefined keys to return some
 other value.  And if it returns some other value then Sergey's rule is
 fine.

Yah, in that case things would work. I wouldn't dislike it, but I think Walter won't want to make that change.
 Sparse matrices are a good example of a hash-like data-structure that
 should return a default value (namely zero) for unset keys.  For such
 a sparse matrix      a.opIndexAssign(b, a.opIndex(b) + c)   will work
 fine.

Glad you brought sparse arrays up. Yah, it would work if a[b] would return a default value instead of throwing.
 Another problem (assuming the above is fixed) is that b will be looked up
 twice in the hash.

That is a problem. But like Sergey said, if performance is your #1 concern then return references.

It's one of the concerns. And there are several solutions that make everything work, including performance.
 Though, that solution is a little problematic for the sparse matrices.
  In order to return a reference to an element that didn't previously
 exist, you must create it.  But if you're just scanning through your
 sparse matrix printing out the values with a[b], you don't expect to
 end up with a dense matrix full of zeros!  In a C++ lib you'd probably
 provide an lvalue-returning operator[] and another function that
 returns just an rvalue.

Exactly.
 Ooh, how about passing in the manipulator  = function somehow?  Either
 delegate or template alias param.
 
 Example for sparse matrix case:
 // Called for  a[b]  = c type operations
 void opIndexUpdate(void delegate(ref ValueType val) updater, uint idx) {
       float *ptr = getPtrToElement(idx);  // creates new zero element if needed
       updater(*ptr);
       // here you can veto the change
       // or throw an overflow exception
       // or clamp *ptr
       // or round it
       //  or whatever you want...
 }
 
 Compiler would generate the different updaters needed for the various
  = operations.
 
 Maybe with a template alias param there'd maybe be better hope of the
 compiler being able to inline it all, but then you have the no
 inheritance problem.
 
 void opIndexUpdate(alias updater)(uint idx) {
       float *ptr = getPtrToElement(idx);  // creates new zero element if needed
       updater(*ptr);
 }

I am very glad you brought up this idea. I think it can work. I brought it up to Walter and Bartosz (in the alias form), and the status was to think about it some more. For whatever reason suggestions are viewed with increased negativity when coming from me around here, so I often wish someone else comes up with it, gets support, Walter implements it, and we all get over with. (That's what I hoped for the property thing, but that didn't go through.) In general, it's much easier to attack an imperfect proposal than to help make it better, and it's all the more enticing when it comes from a perceived authority. (Of course the next logical step is the question "then why didn't you help my proposal? :o)) Andrei
Oct 03 2008
prev sibling parent reply KennyTM~ <kennytm gmail.com> writes:
Bill Baxter wrote:
 On Fri, Oct 3, 2008 at 11:02 PM, Andrei Alexandrescu
 <SeeWebsiteForEmail erdani.org> wrote:
 Sergey Gromov wrote:
 Thu, 02 Oct 2008 15:03:42 -0500,
 Andrei Alexandrescu wrote:
 Yah, overloaded ops are due for an overhaul. I'm almost afraid to ask...
 any ideas? :o)

 One goal is to fix opIndexAssign and make it work similar to the way it
 works in arrays, e.g. a[b] += c. Indexing into hash tables is a good test
 bed.


throw an exception.

I don't see why you expect a[b] += c to work on a key for which a[b] is undefined. If it's undefined how can you increment it?

Actually I did use it once to count things, so I could just use a[b]++ instead of the clumsy if(b in a){a[b]++;}else{a[b]=1;}.
Oct 03 2008
parent Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
Bill Baxter wrote:
 On Sat, Oct 4, 2008 at 11:57 AM, KennyTM~ <kennytm gmail.com> wrote:
 Bill Baxter wrote:
 On Fri, Oct 3, 2008 at 11:02 PM, Andrei Alexandrescu
 <SeeWebsiteForEmail erdani.org> wrote:
 Sergey Gromov wrote:
 Thu, 02 Oct 2008 15:03:42 -0500,
 Andrei Alexandrescu wrote:
 Yah, overloaded ops are due for an overhaul. I'm almost afraid to
 ask...
 any ideas? :o)

 One goal is to fix opIndexAssign and make it work similar to the way it
 works in arrays, e.g. a[b] += c. Indexing into hash tables is a good
 test
 bed.


will throw an exception.

is undefined. If it's undefined how can you increment it?

instead of the clumsy if(b in a){a[b]++;}else{a[b]=1;}.

But D hashmaps don't work like that. I guess std::map in C++ does behave like that, so that could explain why Andrei would expect that behavior from a map data structure.

No, it's not std::map. Actually, much to everyone's confusion, Walter hacked a[b]++ and ++a[b] to automatically insert typeof(a[b]).init at slot b in a if it didn't exist. Try it! Andrei
Oct 03 2008
prev sibling parent Bruno Medeiros <brunodomedeiros+spam com.gmail> writes:
Andrei Alexandrescu wrote:
 Bill Baxter wrote:
 On Fri, Oct 3, 2008 at 3:16 AM, Fawzi Mohamed <fmohamed mac.com> wrote:

 This is a very nice feature to have.
 D reaches this with a minimal change to the language:
 optional parenthesis, and setter functions.

 ...
 It does have some limitation (no +=,-=,...) but is very compact, and 
 simple
 to explain.

Fortunately this is an orthogonal issue. This could (and should IMO) be fixed independent of the current discussion.

Yah, overloaded ops are due for an overhaul. I'm almost afraid to ask... any ideas? :o) One goal is to fix opIndexAssign and make it work similar to the way it works in arrays, e.g. a[b] += c. Indexing into hash tables is a good test bed. Andrei

Agh, this was a clear and good opportunity to fork a new thread. -- Bruno Medeiros - Software Developer, MSc. in CS/E graduate http://www.prowiki.org/wiki4d/wiki.cgi?BrunoMedeiros#D
Oct 03 2008
prev sibling next sibling parent Bruno Medeiros <brunodomedeiros+spam com.gmail> writes:
Fawzi Mohamed wrote:
 Here comes another person that would be unhappy without omittable 
 parenthesis.
 
 Having properties is a very useful thing, one should be able to make a 
 public field of a structure private and controlling access via methods 
 with minimal or no changes to the external code.
 

Like I said before to Andrei, the idea wasn't simply to drop the omittable parenthesis call, but also to find an alternate property mechanism. -- Bruno Medeiros - Software Developer, MSc. in CS/E graduate http://www.prowiki.org/wiki4d/wiki.cgi?BrunoMedeiros#D
Oct 03 2008
prev sibling parent "Bill Baxter" <wbaxter gmail.com> writes:
On Sat, Oct 4, 2008 at 12:07 PM, Andrei Alexandrescu
<SeeWebsiteForEmail erdani.org> wrote:
 Bill Baxter wrote:
 On Sat, Oct 4, 2008 at 11:57 AM, KennyTM~ <kennytm gmail.com> wrote:
 Bill Baxter wrote:
 On Fri, Oct 3, 2008 at 11:02 PM, Andrei Alexandrescu
 <SeeWebsiteForEmail erdani.org> wrote:
 Sergey Gromov wrote:
 Thu, 02 Oct 2008 15:03:42 -0500,
 Andrei Alexandrescu wrote:
 Yah, overloaded ops are due for an overhaul. I'm almost afraid to
 ask...
 any ideas? :o)

 One goal is to fix opIndexAssign and make it work similar to the way
 it
 works in arrays, e.g. a[b] += c. Indexing into hash tables is a good
 test
 bed.

What's wrong with a.opIndexAssign(b, a.opIndex(b) + c)?

One problem is that for a hashtable that does not have b yet, opIndex will throw an exception.

I don't see why you expect a[b] += c to work on a key for which a[b] is undefined. If it's undefined how can you increment it?

Actually I did use it once to count things, so I could just use a[b]++ instead of the clumsy if(b in a){a[b]++;}else{a[b]=1;}.

But D hashmaps don't work like that. I guess std::map in C++ does behave like that, so that could explain why Andrei would expect that behavior from a map data structure.

No, it's not std::map. Actually, much to everyone's confusion, Walter hacked a[b]++ and ++a[b] to automatically insert typeof(a[b]).init at slot b in a if it didn't exist. Try it! Andrei

Wierd. But yet plain a[b] will still throw an exception? --bb
Oct 03 2008
prev sibling parent reply Bruno Medeiros <brunodomedeiros+spam com.gmail> writes:
Andrei Alexandrescu wrote:
 Bruno Medeiros wrote:
 Andrei Alexandrescu wrote:
 Bruno Medeiros wrote:
 Sergey's argument (which is much more objective than subjective) is 
 not strictly against omitting parenthesis, but rather against 
 ambiguity (the dual possible ways to call zero-args functions), 
 which as has been shown, creates various possibilities for bugs when 
 you mix functions that return delegates. This ambiguity problem 
 would also be resolved if omitting parenthesis would be *the only 
 way* of calling functions with zero-args.

Yah, he made great points. There are ambiguities. The counter-argument is that that would make the syntax of calling functions with no arguments inconsistent with the syntax of calling function with arguments. The "()" is a degenerate case of "(a, b, c)". So it's hard to keep everybody happy.

Yes, if we solved the ambiguity problem that way, we would make a lot of people unhappy (myself included). But if we solved the problem the *other way* (forbidding omittable parenthesis for normal zero-args function), it would make, that I know of, only one person unhappy (you).

So there is a cabal. I knew it! :o)

If there is a cabal, it's a pretty lame one... Walter's Inner Circle are the ones with all the power! :o)
 I'm not that sure about "lot" and "one", and numbers-based arguments 
 aren't that strong to begin with. Language design is complex and full of 
 tradeoffs. In each decision there are consequences. If we require "()" 
 then we must come with a separate thing for properties - an addition to 
 the language. Then many people (maybe even including some the "lot" of 
 people you mentioned) will cry foul: "where's my simple language?" etc. 
 Maybe some people will say, hey, you were already there with the 
 omittable "()" and the "=" thing, why didn't you just make the latter 
 less trigger happy and we'd be home free?
 

Yes, we would need an alternate mechanism for properties - an addition to the language. And it's quite true that it's likely there would be disagreement in the "lot of people" about that. But there is only one way to be sure, so we could at least try! Would you and Walter at least consider this, and see if we could find an alternative that satisfies a fair number of people? Try without compromise. In fact, I'm also not a fan of those complex property mechanisms, l C#. I think a fair candidate would be Bill Baxter's proposal, the 'property' keyword: property int foo() { return _foo; }; property void foo(int foo) { _foo = foo; }; The property keyword would make a function callable *only* as a property syntax (either as reading, 'bar = foo;', or as writing, 'foo = 42;'). A function signature which was not adequate for property access would be compile-time error. This proposal fixes the ambiguities issues, and require *minimal changes* to the language, both in terms of syntax and semantics! -- Bruno Medeiros - Software Developer, MSc. in CS/E graduate http://www.prowiki.org/wiki4d/wiki.cgi?BrunoMedeiros#D
Oct 03 2008
parent reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
Bruno Medeiros wrote:
 Andrei Alexandrescu wrote:
 Yes, we would need an alternate mechanism for properties - an addition 
 to the language. And it's quite true that it's likely there would be 
 disagreement in the "lot of people" about that. But there is only one 
 way to be sure, so we could at least try! Would you and Walter at least 
 consider this, and see if we could find an alternative that satisfies a 
 fair number of people? Try without compromise.

What I'd consider is not that important. I do know what Walter's viewpoint is, and that is that there are plenty of bigger rocks to move.
 In fact, I'm also not a fan of those complex property mechanisms,  l 
 C#. I think a fair candidate would be Bill Baxter's proposal, the 
 'property' keyword:
 
   property int foo() { return _foo; };
   property void foo(int foo) { _foo = foo; };
 
 The property keyword would make a function callable *only* as a property 
 syntax (either as reading, 'bar = foo;', or as writing, 'foo = 42;'). A 
 function signature which was not adequate for property access would be 
 compile-time error.
 This proposal fixes the ambiguities issues, and require *minimal 
 changes* to the language, both in terms of syntax and semantics!

Then what would obj.fun mean when fun is not a property? Andrei
Oct 03 2008
next sibling parent Benji Smith <dlanguage benjismith.net> writes:
Andrei Alexandrescu wrote:
 Bruno Medeiros wrote:
 In fact, I'm also not a fan of those complex property mechanisms,  l 
 C#. I think a fair candidate would be Bill Baxter's proposal, the 
 'property' keyword:

   property int foo() { return _foo; };
   property void foo(int foo) { _foo = foo; };

 The property keyword would make a function callable *only* as a 
 property syntax (either as reading, 'bar = foo;', or as writing, 'foo 
 = 42;'). A function signature which was not adequate for property 
 access would be compile-time error.

Then what would obj.fun mean when fun is not a property? Andrei

If I understand the proposal correctly, it would be a compile-time error. --benji
Oct 03 2008
prev sibling parent Bruno Medeiros <brunodomedeiros+spam com.gmail> writes:
Andrei Alexandrescu wrote:
 Bruno Medeiros wrote:
 Andrei Alexandrescu wrote:
 Yes, we would need an alternate mechanism for properties - an addition 
 to the language. And it's quite true that it's likely there would be 
 disagreement in the "lot of people" about that. But there is only one 
 way to be sure, so we could at least try! Would you and Walter at 
 least consider this, and see if we could find an alternative that 
 satisfies a fair number of people? Try without compromise.

What I'd consider is not that important. I do know what Walter's viewpoint is, and that is that there are plenty of bigger rocks to move.

By "taking into consideration" I don't mean that he would implemented it right away. It's clear there are a lot of work being done in other important features. (Still, some of the property proposals, like the one below, could be quite simple if not trivial to implement)
 In fact, I'm also not a fan of those complex property mechanisms,  l 
 C#. I think a fair candidate would be Bill Baxter's proposal, the 
 'property' keyword:

   property int foo() { return _foo; };
   property void foo(int foo) { _foo = foo; };

 The property keyword would make a function callable *only* as a 
 property syntax (either as reading, 'bar = foo;', or as writing, 'foo 
 = 42;'). A function signature which was not adequate for property 
 access would be compile-time error.
 This proposal fixes the ambiguities issues, and require *minimal 
 changes* to the language, both in terms of syntax and semantics!

Then what would obj.fun mean when fun is not a property? Andrei

Good question. One option would be for obj.fun to be a compile error, as mentioned. But other yet, and cleaner IMO is for obj.fun to be the delegate value, ie, the same as the current "&obj.fun". The proposal's &obj.fun could be the same as obj.fun, as a deprecated syntax for backward compatibility. -- Bruno Medeiros - Software Developer, MSc. in CS/E graduate http://www.prowiki.org/wiki4d/wiki.cgi?BrunoMedeiros#D
Oct 06 2008
prev sibling parent "Bill Baxter" <wbaxter gmail.com> writes:
On Sat, Oct 4, 2008 at 11:57 AM, KennyTM~ <kennytm gmail.com> wrote:
 Bill Baxter wrote:
 On Fri, Oct 3, 2008 at 11:02 PM, Andrei Alexandrescu
 <SeeWebsiteForEmail erdani.org> wrote:
 Sergey Gromov wrote:
 Thu, 02 Oct 2008 15:03:42 -0500,
 Andrei Alexandrescu wrote:
 Yah, overloaded ops are due for an overhaul. I'm almost afraid to
 ask...
 any ideas? :o)

 One goal is to fix opIndexAssign and make it work similar to the way it
 works in arrays, e.g. a[b] += c. Indexing into hash tables is a good
 test
 bed.

What's wrong with a.opIndexAssign(b, a.opIndex(b) + c)?

One problem is that for a hashtable that does not have b yet, opIndex will throw an exception.

I don't see why you expect a[b] += c to work on a key for which a[b] is undefined. If it's undefined how can you increment it?

Actually I did use it once to count things, so I could just use a[b]++ instead of the clumsy if(b in a){a[b]++;}else{a[b]=1;}.

But D hashmaps don't work like that. I guess std::map in C++ does behave like that, so that could explain why Andrei would expect that behavior from a map data structure. --bb
Oct 03 2008
prev sibling next sibling parent KennyTM~ <kennytm gmail.com> writes:
I think someone should start a new thread on this? The argument is not 
relation to range design at all, and it's getting very difficult to 
watch this huge thread.

------

Andrei Alexandrescu wrote:
 I agree that 4 is a good principle. I'd even call it "consistency" -
 across languages that is. But let's not forget that C# defined an entire
 feature - properties - to fulfill what was perceived as a need.

And the "property" feature, unlike current D, do lift the ambiguity between procedures and properties that we (well, some of us) dislike. I believe we all agree support for property in D is a good thing, but we just hate the current syntax (making f equivalent to f() regardless of what f is). Again, for me, if you can restrict obj.f to be a valid getter only to pure methods (that won't cause any side effect to content of "this") then I'm all set. But probably not the others who demand getters must be fast. :)
 Also the
 Cecil language goes as far as requiring accessors for any field (and

Who uses Cecil? Never heard of it.
 defining methods implicitly when missing). Eiffel also has properties
 that are obtained via syntactic conventions (getX and setX implement
 property x). 

But not making x === x() I afraid. I don't know Eiffel, though.
 People want to add properties to Java 7 (unfortunately the
 google search is clogged by a Java class called Properties so it needs
 some combing). 

There is an explicit "property" keyword in Java and more importantly, properties are accessed using the syntax obj->prop (ugly I know). The problem we concerned do not exist. (Ref: http://www.javalobby.org/java/forums/t88090.html; The Google search keyword is "java-7 property")
 
 I'm impressed a lot by the analogy with math because it is one extra 
 argument in my favor. In math there's no function without arguments. At 
 most people would say f(.) (with a central dot) when referring to what 
 is akin to the address of the function.
 
 
 Andrei

Sorry, in math the normal way to represent a function is to just use the symbol, i.e. f, e.g. chain rule (f o g)' = g' * f' o g. The central dot is used only when the "function" is not represented in the form f(a,b,c,...), e.g. the commutator [.,.] and the mean <.>. I have *never* seen anyone writes f(.). But I don't think we should directly apply math language to a programming language.
Oct 02 2008
prev sibling next sibling parent "Bill Baxter" <wbaxter gmail.com> writes:
On Fri, Oct 3, 2008 at 9:37 AM, Bill Baxter <wbaxter gmail.com> wrote:
 ... except these extras do have the same issue that plain property
 assignment does.  They would open up a new class of things that are
 valid code but don't behave as expected.  writefln += 5.

Doh, obviously writefln is a bad example there. Imagine a function that takes an int, tries to do something that many times, then returns an int indicating how many times it actually managed to do it. --bb
Oct 02 2008
prev sibling parent "Bill Baxter" <wbaxter gmail.com> writes:
On Fri, Oct 3, 2008 at 11:43 PM, Sergey Gromov <snake.scaly gmail.com> wrote:
 Fri, 03 Oct 2008 20:59:54 +0800,
 KennyTM~ wrote:
 Sergey Gromov wrote:
 What's wrong with a.opIndexAssign(b, a.opIndex(b) + c)?

Probably performance. Consider seeking to the end of a 100M-node single-linked list, and increase its content by 1. But I agree that if something like .opIndexAddAssign() is not defined, the compiler should fall back to use a.opIndexAssign(b, a.opIndex(b)+c). (The same idea can be extended to properties and .opSlice() )

No, if you want performance in this particular case you define ref int opIndex() because I think whenever compiler encounters a[x]++ it should first test whether a[x] is an lvalue and if yes use it accordingly. And only if it is not it should fall back to .opIndexAddButProbablyNotAssign() special overloads.

Yeh, but then you lose encapsulation. That's fine for some cases, like a vector, but in other cases it can be very handy to be able to get a notification any time one of your values changes. You lose that ability once you start handing out pointers to whoever asks for one. --bb
Oct 03 2008
prev sibling next sibling parent Bruno Medeiros <brunodomedeiros+spam com.gmail> writes:
Bruno Medeiros wrote:
  And of that list, only VB 6, Pascal 
 and Perl, have the possibility of omitting parenthesis. 

Correction, Ruby allows it as well. Hum. -- Bruno Medeiros - Software Developer, MSc. in CS/E graduate http://www.prowiki.org/wiki4d/wiki.cgi?BrunoMedeiros#D
Oct 02 2008
prev sibling next sibling parent "Bill Baxter" <wbaxter gmail.com> writes:
On Fri, Oct 3, 2008 at 9:32 AM, Bill Baxter <wbaxter gmail.com> wrote:
 On Fri, Oct 3, 2008 at 8:04 AM, Sergey Gromov <snake.scaly gmail.com> wrote:
 Thu, 02 Oct 2008 15:03:42 -0500,
 Andrei Alexandrescu wrote:
 Yah, overloaded ops are due for an overhaul. I'm almost afraid to ask...
 any ideas? :o)

 One goal is to fix opIndexAssign and make it work similar to the way it
 works in arrays, e.g. a[b] += c. Indexing into hash tables is a good
 test bed.

What's wrong with a.opIndexAssign(b, a.opIndex(b) + c)?

Indeed. I thought there wasn't a lot of debate needed on this, just action.

... except these extras do have the same issue that plain property assignment does. They would open up a new class of things that are valid code but don't behave as expected. writefln += 5. And also, a[b] += c should probably be rewritten as "a.opIndex(b) += c" *if* a returns a reference of some sort. Ok, so maybe there is a little to talk about. :-) --bb
Oct 02 2008
prev sibling parent "Bill Baxter" <wbaxter gmail.com> writes:
On Fri, Oct 3, 2008 at 11:02 PM, Andrei Alexandrescu
<SeeWebsiteForEmail erdani.org> wrote:
 Sergey Gromov wrote:
 Thu, 02 Oct 2008 15:03:42 -0500,
 Andrei Alexandrescu wrote:
 Yah, overloaded ops are due for an overhaul. I'm almost afraid to ask...
 any ideas? :o)

 One goal is to fix opIndexAssign and make it work similar to the way it
 works in arrays, e.g. a[b] += c. Indexing into hash tables is a good test
 bed.

What's wrong with a.opIndexAssign(b, a.opIndex(b) + c)?

One problem is that for a hashtable that does not have b yet, opIndex will throw an exception.

I don't see why you expect a[b] += c to work on a key for which a[b] is undefined. If it's undefined how can you increment it? Unless you've defined your accessor for undefined keys to return some other value. And if it returns some other value then Sergey's rule is fine. Sparse matrices are a good example of a hash-like data-structure that should return a default value (namely zero) for unset keys. For such a sparse matrix a.opIndexAssign(b, a.opIndex(b) + c) will work fine.
 Another problem (assuming the above is fixed) is that b will be looked up
 twice in the hash.

That is a problem. But like Sergey said, if performance is your #1 concern then return references. Though, that solution is a little problematic for the sparse matrices. In order to return a reference to an element that didn't previously exist, you must create it. But if you're just scanning through your sparse matrix printing out the values with a[b], you don't expect to end up with a dense matrix full of zeros! In a C++ lib you'd probably provide an lvalue-returning operator[] and another function that returns just an rvalue. Ooh, how about passing in the manipulator = function somehow? Either delegate or template alias param. Example for sparse matrix case: // Called for a[b] = c type operations void opIndexUpdate(void delegate(ref ValueType val) updater, uint idx) { float *ptr = getPtrToElement(idx); // creates new zero element if needed updater(*ptr); // here you can veto the change // or throw an overflow exception // or clamp *ptr // or round it // or whatever you want... } Compiler would generate the different updaters needed for the various = operations. Maybe with a template alias param there'd maybe be better hope of the compiler being able to inline it all, but then you have the no inheritance problem. void opIndexUpdate(alias updater)(uint idx) { float *ptr = getPtrToElement(idx); // creates new zero element if needed updater(*ptr); } --bb
Oct 03 2008
prev sibling parent "Bill Baxter" <wbaxter gmail.com> writes:
On Fri, Oct 3, 2008 at 8:04 AM, Sergey Gromov <snake.scaly gmail.com> wrote:
 Thu, 02 Oct 2008 15:03:42 -0500,
 Andrei Alexandrescu wrote:
 Yah, overloaded ops are due for an overhaul. I'm almost afraid to ask...
 any ideas? :o)

 One goal is to fix opIndexAssign and make it work similar to the way it
 works in arrays, e.g. a[b] += c. Indexing into hash tables is a good
 test bed.

What's wrong with a.opIndexAssign(b, a.opIndex(b) + c)?

Indeed. I thought there wasn't a lot of debate needed on this, just action. --bb
Oct 02 2008
prev sibling parent "Bill Baxter" <wbaxter gmail.com> writes:
On Fri, Oct 3, 2008 at 3:16 AM, Fawzi Mohamed <fmohamed mac.com> wrote:

 This is a very nice feature to have.
 D reaches this with a minimal change to the language:
 optional parenthesis, and setter functions.

 ...
 It does have some limitation (no +=,-=,...) but is very compact, and simple
 to explain.

Fortunately this is an orthogonal issue. This could (and should IMO) be fixed independent of the current discussion. --bb
Oct 02 2008
prev sibling parent reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
Steven Schveighoffer wrote:
 "Andrei Alexandrescu" wrote
 Steven Schveighoffer wrote:
 Hidden in this statement is that you consider () to be semantically 
 meaningless.  I see it not as meaningless, but a distinction between what 
 the symbol is supposed to represent.  A property should represent a 
 value. A function should represent an action.

principles I quoted above. Then I clearly explained how C's handling of function names breaks these principle, with examples. Please refer to that post. There is nothing hidden my statement.

Sorry, I look at things more simply, and I'm not really into language design theory ;) I didn't realize that calling something a principle meant that it is generally accepted to be fact, my apologies. So I should read your statement as: "a principle that is generally accepted to be universally true by major language design geeks, that I also agree with, is..." I'm not trying to be clever here, I really didn't understand that it was implied.

No, you shouldn't read it that way. (Explanation below.)
 So I agree with your principle, just not that () falls within the category 
 of 'pragmatically or semantically meaningless.'

I'd also disagree with that, and I never said that either. What I said was that in C use of a function name alone is syntactically valid, semantically a no-op, and consequently pragmatically meaningless.
 I'd also disagree that the most frequently used construct of calling a 
 function is without parentheses.

I'd also disagree with that, and I never said that either. My perception is that there is a serious "barrier in miscommunication" out there. What I said was that the most frequent use of a function is to call it. Therefore it is a pity that mentioning its name not only does not call it, but in fact does nothing.
 I'll also note that your basis/proof is not any more proof than mine ;) 
 You just made statements about what you believe, as did I.

using that basis. If you want to invalidate that, you need to invalidate my basis, i.e., the principles of language design I am invoking. You are of course free to do that since principles of programming language design are to large extent a subjective matter.

Not really. You provided no proof that your principles apply to this debate, you just stated that they do.

No, you don't understand. Let me explain again. I stated two principles of language design. They could be true or false. They are up there for debate. They are subjective, because aside from some basics, language design is subjective. The principles are: 1) A language should minimize the number of syntactic constructs that are semantically and/or pragmatically meaningless. 2) The more frequently-used constructs should be given syntactic priority over the less-used constructs, particularly when the latter are also at risk of breaking the first principle. Then based on the principles I explained how C's use of function names breaks them in the post on Thu, 25 Sep 2008 22:59:04 +0000 (UTC). It's easy, really.
 Sorry, change my statement to 'But it makes sense and still does'.  And I 
 never said it was a proof.  It's a statement of my reasons behind 
 believing the () decision was on purpose.

poor decision because it breaks the aforementioned principles.

Respectfully disagree.

Now you either disagree with the principles, the consequences, or the way the consequences derive from the principles. My understanding is that the problem is with the latter. Again I am referring you to that same post.
 Not really.  A function symbol by itself is the address of the function. 
 A pointer symbol by itself is the address of the data it points to.  It's 
 the same.  I don't think you understood the detail that I stated, 'get 
 address it's pointing to', not 'get address of the pointer'.

farther. Please understand how you are making an elementary mistake. Consider: int x; Then if I only use "x", I am getting x's value (an lvalue in fact), not its address. Now consider: int * x; Then if I only use "x", I am getting x's value, which is the address of an int. In neither case is it possible for x and &x to mean the same thing. For functions some really weird stuff happens: // this is C #include <assert.h> void foo() {}

How does this make any difference to whether you can call a function without parentheses or not? You are arguing that for a function defined as foo(), foo; should mean call function foo, and I'm arguing that it should be a syntax error. Neither of us want the C behavior of evaluating to the function address. I think &foo should be the proper method of taking the address of a function, as it is in D.

I am glad this partially took care of itself. Andrei
Sep 29 2008
next sibling parent reply Benji Smith <dlanguage benjismith.net> writes:
Andrei Alexandrescu wrote:
 I stated two principles of language design. They could be true or false. 
 They are up there for debate. They are subjective, because aside from 
 some basics, language design is subjective.
 
 The principles are:
 
 1) A language should minimize the number of syntactic constructs that 
 are semantically and/or pragmatically meaningless.
 
 2) The more frequently-used constructs should be given syntactic 
 priority over the less-used constructs, particularly when the latter are 
 also at risk of breaking the first principle.

I'd like to propose another principle of language design: 3) Consistency -- The expression of a semantic construct should always use the same syntax. Likewise, multiple uses of the same syntactic constructs should always result in the same semantics. Based on that principle, I'd argue that function-calling should either always use parentheses, or it should never use parentheses. Requiring parentheses for some function calls, but not for others violates the principle of consistency. In my prioritization of language-design principles, consistency is more important then syntactic economy. Based on those principles, I believe that the parentheses should be mandatory for all function calls. --benji
Sep 30 2008
next sibling parent reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
Benji Smith wrote:
 Andrei Alexandrescu wrote:
 I stated two principles of language design. They could be true or 
 false. They are up there for debate. They are subjective, because 
 aside from some basics, language design is subjective.

 The principles are:

 1) A language should minimize the number of syntactic constructs that 
 are semantically and/or pragmatically meaningless.

 2) The more frequently-used constructs should be given syntactic 
 priority over the less-used constructs, particularly when the latter 
 are also at risk of breaking the first principle.

I'd like to propose another principle of language design: 3) Consistency -- The expression of a semantic construct should always use the same syntax. Likewise, multiple uses of the same syntactic constructs should always result in the same semantics.

Consistency is good, but that's not consistency. http://www.merriam-webster.com/dictionary/consistency agreement or harmony of parts or features to one another or a whole : correspondence ; specifically : ability to be asserted together without contradiction An example of consistency is that user-defined operators have the same syntax and precedence as built-in operators.
 Based on that principle, I'd argue that function-calling should either 
 always use parentheses, or it should never use parentheses.

Yah I'd like that too. It's economy of syntax.
 Requiring parentheses for some function calls, but not for others 
 violates the principle of consistency.

No. It violates economy of syntax. There are many syntaxes for the same semantics. (I'll also note that Perl does not abide to economy of syntax - see Tim Toady and all that.)
 In my prioritization of language-design principles, consistency is more 
 important then syntactic economy.

Mine too. You just got a few terms jumbled.
 Based on those principles, I believe that the parentheses should be 
 mandatory for all function calls.

That needs revision. Andrei
Oct 01 2008
parent reply Bruno Medeiros <brunodomedeiros+spam com.gmail> writes:
Andrei Alexandrescu wrote:
 Benji Smith wrote:
 Andrei Alexandrescu wrote:
 I stated two principles of language design. They could be true or 
 false. They are up there for debate. They are subjective, because 
 aside from some basics, language design is subjective.

 The principles are:

 1) A language should minimize the number of syntactic constructs that 
 are semantically and/or pragmatically meaningless.

 2) The more frequently-used constructs should be given syntactic 
 priority over the less-used constructs, particularly when the latter 
 are also at risk of breaking the first principle.

I'd like to propose another principle of language design: 3) Consistency -- The expression of a semantic construct should always use the same syntax. Likewise, multiple uses of the same syntactic constructs should always result in the same semantics.

Consistency is good, but that's not consistency. http://www.merriam-webster.com/dictionary/consistency agreement or harmony of parts or features to one another or a whole : correspondence ; specifically : ability to be asserted together without contradiction

The concept of "consistency" has a more specialized meaning when applied to language design. (just has it has in other fields, like mathematics) So I agree with benji that not having "The expression of a semantic construct should always use the same syntax" is not being consistent. (There are of course other situations where things can be inconsistent.) If you don't agree with this definition of "consistency", I'm not gonna argue with that , just substitute it with "conzistency" or "foobar", or whatever and proceed.
 An example of consistency is that user-defined operators have the same
 syntax and precedence as built-in operators.
 
 Based on that principle, I'd argue that function-calling should either 
 always use parentheses, or it should never use parentheses.

Yah I'd like that too. It's economy of syntax.

Then why are you not suggesting, or campaigning for, such change?
 Requiring parentheses for some function calls, but not for others 
 violates the principle of consistency.

No. It violates economy of syntax. There are many syntaxes for the same semantics.

It violates consistency, as defined above. It may violate economy of syntax as well (or it may be the same thing). Whatever. What I ask is, what would you suggest to fix it? Should all function calling not use parenthesis? And how would that syntax be? -- Bruno Medeiros - Software Developer, MSc. in CS/E graduate http://www.prowiki.org/wiki4d/wiki.cgi?BrunoMedeiros#D
Oct 02 2008
parent reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
Bruno Medeiros wrote:
 Andrei Alexandrescu wrote:
 Benji Smith wrote:
 Andrei Alexandrescu wrote:
 I stated two principles of language design. They could be true or 
 false. They are up there for debate. They are subjective, because 
 aside from some basics, language design is subjective.

 The principles are:

 1) A language should minimize the number of syntactic constructs 
 that are semantically and/or pragmatically meaningless.

 2) The more frequently-used constructs should be given syntactic 
 priority over the less-used constructs, particularly when the latter 
 are also at risk of breaking the first principle.

I'd like to propose another principle of language design: 3) Consistency -- The expression of a semantic construct should always use the same syntax. Likewise, multiple uses of the same syntactic constructs should always result in the same semantics.

Consistency is good, but that's not consistency. http://www.merriam-webster.com/dictionary/consistency agreement or harmony of parts or features to one another or a whole : correspondence ; specifically : ability to be asserted together without contradiction

The concept of "consistency" has a more specialized meaning when applied to language design. (just has it has in other fields, like mathematics) So I agree with benji that not having "The expression of a semantic construct should always use the same syntax" is not being consistent. (There are of course other situations where things can be inconsistent.)

No. Not at all. For consistency you must assert together something with something else. If there are two possible and equivalent syntaxes for a feature that's not lack of consistency. It's waste. We'd be talking about consistency if at most "syntax in context A should be the same as syntax in context B". But if the context is irrelevant to the syntax then we're not quite talking about consistency. Let me give another example. One could say in C++ overloading template functions is inconsistent with overloading regular functions because they follow very different rules. The fact that in C++ you can specify either "class" or "typename" for a template parameter is NOT inconsistent. It is wasteful.
 If you don't agree with this definition of "consistency", I'm not gonna 
 argue with that , just substitute it with "conzistency" or "foobar", or 
 whatever and proceed.

No. Defining terms is important. If we have the wrong terms in mind when talking about something, communication is impeded.
 An example of consistency is that user-defined operators have the same
 syntax and precedence as built-in operators.

 Based on that principle, I'd argue that function-calling should 
 either always use parentheses, or it should never use parentheses.

Yah I'd like that too. It's economy of syntax.

Then why are you not suggesting, or campaigning for, such change?

Because language design is putting many desiderata in agreement, not following one blindly.
 Requiring parentheses for some function calls, but not for others 
 violates the principle of consistency.

No. It violates economy of syntax. There are many syntaxes for the same semantics.

It violates consistency, as defined above.

No. You can't define a term to mean whatever you want. I mean you can, but then it's hard to communicate.
 It may violate economy of 
 syntax as well (or it may be the same thing). Whatever. What I ask is, 
 what would you suggest to fix it? Should all function calling not use 
 parenthesis? And how would that syntax be?

I'm happy with how things are now except for the assignment that is accepted all to often. I suggested a simple fix for that. Andrei
Oct 02 2008
next sibling parent reply Ian Sakkis <iansakkis thisisnotmyrealemail.com> writes:
Andrei Alexandrescu Wrote:

 No. Not at all. For consistency you must assert together something with 
 something else. If there are two possible and equivalent syntaxes for a 
 feature that's not lack of consistency. It's waste. We'd be talking 
 about consistency if at most "syntax in context A should be the same as 
 syntax in context B". But if the context is irrelevant to the syntax 
 then we're not quite talking about consistency.

Consistency or economy of syntax I think you are avoiding the issue which is that we need something that fulfills our requirements. And those are: * A way to specify that one or two functions behave exactly as a field without ambiguity. (Assignment, reading, taking an address, etc.) * A way to specify that a function doesn't behave as a field: writelfn = 3; * A way to change from field to property and back without silently affecting client code (which might be out of our control). * And some others that I can find right now. Until now the only proposal that fits these requirements involves adding another keyword which you are reluctant to accept. Personally I prefer the python property decorator but I don't think it fares well with the D style: | Property | def name: | def fget(): | def fset(): | def fdel(): Probably something along the lines of C# 3.0 property declaration its better suited for D: | public string Name { get; set; } And I don't think that backwards compatibility is an issue on D2. We already have several breaking changes (cost, invariant, et al) and this is an easy one to fix. Cheers. PS: On second reading the post has something of a harsh tone but please be assured that this isn't a personal attack. English isn't my first language.
Oct 02 2008
parent Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
Ian Sakkis wrote:
 PS: On second reading the post has something of a harsh tone but
 please be assured that this isn't a personal attack. English isn't my
 first language.

Then revision before posting might have been an option. There's no "me" against "we". This is ridiculous. If a cabal was being created, please mail me the application papers. Andrei
Oct 02 2008
prev sibling parent reply Bruno Medeiros <brunodomedeiros+spam com.gmail> writes:
Andrei Alexandrescu wrote:
 Bruno Medeiros wrote:
 Andrei Alexandrescu wrote:
 Benji Smith wrote:
 Andrei Alexandrescu wrote:
 I stated two principles of language design. They could be true or 
 false. They are up there for debate. They are subjective, because 
 aside from some basics, language design is subjective.

 The principles are:

 1) A language should minimize the number of syntactic constructs 
 that are semantically and/or pragmatically meaningless.

 2) The more frequently-used constructs should be given syntactic 
 priority over the less-used constructs, particularly when the 
 latter are also at risk of breaking the first principle.

I'd like to propose another principle of language design: 3) Consistency -- The expression of a semantic construct should always use the same syntax. Likewise, multiple uses of the same syntactic constructs should always result in the same semantics.

Consistency is good, but that's not consistency. http://www.merriam-webster.com/dictionary/consistency agreement or harmony of parts or features to one another or a whole : correspondence ; specifically : ability to be asserted together without contradiction

The concept of "consistency" has a more specialized meaning when applied to language design. (just has it has in other fields, like mathematics) So I agree with benji that not having "The expression of a semantic construct should always use the same syntax" is not being consistent. (There are of course other situations where things can be inconsistent.)

No. Not at all. For consistency you must assert together something with something else. If there are two possible and equivalent syntaxes for a feature that's not lack of consistency. It's waste. We'd be talking about consistency if at most "syntax in context A should be the same as syntax in context B". But if the context is irrelevant to the syntax then we're not quite talking about consistency.

I wasn't talking about the two cases of calling a zero-args function with parenthesis, and calling it without parenteshis (the "two possible and equivalent syntaxes for a feature"). That's not inconsistency, it's waste, I agree. I was talking about the two cases of calling a zero-args function ("context A"), and a n-args function ("context B"). So here we can talk about consistency, like you defined: "syntax in context A should be the same as syntax in context B". As such, the syntax for both cases is more consistent if they both use parenthesis.
 
 If you don't agree with this definition of "consistency", I'm not 
 gonna argue with that , just substitute it with "conzistency" or 
 "foobar", or whatever and proceed.

No. Defining terms is important. If we have the wrong terms in mind when talking about something, communication is impeded.

I agree.
 Based on that principle, I'd argue that function-calling should 
 either always use parentheses, or it should never use parentheses.

Yah I'd like that too. It's economy of syntax.

Then why are you not suggesting, or campaigning for, such change?

Because language design is putting many desiderata in agreement, not following one blindly.

I don't get it. What other desiderata are in conflict? Those of other people, or other desiderata of your own? Because when you say "I'd like that too", I read that as meaning that, at least for your own, you would prefer if such change was implemented. (ie, weighting all your concerns, you still prefer the change)
 Requiring parentheses for some function calls, but not for others 
 violates the principle of consistency.

No. It violates economy of syntax. There are many syntaxes for the same semantics.

It violates consistency, as defined above.

No. You can't define a term to mean whatever you want. I mean you can, but then it's hard to communicate.

By "some functions" I meant n-args functions, and by "others" I meant zero-args functions. That gives: "[Putting] parenthesis for n-args function calls, but not for zero-args function calls violates the principle of consistency." So we go back to the first point in this post.
 It may violate economy of syntax as well (or it may be the same 
 thing). Whatever. What I ask is, what would you suggest to fix it? 
 Should all function calling not use parenthesis? And how would that 
 syntax be?

I'm happy with how things are now except for the assignment that is accepted all to often. I suggested a simple fix for that. Andrei

I don't understand that. You stated "It violates economy of syntax.", so it violates your Principle 1 of language design. So how can you be happy with that (apart from the assignment issue)? Does fixing this problem violate some other principle or concern of yours? -- Bruno Medeiros - Software Developer, MSc. in CS/E graduate http://www.prowiki.org/wiki4d/wiki.cgi?BrunoMedeiros#D
Oct 02 2008
parent reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
Bruno Medeiros wrote:
 Andrei Alexandrescu wrote:
 Because language design is putting many desiderata in agreement, not 
 following one blindly.

I don't get it. What other desiderata are in conflict? Those of other people, or other desiderata of your own? Because when you say "I'd like that too", I read that as meaning that, at least for your own, you would prefer if such change was implemented. (ie, weighting all your concerns, you still prefer the change)

It's really simple in fact. For example one desideratum is to not complicate the language unnecessarily. When I said I'd like that, I put aside the obvious cost of burdening the language. (Sorry for being unclear.)
 Requiring parentheses for some function calls, but not for others 
 violates the principle of consistency.

No. It violates economy of syntax. There are many syntaxes for the same semantics.

It violates consistency, as defined above.

No. You can't define a term to mean whatever you want. I mean you can, but then it's hard to communicate.

By "some functions" I meant n-args functions, and by "others" I meant zero-args functions. That gives: "[Putting] parenthesis for n-args function calls, but not for zero-args function calls violates the principle of consistency." So we go back to the first point in this post.
 It may violate economy of syntax as well (or it may be the same 
 thing). Whatever. What I ask is, what would you suggest to fix it? 
 Should all function calling not use parenthesis? And how would that 
 syntax be?

I'm happy with how things are now except for the assignment that is accepted all to often. I suggested a simple fix for that. Andrei

I don't understand that. You stated "It violates economy of syntax.", so it violates your Principle 1 of language design. So how can you be happy with that (apart from the assignment issue)? Does fixing this problem violate some other principle or concern of yours?

I think keeping the language simple is also a good goal. Andrei
Oct 02 2008
parent reply Sean Kelly <sean invisibleduck.org> writes:
== Quote from Andrei Alexandrescu (SeeWebsiteForEmail erdani.org)'s article
 Bruno Medeiros wrote:
 I don't understand that. You stated "It violates economy of syntax.", so
  it violates your Principle 1 of language design. So how can you be
 happy with that (apart from the assignment issue)? Does fixing this
 problem violate some other principle or concern of yours?


I apologize if this has been brought up before, but I'm not sure that simplicity is a good thing if it can result in unpredictable behavior. For example: import std.stdio; class C { this( bool delegate() p ) { version( a ) cond = p; version( b ) ptr = p; } version( a ) { bool delegate() cond; } version( b ) { bool delegate() ptr; bool delegate() cond() { return ptr; } } } void main() { bool test() { writefln( "hi" ); return false; } auto c = new C( &test ); if( c.cond() ) writefln( "true" ); } Running version=a prints "hi" while version=b prints "true." If there were some way in the language to say "cond() is a property" then this issue would not be silently introduced when changing cond from a variable to a method. Sean
Oct 02 2008
next sibling parent Fawzi Mohamed <fmohamed mac.com> writes:
On 2008-10-02 20:06:32 +0200, Sean Kelly <sean invisibleduck.org> said:

 == Quote from Andrei Alexandrescu (SeeWebsiteForEmail erdani.org)'s article
 Bruno Medeiros wrote:
 
 I don't understand that. You stated "It violates economy of syntax.", so
 it violates your Principle 1 of language design. So how can you be
 happy with that (apart from the assignment issue)? Does fixing this
 problem violate some other principle or concern of yours?


I apologize if this has been brought up before, but I'm not sure that simplicity is a good thing if it can result in unpredictable behavior. For example: import std.stdio; class C { this( bool delegate() p ) { version( a ) cond = p; version( b ) ptr = p; } version( a ) { bool delegate() cond; } version( b ) { bool delegate() ptr; bool delegate() cond() { return ptr; } } } void main() { bool test() { writefln( "hi" ); return false; } auto c = new C( &test ); if( c.cond() ) writefln( "true" ); } Running version=a prints "hi" while version=b prints "true." If there were some way in the language to say "cond() is a property" then this issue would not be silently introduced when changing cond from a variable to a method. Sean

Ok this is a good reason to have real properties, a somewhat exotic example (with a delegate one can also wrap the delegate in the functions hew wants to call , and store the wrapped delegate instead of having a property access), but still a good reason. Fawzi
Oct 02 2008
prev sibling parent reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
Sean Kelly wrote:
 == Quote from Andrei Alexandrescu (SeeWebsiteForEmail erdani.org)'s article
 Bruno Medeiros wrote:
 I don't understand that. You stated "It violates economy of syntax.", so
  it violates your Principle 1 of language design. So how can you be
 happy with that (apart from the assignment issue)? Does fixing this
 problem violate some other principle or concern of yours?


I apologize if this has been brought up before, but I'm not sure that simplicity is a good thing if it can result in unpredictable behavior. For example: import std.stdio; class C { this( bool delegate() p ) { version( a ) cond = p; version( b ) ptr = p; } version( a ) { bool delegate() cond; } version( b ) { bool delegate() ptr; bool delegate() cond() { return ptr; } } } void main() { bool test() { writefln( "hi" ); return false; } auto c = new C( &test ); if( c.cond() ) writefln( "true" ); } Running version=a prints "hi" while version=b prints "true." If there were some way in the language to say "cond() is a property" then this issue would not be silently introduced when changing cond from a variable to a method.

Oh how I am with you on this. I've always thought mentioning a delegate name should NOT EVER evaluate the delegate. Walter definitely took the wrong turn down that alley there. And guess what. He got ambushed by the "lazy" keyword right there. I told Walter to not do that "lazy" keyword, he disregarded, but the time will come when that stone will be turned. Andrei
Oct 02 2008
next sibling parent reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
Bill Baxter wrote:
 Running version=a prints "hi" while version=b prints "true."  If there
 were
 some way in the language to say "cond() is a property" then this issue
 would
 not be silently introduced when changing cond from a variable to a method.

should NOT EVER evaluate the delegate. Walter definitely took the wrong turn down that alley there. And guess what. He got ambushed by the "lazy" keyword right there. I told Walter to not do that "lazy" keyword, he disregarded, but the time will come when that stone will be turned. Andrei

He mentions your name right there on the description of lazy, though. I always thought it was basically your idea because of that, or at least that you designed it together. Given what you are saying above, you might want to ask him politely to remove your name from that page.

I suggested that a function taking a delegate should accept an unadorned expression: void twice(void delegate() fun) { fun(); fun(); } ... int a; twice(++a); // makes a = 2 That has a number of issues itself, which can be worked out. Then somebody (in the newsgroup if I remember correctly) said, well this is fine and dandy, but just add a "lazy" keyword there. He did, and I knew it was a mistake the first time I saw it. Andrei
Oct 02 2008
parent Tom S <h3r3tic remove.mat.uni.torun.pl> writes:
Andrei Alexandrescu wrote:
 I suggested that a function taking a delegate should accept an unadorned 
 expression:
 
 void twice(void delegate() fun) { fun(); fun(); }
 ...
 int a;
 twice(++a); // makes a = 2
 
 That has a number of issues itself, which can be worked out. Then 
 somebody (in the newsgroup if I remember correctly) said, well this is 
 fine and dandy, but just add a "lazy" keyword there. He did, and I knew 
 it was a mistake the first time I saw it.

I guess that someone was me (and at the NG, so you remember right). But yea, let's just trivialize all the broken code and surrounding issues :P People actually wanted to burn lazy evaluation on a stack, and my post was merely an attempt on reaching a compromise. And I didn't really say it was all fine and dandy. Why not? Suddenly you got conflicts between: void foo(int delegate()) void foo(int) Apparently some libraries used that. And I don't think that any of my own libs were the issue. So please bear in mind that there were more people behind it and their frustration level was at some point pretty high (along the lines of "Walter keeps releasing new stuff and instead of fixing old issues, breaks fine code. [Expletive deleted] this."). If you had ideas to work it out, it would've been fine time to state them. I'm actually looking forward to the solution for D2.0. -- Tomasz Stachowiak http://h3.team0xf.com/ h3/h3r3tic on #D freenode
Oct 02 2008
prev sibling next sibling parent reply Bruno Medeiros <brunodomedeiros+spam com.gmail> writes:
Andrei Alexandrescu wrote:
 Sean Kelly wrote:
 == Quote from Andrei Alexandrescu (SeeWebsiteForEmail erdani.org)'s 
 article
 Bruno Medeiros wrote:
 I don't understand that. You stated "It violates economy of 
 syntax.", so
  it violates your Principle 1 of language design. So how can you be
 happy with that (apart from the assignment issue)? Does fixing this
 problem violate some other principle or concern of yours?


I apologize if this has been brought up before, but I'm not sure that simplicity is a good thing if it can result in unpredictable behavior. For example: import std.stdio; class C { this( bool delegate() p ) { version( a ) cond = p; version( b ) ptr = p; } version( a ) { bool delegate() cond; } version( b ) { bool delegate() ptr; bool delegate() cond() { return ptr; } } } void main() { bool test() { writefln( "hi" ); return false; } auto c = new C( &test ); if( c.cond() ) writefln( "true" ); } Running version=a prints "hi" while version=b prints "true." If there were some way in the language to say "cond() is a property" then this issue would not be silently introduced when changing cond from a variable to a method.

Oh how I am with you on this. I've always thought mentioning a delegate name should NOT EVER evaluate the delegate. Walter definitely took the wrong turn down that alley there. And guess what. He got ambushed by the "lazy" keyword right there. I told Walter to not do that "lazy" keyword, he disregarded, but the time will come when that stone will be turned. Andrei

Hold it right there! Are you saying that the existence of 'lazy' somehow required, or helped justify the existence of the omittable parenthesis functionality? How is that? I think you are accusing innocents ('lazy') or wrongdoing. 'lazy' has nothing to do with the omittable parenthesis functionality, I think they are completely orthogonal. Prove me wrong otherwise. -- Bruno Medeiros - Software Developer, MSc. in CS/E graduate http://www.prowiki.org/wiki4d/wiki.cgi?BrunoMedeiros#D
Oct 03 2008
next sibling parent Fawzi Mohamed <fmohamed mac.com> writes:
On 2008-10-03 12:05:17 +0200, Bruno Medeiros 
<brunodomedeiros+spam com.gmail> said:

 Andrei Alexandrescu wrote:
 Sean Kelly wrote:
 == Quote from Andrei Alexandrescu (SeeWebsiteForEmail erdani.org)'s article
 Bruno Medeiros wrote:
 I don't understand that. You stated "It violates economy of syntax.", so
  it violates your Principle 1 of language design. So how can you be
 happy with that (apart from the assignment issue)? Does fixing this
 problem violate some other principle or concern of yours?


I apologize if this has been brought up before, but I'm not sure that simplicity is a good thing if it can result in unpredictable behavior. For example: import std.stdio; class C { this( bool delegate() p ) { version( a ) cond = p; version( b ) ptr = p; } version( a ) { bool delegate() cond; } version( b ) { bool delegate() ptr; bool delegate() cond() { return ptr; } } } void main() { bool test() { writefln( "hi" ); return false; } auto c = new C( &test ); if( c.cond() ) writefln( "true" ); } Running version=a prints "hi" while version=b prints "true." If there were some way in the language to say "cond() is a property" then this issue would not be silently introduced when changing cond from a variable to a method.

Oh how I am with you on this. I've always thought mentioning a delegate name should NOT EVER evaluate the delegate. Walter definitely took the wrong turn down that alley there. And guess what. He got ambushed by the "lazy" keyword right there. I told Walter to not do that "lazy" keyword, he disregarded, but the time will come when that stone will be turned. Andrei

Hold it right there! Are you saying that the existence of 'lazy' somehow required, or helped justify the existence of the omittable parenthesis functionality? How is that? I think you are accusing innocents ('lazy') or wrongdoing. 'lazy' has nothing to do with the omittable parenthesis functionality, I think they are completely orthogonal. Prove me wrong otherwise.

I think that he meant that lazy automatically evaluates a delegate, and that he thinks it is wrong. He said it because he thought that the problem shown by Sean was connected with automatically evaluating a delegate, which it is not, but let's not start another sub-thread... Fawzi
Oct 03 2008
prev sibling parent reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
Bruno Medeiros wrote:
 Hold it right there! Are you saying that the existence of 'lazy' somehow 
 required, or helped justify the existence of the omittable parenthesis 
 functionality? How is that?
 I think you are accusing innocents ('lazy') or wrongdoing. 'lazy' has 
 nothing to do with the omittable parenthesis functionality, I think they 
 are completely orthogonal. Prove me wrong otherwise.

The connection is indirect. Lazy showed that omitting trailing parens after delegate names is not recommended. If trailing parens after delegate names were required, much of the ambiguity mentioned by Sergey would disappear. Andrei
Oct 03 2008
parent Bruno Medeiros <brunodomedeiros+spam com.gmail> writes:
Andrei Alexandrescu wrote:
 Bruno Medeiros wrote:
 Hold it right there! Are you saying that the existence of 'lazy' 
 somehow required, or helped justify the existence of the omittable 
 parenthesis functionality? How is that?
 I think you are accusing innocents ('lazy') or wrongdoing. 'lazy' has 
 nothing to do with the omittable parenthesis functionality, I think 
 they are completely orthogonal. Prove me wrong otherwise.

The connection is indirect. Lazy showed that omitting trailing parens after delegate names is not recommended. If trailing parens after delegate names were required, much of the ambiguity mentioned by Sergey would disappear. Andrei

So your point before was, that you are criticizing lazy because it shows/recommends that trailing parenthesis are not ommited? Then you realize that such is only considered a disadvantage to those that think trailing parenthesis should be omitted in the first place... -- Bruno Medeiros - Software Developer, MSc. in CS/E graduate http://www.prowiki.org/wiki4d/wiki.cgi?BrunoMedeiros#D
Oct 06 2008
prev sibling parent reply Fawzi Mohamed <fmohamed mac.com> writes:
On 2008-10-02 21:13:16 +0200, Andrei Alexandrescu 
<SeeWebsiteForEmail erdani.org> said:

 Sean Kelly wrote:
 == Quote from Andrei Alexandrescu (SeeWebsiteForEmail erdani.org)'s article
 Bruno Medeiros wrote:
 I don't understand that. You stated "It violates economy of syntax.", so
  it violates your Principle 1 of language design. So how can you be
 happy with that (apart from the assignment issue)? Does fixing this
 problem violate some other principle or concern of yours?


I apologize if this has been brought up before, but I'm not sure that simplicity is a good thing if it can result in unpredictable behavior. For example: import std.stdio; class C { this( bool delegate() p ) { version( a ) cond = p; version( b ) ptr = p; } version( a ) { bool delegate() cond; } version( b ) { bool delegate() ptr; bool delegate() cond() { return ptr; } } } void main() { bool test() { writefln( "hi" ); return false; } auto c = new C( &test ); if( c.cond() ) writefln( "true" ); } Running version=a prints "hi" while version=b prints "true." If there were some way in the language to say "cond() is a property" then this issue would not be silently introduced when changing cond from a variable to a method.

Oh how I am with you on this. I've always thought mentioning a delegate name should NOT EVER evaluate the delegate.

I think here the problem is different c.cond() in version a evaluates the delegate in version b c.cond() is interpreted as the the omittable parenthesis of the accessor method, and returns the delegate. As (as you want, and I think is reasonable) the delegate is not evaluated. In if it is then interpreted as a logical and gives true (as it is a valid delegate, not null). c.cond()() would give false. So actually automatically evaluating the delegate would actually fix this particular problem (but would indeed introduce many other problems, like how to then get the delegate).
 Walter definitely took the wrong turn down that alley there. And guess 
 what. He got ambushed by the "lazy" keyword right there. I told Walter 
 to not do that "lazy" keyword, he disregarded, but the time will come 
 when that stone will be turned.
 
 
 Andrei

Oct 03 2008
parent Michel Fortin <michel.fortin michelf.com> writes:
On 2008-10-03 06:27:24 -0400, Fawzi Mohamed <fmohamed mac.com> said:

 So actually automatically evaluating the delegate would actually fix 
 this particular problem (but would indeed introduce many other 
 problems, like how to then get the delegate).

Hum... it could work a little like references (&) in C++. Basically, if you assign a lazy value to another lazy value, it just copy the delegate. Obviously, you'd need to allow "lazy" as a type modifier everywhere, in the same sense as "ref" could (will?) be made a type modifier acceptable everywhere. void func(lazy int x) { lazy int y = x; // assigns x delegate to y. y; // calls y delegate, which is the same as x. x; // calls x delegate. } We could even extend the concept to create "lazy values", which could work as getters for properties, and disallow parenthesis for them: lazy int func { return 1; } lazy int delegate() deleg { return &func; } int i = func; // calls func. int j = func(); // error, func is lazy and cannot be called with parenthesis. int k = deleg(); // calls deleg(), then evaluate the returned delegate. lazy int x = func; // assigns &func to x. lazy int delegate() y = deleg; // assigns &deleg to y. lazy int z = deleg(); // implicitly creates a delegate for calling deleg() lazily. Doesn't this generalize well? I'm just not sure how to extend this to a syntax for setters though. Ideas? -- Michel Fortin michel.fortin michelf.com http://michelf.com/
Oct 03 2008
prev sibling parent reply Yigal Chripun <yigal100 gmail.com> writes:
Benji Smith wrote:
 Andrei Alexandrescu wrote:
 I stated two principles of language design. They could be true or
 false. They are up there for debate. They are subjective, because
 aside from some basics, language design is subjective.

 The principles are:

 1) A language should minimize the number of syntactic constructs that
 are semantically and/or pragmatically meaningless.

 2) The more frequently-used constructs should be given syntactic
 priority over the less-used constructs, particularly when the latter
 are also at risk of breaking the first principle.

I'd like to propose another principle of language design: 3) Consistency -- The expression of a semantic construct should always use the same syntax. Likewise, multiple uses of the same syntactic constructs should always result in the same semantics. Based on that principle, I'd argue that function-calling should either always use parentheses, or it should never use parentheses. Requiring parentheses for some function calls, but not for others violates the principle of consistency. In my prioritization of language-design principles, consistency is more important then syntactic economy. Based on those principles, I believe that the parentheses should be mandatory for all function calls. --benji

I was about to post a very similar post (it's still in my drafts folder) besides fully agreeing with the above, I'll add the following: operator & in D is not consistent. int x; auto y = &x; // y is int* (i.e pointer to int) int func(int); auto f = &func; // consistent with the above: // f's type is - int function(int); i.e function pointer. let's try a class: class Test { int x; int func(int); } auto test = new Test(); auto y = &test.x; // consistent with the above auto g = &test.func; // this is inconsistent! in the above g is a delegate which is not a function pointer. just as int* is different from int[]. you wouldn't expect to get an int[] in the above, would you? a more consistent approach would be to have: - func and obj.method to be treated as delegates, of course for function pointers there should be an implicit cast to delegate. - &func and &obj.method would be strictly function pointers. - using parentheses is mandatory for function calls. IMHO, current low usage of delegates is not a reason to keep the current syntax but rather a result of the current syntax. --Yigal
Oct 01 2008
next sibling parent reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
Yigal Chripun wrote:
 Benji Smith wrote:
 Andrei Alexandrescu wrote:
 I stated two principles of language design. They could be true or
 false. They are up there for debate. They are subjective, because
 aside from some basics, language design is subjective.

 The principles are:

 1) A language should minimize the number of syntactic constructs that
 are semantically and/or pragmatically meaningless.

 2) The more frequently-used constructs should be given syntactic
 priority over the less-used constructs, particularly when the latter
 are also at risk of breaking the first principle.

3) Consistency -- The expression of a semantic construct should always use the same syntax. Likewise, multiple uses of the same syntactic constructs should always result in the same semantics. Based on that principle, I'd argue that function-calling should either always use parentheses, or it should never use parentheses. Requiring parentheses for some function calls, but not for others violates the principle of consistency. In my prioritization of language-design principles, consistency is more important then syntactic economy. Based on those principles, I believe that the parentheses should be mandatory for all function calls. --benji

I was about to post a very similar post (it's still in my drafts folder) besides fully agreeing with the above, I'll add the following: operator & in D is not consistent. int x; auto y = &x; // y is int* (i.e pointer to int) int func(int); auto f = &func; // consistent with the above: // f's type is - int function(int); i.e function pointer. let's try a class: class Test { int x; int func(int); } auto test = new Test(); auto y = &test.x; // consistent with the above auto g = &test.func; // this is inconsistent! in the above g is a delegate which is not a function pointer. just as int* is different from int[]. you wouldn't expect to get an int[] in the above, would you? a more consistent approach would be to have: - func and obj.method to be treated as delegates, of course for function pointers there should be an implicit cast to delegate. - &func and &obj.method would be strictly function pointers. - using parentheses is mandatory for function calls.

A delegate /is/ a function pointer, more precisely, a pointer to function packed with a pointer to a value. This also seems to reveal a lack of understanding of how class methods work. What function would you make &obj.method point to? Andrei
Oct 01 2008
parent reply Yigal Chripun <yigal100 gmail.com> writes:
Andrei Alexandrescu wrote:
 Yigal Chripun wrote:
 Benji Smith wrote:
 Andrei Alexandrescu wrote:
 I stated two principles of language design. They could be true or
 false. They are up there for debate. They are subjective, because
 aside from some basics, language design is subjective.

 The principles are:

 1) A language should minimize the number of syntactic constructs that
 are semantically and/or pragmatically meaningless.

 2) The more frequently-used constructs should be given syntactic
 priority over the less-used constructs, particularly when the latter
 are also at risk of breaking the first principle.

3) Consistency -- The expression of a semantic construct should always use the same syntax. Likewise, multiple uses of the same syntactic constructs should always result in the same semantics. Based on that principle, I'd argue that function-calling should either always use parentheses, or it should never use parentheses. Requiring parentheses for some function calls, but not for others violates the principle of consistency. In my prioritization of language-design principles, consistency is more important then syntactic economy. Based on those principles, I believe that the parentheses should be mandatory for all function calls. --benji

I was about to post a very similar post (it's still in my drafts folder) besides fully agreeing with the above, I'll add the following: operator & in D is not consistent. int x; auto y = &x; // y is int* (i.e pointer to int) int func(int); auto f = &func; // consistent with the above: // f's type is - int function(int); i.e function pointer. let's try a class: class Test { int x; int func(int); } auto test = new Test(); auto y = &test.x; // consistent with the above auto g = &test.func; // this is inconsistent! in the above g is a delegate which is not a function pointer. just as int* is different from int[]. you wouldn't expect to get an int[] in the above, would you? a more consistent approach would be to have: - func and obj.method to be treated as delegates, of course for function pointers there should be an implicit cast to delegate. - &func and &obj.method would be strictly function pointers. - using parentheses is mandatory for function calls.

A delegate /is/ a function pointer, more precisely, a pointer to function packed with a pointer to a value. This also seems to reveal a lack of understanding of how class methods work. What function would you make &obj.method point to? Andrei

I understand a delegate as a struct of two pointers as you said so yourself, above. an int[] != int* and so does a delegate != [C like] function pointer. I'd make &obj.method to be the function pointer stored in the delegate. so if a delegate contains two pointers (function pointer and context pointer) I'd take the first with & and the other with a property of the delegate as a possible syntax. a delegate should have both a ptr and a context properties. this can be used for example when calling C code. just like you can have a D array and call a C function with C_func(array.ptr, array.length); C_function_which_calls_d_delegate(dg.ptr, dg.context); OR C_function_which_calls_d_delegate(&dg, dg.context); in D code you'd probably use the delegate form more often than the function pointer, at least for regular application coding stuff.
Oct 01 2008
parent reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
Yigal Chripun wrote:
 Andrei Alexandrescu wrote:
 Yigal Chripun wrote:
 Benji Smith wrote:
 Andrei Alexandrescu wrote:
 I stated two principles of language design. They could be true or
 false. They are up there for debate. They are subjective, because
 aside from some basics, language design is subjective.

 The principles are:

 1) A language should minimize the number of syntactic constructs that
 are semantically and/or pragmatically meaningless.

 2) The more frequently-used constructs should be given syntactic
 priority over the less-used constructs, particularly when the latter
 are also at risk of breaking the first principle.

3) Consistency -- The expression of a semantic construct should always use the same syntax. Likewise, multiple uses of the same syntactic constructs should always result in the same semantics. Based on that principle, I'd argue that function-calling should either always use parentheses, or it should never use parentheses. Requiring parentheses for some function calls, but not for others violates the principle of consistency. In my prioritization of language-design principles, consistency is more important then syntactic economy. Based on those principles, I believe that the parentheses should be mandatory for all function calls. --benji

besides fully agreeing with the above, I'll add the following: operator & in D is not consistent. int x; auto y = &x; // y is int* (i.e pointer to int) int func(int); auto f = &func; // consistent with the above: // f's type is - int function(int); i.e function pointer. let's try a class: class Test { int x; int func(int); } auto test = new Test(); auto y = &test.x; // consistent with the above auto g = &test.func; // this is inconsistent! in the above g is a delegate which is not a function pointer. just as int* is different from int[]. you wouldn't expect to get an int[] in the above, would you? a more consistent approach would be to have: - func and obj.method to be treated as delegates, of course for function pointers there should be an implicit cast to delegate. - &func and &obj.method would be strictly function pointers. - using parentheses is mandatory for function calls.

function packed with a pointer to a value. This also seems to reveal a lack of understanding of how class methods work. What function would you make &obj.method point to? Andrei

I understand a delegate as a struct of two pointers as you said so yourself, above. an int[] != int* and so does a delegate != [C like] function pointer. I'd make &obj.method to be the function pointer stored in the delegate.

What is the signature of that function? Andrei
Oct 01 2008
parent reply Yigal Chripun <yigal100 gmail.com> writes:
Andrei Alexandrescu wrote:
 Yigal Chripun wrote:
 Andrei Alexandrescu wrote:
 Yigal Chripun wrote:
 Benji Smith wrote:
 Andrei Alexandrescu wrote:
 I stated two principles of language design. They could be true or
 false. They are up there for debate. They are subjective, because
 aside from some basics, language design is subjective.

 The principles are:

 1) A language should minimize the number of syntactic constructs that
 are semantically and/or pragmatically meaningless.

 2) The more frequently-used constructs should be given syntactic
 priority over the less-used constructs, particularly when the latter
 are also at risk of breaking the first principle.

3) Consistency -- The expression of a semantic construct should always use the same syntax. Likewise, multiple uses of the same syntactic constructs should always result in the same semantics. Based on that principle, I'd argue that function-calling should either always use parentheses, or it should never use parentheses. Requiring parentheses for some function calls, but not for others violates the principle of consistency. In my prioritization of language-design principles, consistency is more important then syntactic economy. Based on those principles, I believe that the parentheses should be mandatory for all function calls. --benji

folder) besides fully agreeing with the above, I'll add the following: operator & in D is not consistent. int x; auto y = &x; // y is int* (i.e pointer to int) int func(int); auto f = &func; // consistent with the above: // f's type is - int function(int); i.e function pointer. let's try a class: class Test { int x; int func(int); } auto test = new Test(); auto y = &test.x; // consistent with the above auto g = &test.func; // this is inconsistent! in the above g is a delegate which is not a function pointer. just as int* is different from int[]. you wouldn't expect to get an int[] in the above, would you? a more consistent approach would be to have: - func and obj.method to be treated as delegates, of course for function pointers there should be an implicit cast to delegate. - &func and &obj.method would be strictly function pointers. - using parentheses is mandatory for function calls.

function packed with a pointer to a value. This also seems to reveal a lack of understanding of how class methods work. What function would you make &obj.method point to? Andrei

I understand a delegate as a struct of two pointers as you said so yourself, above. an int[] != int* and so does a delegate != [C like] function pointer. I'd make &obj.method to be the function pointer stored in the delegate.

What is the signature of that function? Andrei

say we have: class Test { int method(int); } auto obj = new Test(); auto fPtr = &obj.method; type of fPtr would be: int function(int); or in C syntax: int (*)(int); are you asking because of C++ pointer-to-member-function types? in C++: int (Test::*)(int); ? You know C++ much better than most of us here (certainly better than me), in your experience, is that distinction useful?
Oct 01 2008
parent reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
Yigal Chripun wrote:
 Andrei Alexandrescu wrote:
 Yigal Chripun wrote:
 Andrei Alexandrescu wrote:
 Yigal Chripun wrote:
 Benji Smith wrote:
 Andrei Alexandrescu wrote:
 I stated two principles of language design. They could be true or
 false. They are up there for debate. They are subjective, because
 aside from some basics, language design is subjective.

 The principles are:

 1) A language should minimize the number of syntactic constructs that
 are semantically and/or pragmatically meaningless.

 2) The more frequently-used constructs should be given syntactic
 priority over the less-used constructs, particularly when the latter
 are also at risk of breaking the first principle.

3) Consistency -- The expression of a semantic construct should always use the same syntax. Likewise, multiple uses of the same syntactic constructs should always result in the same semantics. Based on that principle, I'd argue that function-calling should either always use parentheses, or it should never use parentheses. Requiring parentheses for some function calls, but not for others violates the principle of consistency. In my prioritization of language-design principles, consistency is more important then syntactic economy. Based on those principles, I believe that the parentheses should be mandatory for all function calls. --benji

folder) besides fully agreeing with the above, I'll add the following: operator & in D is not consistent. int x; auto y = &x; // y is int* (i.e pointer to int) int func(int); auto f = &func; // consistent with the above: // f's type is - int function(int); i.e function pointer. let's try a class: class Test { int x; int func(int); } auto test = new Test(); auto y = &test.x; // consistent with the above auto g = &test.func; // this is inconsistent! in the above g is a delegate which is not a function pointer. just as int* is different from int[]. you wouldn't expect to get an int[] in the above, would you? a more consistent approach would be to have: - func and obj.method to be treated as delegates, of course for function pointers there should be an implicit cast to delegate. - &func and &obj.method would be strictly function pointers. - using parentheses is mandatory for function calls.

function packed with a pointer to a value. This also seems to reveal a lack of understanding of how class methods work. What function would you make &obj.method point to? Andrei

yourself, above. an int[] != int* and so does a delegate != [C like] function pointer. I'd make &obj.method to be the function pointer stored in the delegate.

Andrei

say we have: class Test { int method(int); } auto obj = new Test(); auto fPtr = &obj.method; type of fPtr would be: int function(int); or in C syntax: int (*)(int);

That doesn't stay glued. What is "this" inside the function?
 are you asking because of C++ pointer-to-member-function types?
 in C++: int (Test::*)(int); ?
 
 You know C++ much better than most of us here (certainly better than
 me), in your experience, is that distinction useful?

C++ pointers to member functions aren't that well designed. But at least they work :o|. Andrei
Oct 01 2008
parent reply Yigal Chripun <yigal100 gmail.com> writes:
Andrei Alexandrescu wrote:
 Yigal Chripun wrote:
 Andrei Alexandrescu wrote:
 Yigal Chripun wrote:
 Andrei Alexandrescu wrote:
 Yigal Chripun wrote:
 Benji Smith wrote:
 Andrei Alexandrescu wrote:
 I stated two principles of language design. They could be true or
 false. They are up there for debate. They are subjective, because
 aside from some basics, language design is subjective.

 The principles are:

 1) A language should minimize the number of syntactic constructs
 that
 are semantically and/or pragmatically meaningless.

 2) The more frequently-used constructs should be given syntactic
 priority over the less-used constructs, particularly when the
 latter
 are also at risk of breaking the first principle.

3) Consistency -- The expression of a semantic construct should always use the same syntax. Likewise, multiple uses of the same syntactic constructs should always result in the same semantics. Based on that principle, I'd argue that function-calling should either always use parentheses, or it should never use parentheses. Requiring parentheses for some function calls, but not for others violates the principle of consistency. In my prioritization of language-design principles, consistency is more important then syntactic economy. Based on those principles, I believe that the parentheses should be mandatory for all function calls. --benji

folder) besides fully agreeing with the above, I'll add the following: operator & in D is not consistent. int x; auto y = &x; // y is int* (i.e pointer to int) int func(int); auto f = &func; // consistent with the above: // f's type is - int function(int); i.e function pointer. let's try a class: class Test { int x; int func(int); } auto test = new Test(); auto y = &test.x; // consistent with the above auto g = &test.func; // this is inconsistent! in the above g is a delegate which is not a function pointer. just as int* is different from int[]. you wouldn't expect to get an int[] in the above, would you? a more consistent approach would be to have: - func and obj.method to be treated as delegates, of course for function pointers there should be an implicit cast to delegate. - &func and &obj.method would be strictly function pointers. - using parentheses is mandatory for function calls.

function packed with a pointer to a value. This also seems to reveal a lack of understanding of how class methods work. What function would you make &obj.method point to? Andrei

yourself, above. an int[] != int* and so does a delegate != [C like] function pointer. I'd make &obj.method to be the function pointer stored in the delegate.

Andrei

say we have: class Test { int method(int); } auto obj = new Test(); auto fPtr = &obj.method; type of fPtr would be: int function(int); or in C syntax: int (*)(int);

That doesn't stay glued. What is "this" inside the function?
 are you asking because of C++ pointer-to-member-function types?
 in C++: int (Test::*)(int); ?

 You know C++ much better than most of us here (certainly better than
 me), in your experience, is that distinction useful?

C++ pointers to member functions aren't that well designed. But at least they work :o|. Andrei

if I understand it correctly, in C++ the type of this is encoded in the signature (as above) and you use obj.*pointer(..); to call the function. this is a possible solution (using the C++ pointer to member function). however, maybe we should use a more python like solution: the signature becomes: int function(Test, int); and this refers to the first argument (just like you pass the "self" in Python). I think i prefer the second option more [ int function(Test, int) ] and it will be easier to use in C. -- Yigal
Oct 01 2008
parent reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
Yigal Chripun wrote:
 Andrei Alexandrescu wrote:
 Yigal Chripun wrote:
 Andrei Alexandrescu wrote:
 Yigal Chripun wrote:
 Andrei Alexandrescu wrote:
 Yigal Chripun wrote:
 Benji Smith wrote:
 Andrei Alexandrescu wrote:
 I stated two principles of language design. They could be true or
 false. They are up there for debate. They are subjective, because
 aside from some basics, language design is subjective.

 The principles are:

 1) A language should minimize the number of syntactic constructs
 that
 are semantically and/or pragmatically meaningless.

 2) The more frequently-used constructs should be given syntactic
 priority over the less-used constructs, particularly when the
 latter
 are also at risk of breaking the first principle.

3) Consistency -- The expression of a semantic construct should always use the same syntax. Likewise, multiple uses of the same syntactic constructs should always result in the same semantics. Based on that principle, I'd argue that function-calling should either always use parentheses, or it should never use parentheses. Requiring parentheses for some function calls, but not for others violates the principle of consistency. In my prioritization of language-design principles, consistency is more important then syntactic economy. Based on those principles, I believe that the parentheses should be mandatory for all function calls. --benji

folder) besides fully agreeing with the above, I'll add the following: operator & in D is not consistent. int x; auto y = &x; // y is int* (i.e pointer to int) int func(int); auto f = &func; // consistent with the above: // f's type is - int function(int); i.e function pointer. let's try a class: class Test { int x; int func(int); } auto test = new Test(); auto y = &test.x; // consistent with the above auto g = &test.func; // this is inconsistent! in the above g is a delegate which is not a function pointer. just as int* is different from int[]. you wouldn't expect to get an int[] in the above, would you? a more consistent approach would be to have: - func and obj.method to be treated as delegates, of course for function pointers there should be an implicit cast to delegate. - &func and &obj.method would be strictly function pointers. - using parentheses is mandatory for function calls.

function packed with a pointer to a value. This also seems to reveal a lack of understanding of how class methods work. What function would you make &obj.method point to? Andrei

yourself, above. an int[] != int* and so does a delegate != [C like] function pointer. I'd make &obj.method to be the function pointer stored in the delegate.

Andrei

class Test { int method(int); } auto obj = new Test(); auto fPtr = &obj.method; type of fPtr would be: int function(int); or in C syntax: int (*)(int);

 are you asking because of C++ pointer-to-member-function types?
 in C++: int (Test::*)(int); ?

 You know C++ much better than most of us here (certainly better than
 me), in your experience, is that distinction useful?

they work :o|. Andrei

if I understand it correctly, in C++ the type of this is encoded in the signature (as above) and you use obj.*pointer(..); to call the function. this is a possible solution (using the C++ pointer to member function). however, maybe we should use a more python like solution: the signature becomes: int function(Test, int); and this refers to the first argument (just like you pass the "self" in Python). I think i prefer the second option more [ int function(Test, int) ] and it will be easier to use in C.

It's great that you are now equipped to appreciate the advantages and disadvantages of various tradeoffs. Maybe a reevaluation of the existing solution would be useful. Andrei
Oct 01 2008
parent reply Yigal Chripun <yigal100 gmail.com> writes:
Andrei Alexandrescu wrote:
 Yigal Chripun wrote:
 Andrei Alexandrescu wrote:
 Yigal Chripun wrote:
 Andrei Alexandrescu wrote:
 Yigal Chripun wrote:
 Andrei Alexandrescu wrote:
 Yigal Chripun wrote:
 Benji Smith wrote:
 Andrei Alexandrescu wrote:
 I stated two principles of language design. They could be true or
 false. They are up there for debate. They are subjective, because
 aside from some basics, language design is subjective.

 The principles are:

 1) A language should minimize the number of syntactic constructs
 that
 are semantically and/or pragmatically meaningless.

 2) The more frequently-used constructs should be given syntactic
 priority over the less-used constructs, particularly when the
 latter
 are also at risk of breaking the first principle.

3) Consistency -- The expression of a semantic construct should always use the same syntax. Likewise, multiple uses of the same syntactic constructs should always result in the same semantics. Based on that principle, I'd argue that function-calling should either always use parentheses, or it should never use parentheses. Requiring parentheses for some function calls, but not for others violates the principle of consistency. In my prioritization of language-design principles, consistency is more important then syntactic economy. Based on those principles, I believe that the parentheses should be mandatory for all function calls. --benji

folder) besides fully agreeing with the above, I'll add the following: operator & in D is not consistent. int x; auto y = &x; // y is int* (i.e pointer to int) int func(int); auto f = &func; // consistent with the above: // f's type is - int function(int); i.e function pointer. let's try a class: class Test { int x; int func(int); } auto test = new Test(); auto y = &test.x; // consistent with the above auto g = &test.func; // this is inconsistent! in the above g is a delegate which is not a function pointer. just as int* is different from int[]. you wouldn't expect to get an int[] in the above, would you? a more consistent approach would be to have: - func and obj.method to be treated as delegates, of course for function pointers there should be an implicit cast to delegate. - &func and &obj.method would be strictly function pointers. - using parentheses is mandatory for function calls.

function packed with a pointer to a value. This also seems to reveal a lack of understanding of how class methods work. What function would you make &obj.method point to? Andrei

yourself, above. an int[] != int* and so does a delegate != [C like] function pointer. I'd make &obj.method to be the function pointer stored in the delegate.

Andrei

class Test { int method(int); } auto obj = new Test(); auto fPtr = &obj.method; type of fPtr would be: int function(int); or in C syntax: int (*)(int);

 are you asking because of C++ pointer-to-member-function types?
 in C++: int (Test::*)(int); ?

 You know C++ much better than most of us here (certainly better than
 me), in your experience, is that distinction useful?

they work :o|. Andrei

if I understand it correctly, in C++ the type of this is encoded in the signature (as above) and you use obj.*pointer(..); to call the function. this is a possible solution (using the C++ pointer to member function). however, maybe we should use a more python like solution: the signature becomes: int function(Test, int); and this refers to the first argument (just like you pass the "self" in Python). I think i prefer the second option more [ int function(Test, int) ] and it will be easier to use in C.

It's great that you are now equipped to appreciate the advantages and disadvantages of various tradeoffs. Maybe a reevaluation of the existing solution would be useful. Andrei

my main issue was with the syntax. and I still think it's inconsistent. the only drawback I see with my suggestion is the need to implement this additional feature with method pointers. it adds consistency for the common case (delegates) without limiting any of the current uses. What other cons do you see for my suggestion besides the difficulty to implement?
Oct 01 2008
parent reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
Yigal Chripun wrote:
 Andrei Alexandrescu wrote:
 Yigal Chripun wrote:
 Andrei Alexandrescu wrote:
 Yigal Chripun wrote:
 Andrei Alexandrescu wrote:
 Yigal Chripun wrote:
 Andrei Alexandrescu wrote:
 Yigal Chripun wrote:
 Benji Smith wrote:
 Andrei Alexandrescu wrote:
 I stated two principles of language design. They could be true or
 false. They are up there for debate. They are subjective, because
 aside from some basics, language design is subjective.

 The principles are:

 1) A language should minimize the number of syntactic constructs
 that
 are semantically and/or pragmatically meaningless.

 2) The more frequently-used constructs should be given syntactic
 priority over the less-used constructs, particularly when the
 latter
 are also at risk of breaking the first principle.

3) Consistency -- The expression of a semantic construct should always use the same syntax. Likewise, multiple uses of the same syntactic constructs should always result in the same semantics. Based on that principle, I'd argue that function-calling should either always use parentheses, or it should never use parentheses. Requiring parentheses for some function calls, but not for others violates the principle of consistency. In my prioritization of language-design principles, consistency is more important then syntactic economy. Based on those principles, I believe that the parentheses should be mandatory for all function calls. --benji

folder) besides fully agreeing with the above, I'll add the following: operator & in D is not consistent. int x; auto y = &x; // y is int* (i.e pointer to int) int func(int); auto f = &func; // consistent with the above: // f's type is - int function(int); i.e function pointer. let's try a class: class Test { int x; int func(int); } auto test = new Test(); auto y = &test.x; // consistent with the above auto g = &test.func; // this is inconsistent! in the above g is a delegate which is not a function pointer. just as int* is different from int[]. you wouldn't expect to get an int[] in the above, would you? a more consistent approach would be to have: - func and obj.method to be treated as delegates, of course for function pointers there should be an implicit cast to delegate. - &func and &obj.method would be strictly function pointers. - using parentheses is mandatory for function calls.

function packed with a pointer to a value. This also seems to reveal a lack of understanding of how class methods work. What function would you make &obj.method point to? Andrei

yourself, above. an int[] != int* and so does a delegate != [C like] function pointer. I'd make &obj.method to be the function pointer stored in the delegate.

Andrei

class Test { int method(int); } auto obj = new Test(); auto fPtr = &obj.method; type of fPtr would be: int function(int); or in C syntax: int (*)(int);

 are you asking because of C++ pointer-to-member-function types?
 in C++: int (Test::*)(int); ?

 You know C++ much better than most of us here (certainly better than
 me), in your experience, is that distinction useful?

they work :o|. Andrei

signature (as above) and you use obj.*pointer(..); to call the function. this is a possible solution (using the C++ pointer to member function). however, maybe we should use a more python like solution: the signature becomes: int function(Test, int); and this refers to the first argument (just like you pass the "self" in Python). I think i prefer the second option more [ int function(Test, int) ] and it will be easier to use in C.

disadvantages of various tradeoffs. Maybe a reevaluation of the existing solution would be useful. Andrei

my main issue was with the syntax. and I still think it's inconsistent. the only drawback I see with my suggestion is the need to implement this additional feature with method pointers. it adds consistency for the common case (delegates) without limiting any of the current uses. What other cons do you see for my suggestion besides the difficulty to implement?

I think we're better off with the current state of affairs. Andrei
Oct 01 2008
parent reply Yigal Chripun <yigal100 gmail.com> writes:
Andrei Alexandrescu wrote:
 Yigal Chripun wrote:
 Andrei Alexandrescu wrote:
 Yigal Chripun wrote:
 Andrei Alexandrescu wrote:
 Yigal Chripun wrote:
 Andrei Alexandrescu wrote:
 Yigal Chripun wrote:
 Andrei Alexandrescu wrote:
 Yigal Chripun wrote:
 Benji Smith wrote:
 Andrei Alexandrescu wrote:
 I stated two principles of language design. They could be
 true or
 false. They are up there for debate. They are subjective,
 because
 aside from some basics, language design is subjective.

 The principles are:

 1) A language should minimize the number of syntactic
 constructs
 that
 are semantically and/or pragmatically meaningless.

 2) The more frequently-used constructs should be given
 syntactic
 priority over the less-used constructs, particularly when the
 latter
 are also at risk of breaking the first principle.

3) Consistency -- The expression of a semantic construct should always use the same syntax. Likewise, multiple uses of the same syntactic constructs should always result in the same semantics. Based on that principle, I'd argue that function-calling should either always use parentheses, or it should never use parentheses. Requiring parentheses for some function calls, but not for others violates the principle of consistency. In my prioritization of language-design principles, consistency is more important then syntactic economy. Based on those principles, I believe that the parentheses should be mandatory for all function calls. --benji

folder) besides fully agreeing with the above, I'll add the following: operator & in D is not consistent. int x; auto y = &x; // y is int* (i.e pointer to int) int func(int); auto f = &func; // consistent with the above: // f's type is - int function(int); i.e function pointer. let's try a class: class Test { int x; int func(int); } auto test = new Test(); auto y = &test.x; // consistent with the above auto g = &test.func; // this is inconsistent! in the above g is a delegate which is not a function pointer. just as int* is different from int[]. you wouldn't expect to get an int[] in the above, would you? a more consistent approach would be to have: - func and obj.method to be treated as delegates, of course for function pointers there should be an implicit cast to delegate. - &func and &obj.method would be strictly function pointers. - using parentheses is mandatory for function calls.

function packed with a pointer to a value. This also seems to reveal a lack of understanding of how class methods work. What function would you make &obj.method point to? Andrei

yourself, above. an int[] != int* and so does a delegate != [C like] function pointer. I'd make &obj.method to be the function pointer stored in the delegate.

Andrei

class Test { int method(int); } auto obj = new Test(); auto fPtr = &obj.method; type of fPtr would be: int function(int); or in C syntax: int (*)(int);

 are you asking because of C++ pointer-to-member-function types?
 in C++: int (Test::*)(int); ?

 You know C++ much better than most of us here (certainly better than
 me), in your experience, is that distinction useful?

least they work :o|. Andrei

signature (as above) and you use obj.*pointer(..); to call the function. this is a possible solution (using the C++ pointer to member function). however, maybe we should use a more python like solution: the signature becomes: int function(Test, int); and this refers to the first argument (just like you pass the "self" in Python). I think i prefer the second option more [ int function(Test, int) ] and it will be easier to use in C.

disadvantages of various tradeoffs. Maybe a reevaluation of the existing solution would be useful. Andrei

my main issue was with the syntax. and I still think it's inconsistent. the only drawback I see with my suggestion is the need to implement this additional feature with method pointers. it adds consistency for the common case (delegates) without limiting any of the current uses. What other cons do you see for my suggestion besides the difficulty to implement?

I think we're better off with the current state of affairs. Andrei

so if I understand you correctly, you don't see any other disadvantages. so it all comes to whether it's worth the time to implement this, on which we appear to disagree. since I'm unable to convince you that [from my POV as a user] it's worth it, I won't write more about it. Thank you for your time discussing this issue. Yigal
Oct 01 2008
parent superdan <super dan.org> writes:
Yigal Chripun Wrote:

 Andrei Alexandrescu wrote:
 Yigal Chripun wrote:
 Andrei Alexandrescu wrote:
 Yigal Chripun wrote:
 Andrei Alexandrescu wrote:
 Yigal Chripun wrote:
 Andrei Alexandrescu wrote:
 Yigal Chripun wrote:
 Andrei Alexandrescu wrote:
 Yigal Chripun wrote:
 Benji Smith wrote:
 Andrei Alexandrescu wrote:
 I stated two principles of language design. They could be
 true or
 false. They are up there for debate. They are subjective,
 because
 aside from some basics, language design is subjective.

 The principles are:

 1) A language should minimize the number of syntactic
 constructs
 that
 are semantically and/or pragmatically meaningless.

 2) The more frequently-used constructs should be given
 syntactic
 priority over the less-used constructs, particularly when the
 latter
 are also at risk of breaking the first principle.

3) Consistency -- The expression of a semantic construct should always use the same syntax. Likewise, multiple uses of the same syntactic constructs should always result in the same semantics. Based on that principle, I'd argue that function-calling should either always use parentheses, or it should never use parentheses. Requiring parentheses for some function calls, but not for others violates the principle of consistency. In my prioritization of language-design principles, consistency is more important then syntactic economy. Based on those principles, I believe that the parentheses should be mandatory for all function calls. --benji

folder) besides fully agreeing with the above, I'll add the following: operator & in D is not consistent. int x; auto y = &x; // y is int* (i.e pointer to int) int func(int); auto f = &func; // consistent with the above: // f's type is - int function(int); i.e function pointer. let's try a class: class Test { int x; int func(int); } auto test = new Test(); auto y = &test.x; // consistent with the above auto g = &test.func; // this is inconsistent! in the above g is a delegate which is not a function pointer. just as int* is different from int[]. you wouldn't expect to get an int[] in the above, would you? a more consistent approach would be to have: - func and obj.method to be treated as delegates, of course for function pointers there should be an implicit cast to delegate. - &func and &obj.method would be strictly function pointers. - using parentheses is mandatory for function calls.

function packed with a pointer to a value. This also seems to reveal a lack of understanding of how class methods work. What function would you make &obj.method point to? Andrei

yourself, above. an int[] != int* and so does a delegate != [C like] function pointer. I'd make &obj.method to be the function pointer stored in the delegate.

Andrei

class Test { int method(int); } auto obj = new Test(); auto fPtr = &obj.method; type of fPtr would be: int function(int); or in C syntax: int (*)(int);

 are you asking because of C++ pointer-to-member-function types?
 in C++: int (Test::*)(int); ?

 You know C++ much better than most of us here (certainly better than
 me), in your experience, is that distinction useful?

least they work :o|. Andrei

signature (as above) and you use obj.*pointer(..); to call the function. this is a possible solution (using the C++ pointer to member function). however, maybe we should use a more python like solution: the signature becomes: int function(Test, int); and this refers to the first argument (just like you pass the "self" in Python). I think i prefer the second option more [ int function(Test, int) ] and it will be easier to use in C.

disadvantages of various tradeoffs. Maybe a reevaluation of the existing solution would be useful. Andrei

my main issue was with the syntax. and I still think it's inconsistent. the only drawback I see with my suggestion is the need to implement this additional feature with method pointers. it adds consistency for the common case (delegates) without limiting any of the current uses. What other cons do you see for my suggestion besides the difficulty to implement?

I think we're better off with the current state of affairs. Andrei

so if I understand you correctly, you don't see any other disadvantages. so it all comes to whether it's worth the time to implement this, on which we appear to disagree. since I'm unable to convince you that [from my POV as a user] it's worth it, I won't write more about it. Thank you for your time discussing this issue. Yigal

way i read it he was tryin' to save you face, but of course you missed that too. just continued making a fool of urself. nothing personal dood. but really u r in a mismatch here. first u make a "proposal" that makes no sense. you can't as much put two and two together in a function sig. the man is patient kind enuff to explain. here you're wrong here you don't make sense here you're talking outta your ass. did you my friend miss a beat. hell no. no ``thank you i'll go study some more'' no ``sorry you was right i was wrong'' no ``let me correct myself'' not even effin' ``oh ok''. wtf man. r u really only mouth and no brain. then who's cocky and holding his tail up. ``so if I understand you correctly, you don't see any other disadvantages''. oxpoop. there are no advantages, that's the problem. poop.
Oct 01 2008
prev sibling parent Christopher Wright <dhasenan gmail.com> writes:
Yigal Chripun wrote:
 auto test = new Test();
 auto y = &test.x; // consistent with the above
 auto g = &test.func; // this is inconsistent!
 in the above g is a delegate which is not a function pointer.
 just as int* is different from int[]. you wouldn't expect to get an
 int[] in the above, would you?

&obj.method returns a pointer to a method for this instance. &obj.field returns a pointer to a field in this instance. If &obj.method returns a function pointer instead, &obj.field should return an OffsetTypeInfo struct. obj.field returns the value of the field. obj.method returns the result of executing obj.method(). The value of a method is either its return value or the machine code that corresponds with that method; and it's rarely the case that you intend to deal with machine code as data. Additionally, for obj.method to return machine code, D would have to support functions returning static arrays.
 a more consistent approach would be to have:
 - func and obj.method to be treated as delegates, of course for function
 pointers there should be an implicit cast to delegate.
 - &func and &obj.method would be strictly function pointers.
 - using parentheses is mandatory for function calls.

 IMHO, current low usage of delegates is not a reason to keep the current
 syntax but rather a result of the current syntax.
 
 --Yigal

I wasn't aware that delegates were poorly used in D. If I had a use for event-driven design, I'd use delegates all the time. If I did functional programming, likewise.
Oct 01 2008
prev sibling parent "Bill Baxter" <wbaxter gmail.com> writes:
 Running version=a prints "hi" while version=b prints "true."  If there
 were
 some way in the language to say "cond() is a property" then this issue
 would
 not be silently introduced when changing cond from a variable to a method.

Oh how I am with you on this. I've always thought mentioning a delegate name should NOT EVER evaluate the delegate. Walter definitely took the wrong turn down that alley there. And guess what. He got ambushed by the "lazy" keyword right there. I told Walter to not do that "lazy" keyword, he disregarded, but the time will come when that stone will be turned. Andrei

He mentions your name right there on the description of lazy, though. I always thought it was basically your idea because of that, or at least that you designed it together. Given what you are saying above, you might want to ask him politely to remove your name from that page. --bb
Oct 02 2008
prev sibling next sibling parent Walter Bright <newshound1 digitalmars.com> writes:
Andrei Alexandrescu wrote:
 In neither case is it possible for x and &x to mean the same thing. For 
 functions some really weird stuff happens:

One flag that something wacky is going is to look at the compiler code needed to implement it. In DMC, there's the nutty function arraytoptr() which is called from about 30 places in the semantic analysis. It's clearly a special case: /************************** * If e is <array of>, convert it to <pointer to>. * If e is <function>, convert it to <pointer to><function>. */ elem *arraytoptr(elem *e) { type *t; t = e->ET; if (t) { switch (tybasic(t->Tty)) { case TYarray: if (e->Eoper == OPvar && type_isvla(t)) { // It already is a pointer, so just change the type type_settype(&e->ET, newpointer(t->Tnext)); } else if (CPP || !(e->PEFflags & PEFnotlvalue)) // ANSI C 3.2.2 { type *tp = type_ptr(e,t->Tnext); tp->Talternate = t; t->Tcount++; e = el_unat(OPaddr,tp,e); } break; #if TX86 case TYnfunc: case TYnpfunc: case TYnsfunc: case TYnsysfunc: case TYfsysfunc: case TYf16func: case TYfsfunc: case TYifunc: case TYjfunc: #endif case TYffunc: case TYfpfunc: e = exp2_addr(e); break; } } return e; }
Oct 01 2008
prev sibling parent "Bill Baxter" <wbaxter gmail.com> writes:
On Fri, Oct 3, 2008 at 2:32 AM, KennyTM~ <kennytm gmail.com> wrote:
 Andrei Alexandrescu wrote:
 I'm impressed a lot by the analogy with math because it is one extra
 argument in my favor. In math there's no function without arguments. At most
 people would say f(.) (with a central dot) when referring to what is akin to
 the address of the function.

Sorry, in math the normal way to represent a function is to just use the symbol, i.e. f, e.g. chain rule (f o g)' = g' * f' o g.

Yeh, and that's because in math there *are* no zero argument functions so in math just writing the symbol means essentially "pointer to function", while in D that is &f. At best the math precedent argues that f() should be illegal and plain f should be the only way to evaluate this thing-that-is-not-a-function-because-it-takes-no-arguments, but I don't think that's what Andrei really wants.
 The central dot is used only when the "function" is not represented in the
 form f(a,b,c,...), e.g. the commutator [.,.] and the mean <.>. I have
 *never* seen anyone writes f(.).

I have, when talking about functionals (functions of functions). Something like g(f(., .)). I think the point was to remind the reader that the argument f is really a function and show how many arguments it takes, without implying that you're evaluating it. Maybe that's what Andrei was trying to say by "taking the address". But I've forgotten what the point of this was. --bb
Oct 02 2008
prev sibling next sibling parent "Steven Schveighoffer" <schveiguy yahoo.com> writes:
"Bill Baxter" wrote
 On Tue, Sep 30, 2008 at 3:36 AM, Steven Schveighoffer
 <schveiguy yahoo.com> wrote:
 There is no ambiguity either case. You evaluate Stdout.newline. The
 evaluation yields a value of some type. Then you evaluate formatln 
 against
 that value.

OK, then tell me what this does: x.y.z(); Is y a property/field of x or a function call with no args? I see a benefit to being able to understand a line of code without requiring lots of extra context. I have to do less lookups of the source of a function or property.

The problem with this argument is that unless you disallow properties altogether, you still won't know whether y is actually a field or a call to a property method without looking it up.

Yes, but that is not in question. A property method is *meant* to be used like a field, and so it shouldn't matter whether y is a property or field, it should be interchangable. But if y is not meant to be a property, then it is confusing to read x.y.z() and realize that y isn't meant to be used as a property, and might not even do what you are expecting. See the TimeSpan problem that I went through for an example: http://www.dsource.org/projects/tango/ticket/1184 This problem would never occur if you could not just call any function as a property. -Steve
Sep 29 2008
prev sibling parent reply Benji Smith <dlanguage benjismith.net> writes:
Bill Baxter wrote:
 On Tue, Sep 30, 2008 at 3:36 AM, Steven Schveighoffer
 <schveiguy yahoo.com> wrote:
 There is no ambiguity either case. You evaluate Stdout.newline. The
 evaluation yields a value of some type. Then you evaluate formatln against
 that value.

x.y.z(); Is y a property/field of x or a function call with no args? I see a benefit to being able to understand a line of code without requiring lots of extra context. I have to do less lookups of the source of a function or property.

The problem with this argument is that unless you disallow properties altogether, you still won't know whether y is actually a field or a call to a property method without looking it up. --bb

One thing that I've found especially annoying about the current implementation is this: x.y = 3; // Working code x.y += 3; // Broken, if x.y is actually a function x.y = x.y + 3; // Ugly work-around I've been bitten by this at least a dozen different times. Having a real property syntax would eliminate cases like that. --benji
Sep 30 2008
next sibling parent reply KennyTM~ <kennytm gmail.com> writes:
Simen Kjaeraas wrote:
 On Wed, 01 Oct 2008 07:58:20 +0200, Benji Smith 
 <dlanguage benjismith.net> wrote:
 
 Bill Baxter wrote:
 On Tue, Sep 30, 2008 at 3:36 AM, Steven Schveighoffer
 <schveiguy yahoo.com> wrote:
 There is no ambiguity either case. You evaluate Stdout.newline. The
 evaluation yields a value of some type. Then you evaluate formatln 
 against
 that value.

x.y.z(); Is y a property/field of x or a function call with no args? I see a benefit to being able to understand a line of code without requiring lots of extra context. I have to do less lookups of the source of a function or property.

altogether, you still won't know whether y is actually a field or a call to a property method without looking it up. --bb

One thing that I've found especially annoying about the current implementation is this: x.y = 3; // Working code x.y += 3; // Broken, if x.y is actually a function x.y = x.y + 3; // Ugly work-around I've been bitten by this at least a dozen different times. Having a real property syntax would eliminate cases like that. --benji

It seems we will soon get reference return values, in which case this would no longer be a problem. I have also written a property template that enables things such as that, but it would also benefit immensely from reference return values.

It's still an issue if we want to support property setters, say class Time { private double seconds; invariant /*pure?*/ double minutes () { return seconds/60.; } void minutes (invariant double x) { seconds = x*60.; } } You cannot return a double& when calling .minutes because it simply does not exist. Therefore, the interpreter have to clever enough to convert t.minutes += 2; into t.minutes = t.minutes + 2; i.e. t.minutes(t.minutes() + 2); if .minutes does not return a reference. A similar problem also exist with .opIndexAssign and .opSliceAssign.
Oct 01 2008
next sibling parent Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
KennyTM~ wrote:
 Simen Kjaeraas wrote:
 On Wed, 01 Oct 2008 07:58:20 +0200, Benji Smith 
 <dlanguage benjismith.net> wrote:

 Bill Baxter wrote:
 On Tue, Sep 30, 2008 at 3:36 AM, Steven Schveighoffer
 <schveiguy yahoo.com> wrote:
 There is no ambiguity either case. You evaluate Stdout.newline. The
 evaluation yields a value of some type. Then you evaluate formatln 
 against
 that value.

x.y.z(); Is y a property/field of x or a function call with no args? I see a benefit to being able to understand a line of code without requiring lots of extra context. I have to do less lookups of the source of a function or property.

altogether, you still won't know whether y is actually a field or a call to a property method without looking it up. --bb

One thing that I've found especially annoying about the current implementation is this: x.y = 3; // Working code x.y += 3; // Broken, if x.y is actually a function x.y = x.y + 3; // Ugly work-around I've been bitten by this at least a dozen different times. Having a real property syntax would eliminate cases like that. --benji

It seems we will soon get reference return values, in which case this would no longer be a problem. I have also written a property template that enables things such as that, but it would also benefit immensely from reference return values.

It's still an issue if we want to support property setters, say class Time { private double seconds; invariant /*pure?*/ double minutes () { return seconds/60.; } void minutes (invariant double x) { seconds = x*60.; } } You cannot return a double& when calling .minutes because it simply does not exist. Therefore, the interpreter have to clever enough to convert t.minutes += 2; into t.minutes = t.minutes + 2; i.e. t.minutes(t.minutes() + 2); if .minutes does not return a reference. A similar problem also exist with .opIndexAssign and .opSliceAssign.

Ah, didn't see your post before I posted a similar argument. Andrei
Oct 01 2008
prev sibling parent "Steven Schveighoffer" <schveiguy yahoo.com> writes:
"KennyTM~" wrote
 Simen Kjaeraas wrote:
 On Wed, 01 Oct 2008 07:58:20 +0200, Benji Smith 
 <dlanguage benjismith.net> wrote:

 Bill Baxter wrote:
 On Tue, Sep 30, 2008 at 3:36 AM, Steven Schveighoffer
 <schveiguy yahoo.com> wrote:
 There is no ambiguity either case. You evaluate Stdout.newline. The
 evaluation yields a value of some type. Then you evaluate formatln 
 against
 that value.

x.y.z(); Is y a property/field of x or a function call with no args? I see a benefit to being able to understand a line of code without requiring lots of extra context. I have to do less lookups of the source of a function or property.

altogether, you still won't know whether y is actually a field or a call to a property method without looking it up. --bb

One thing that I've found especially annoying about the current implementation is this: x.y = 3; // Working code x.y += 3; // Broken, if x.y is actually a function x.y = x.y + 3; // Ugly work-around I've been bitten by this at least a dozen different times. Having a real property syntax would eliminate cases like that. --benji

It seems we will soon get reference return values, in which case this would no longer be a problem. I have also written a property template that enables things such as that, but it would also benefit immensely from reference return values.

It's still an issue if we want to support property setters, say class Time { private double seconds; invariant /*pure?*/ double minutes () { return seconds/60.; } void minutes (invariant double x) { seconds = x*60.; } } You cannot return a double& when calling .minutes because it simply does not exist. Therefore, the interpreter have to clever enough to convert t.minutes += 2; into t.minutes = t.minutes + 2; i.e. t.minutes(t.minutes() + 2); if .minutes does not return a reference. A similar problem also exist with .opIndexAssign and .opSliceAssign.

Yes yes! vote++ -Steve
Oct 01 2008
prev sibling parent Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
Simen Kjaeraas wrote:
 On Wed, 01 Oct 2008 07:58:20 +0200, Benji Smith 
 <dlanguage benjismith.net> wrote:
 
 Bill Baxter wrote:
 On Tue, Sep 30, 2008 at 3:36 AM, Steven Schveighoffer
 <schveiguy yahoo.com> wrote:
 There is no ambiguity either case. You evaluate Stdout.newline. The
 evaluation yields a value of some type. Then you evaluate formatln 
 against
 that value.

x.y.z(); Is y a property/field of x or a function call with no args? I see a benefit to being able to understand a line of code without requiring lots of extra context. I have to do less lookups of the source of a function or property.

altogether, you still won't know whether y is actually a field or a call to a property method without looking it up. --bb

One thing that I've found especially annoying about the current implementation is this: x.y = 3; // Working code x.y += 3; // Broken, if x.y is actually a function x.y = x.y + 3; // Ugly work-around I've been bitten by this at least a dozen different times. Having a real property syntax would eliminate cases like that. --benji

It seems we will soon get reference return values, in which case this would no longer be a problem. I have also written a property template that enables things such as that, but it would also benefit immensely from reference return values.

Not quite. Sorry, I woke up all negative this morning :o). Consider the .length property for built-in arrays. Better yet, consider you implement your own array and want to define a .length property. You don't want it to return a reference! You want to have control over the assignment so that you execute the actual array change. What I'd proposed to Walter for a long time is to allow a.b = c to work if a.b = a.b c works. I'm sure people around here will find reasons to dislike that :o). Andrei
Oct 01 2008
prev sibling parent reply Michel Fortin <michel.fortin michelf.com> writes:
On 2008-09-29 12:55:18 -0400, "Steven Schveighoffer" 
<schveiguy yahoo.com> said:

 Stdout.newline.formatln("Hi there {}", var);
 
 Is newline a property of Stdout, on which I'm calling formatln, or is
 newline a function that I'm calling, and then using the result of that
 function to call formatln?  Compare with:
 
 Stdout.newline().formatln("Hi there {}", var);
 
 There is no ambiguity.  newline is clearly a function, not a property.

Interesting. To me both of these things have exactly the same meaning. Perhaps it's because I'm used to D, but I think it's more because I can't count how many times I've seen this pattern in C++: SomeSimgleton::Instance().doSomething(); where Instance() is simply an accessor. In my opinion, "newline" isn't a good function name for what it does because it has no verb. Having no verb makes it look like an accessor much more than the absence or presence of parenthesis. -- Michel Fortin michel.fortin michelf.com http://michelf.com/
Sep 29 2008
parent reply "Steven Schveighoffer" <schveiguy yahoo.com> writes:
"Michel Fortin" wrote
 On 2008-09-29 12:55:18 -0400, "Steven Schveighoffer" said:

 Stdout.newline.formatln("Hi there {}", var);

 Is newline a property of Stdout, on which I'm calling formatln, or is
 newline a function that I'm calling, and then using the result of that
 function to call formatln?  Compare with:

 Stdout.newline().formatln("Hi there {}", var);

 There is no ambiguity.  newline is clearly a function, not a property.

Interesting. To me both of these things have exactly the same meaning. Perhaps it's because I'm used to D, but I think it's more because I can't count how many times I've seen this pattern in C++: SomeSimgleton::Instance().doSomething(); where Instance() is simply an accessor.

Yes, that is a workaround for not having property constructs available. The intuitive label of 'property' comes from the name of the getter 'Instance'. It screams that you are getting the instance of the class, and it's well accepted as a property. What has happened is that instead of relying on the compiler to enforce what is a property and what is not, you are relying on the interpretation of English words. But in a language like C#, you would define Instance as a property, not a function. So it would never look like that. In D, properties are equivalent to functions, so you have a choice as to whether to use the parentheses or not. But that isn't the problem, the problem is really that you can call functions as properties.
 In my opinion, "newline" isn't a good function name for what it does 
 because it has no verb. Having no verb makes it look like an accessor much 
 more than the absence or presence of parenthesis.

So newline() seems like a property to you more than 'start a new line'? I suppose I can see that, but consider if you were used to a system where properties *prohibited* parentheses, and functions *required* them. It becomes less about interpretation of English vocabulary and more about compiler-enforced interface. I like that system better. -Steve
Sep 30 2008
parent reply Michel Fortin <michel.fortin michelf.com> writes:
On 2008-09-30 09:42:43 -0400, "Steven Schveighoffer" 
<schveiguy yahoo.com> said:

 "Michel Fortin" wrote
 On 2008-09-29 12:55:18 -0400, "Steven Schveighoffer" said:
 
 Stdout.newline.formatln("Hi there {}", var);
 
 Is newline a property of Stdout, on which I'm calling formatln, or is
 newline a function that I'm calling, and then using the result of that
 function to call formatln?  Compare with:
 
 Stdout.newline().formatln("Hi there {}", var);
 
 There is no ambiguity.  newline is clearly a function, not a property.

Interesting. To me both of these things have exactly the same meaning. Perhaps it's because I'm used to D, but I think it's more because I can't count how many times I've seen this pattern in C++: SomeSimgleton::Instance().doSomething(); where Instance() is simply an accessor.

Yes, that is a workaround for not having property constructs available. The intuitive label of 'property' comes from the name of the getter 'Instance'. It screams that you are getting the instance of the class, and it's well accepted as a property. What has happened is that instead of relying on the compiler to enforce what is a property and what is not, you are relying on the interpretation of English words.

But one problem is that the distinction between a accessor (of a property) and a function is often matter to interpretation. In the case above, most likely the Instance() function will create the singleton object if it doesn't exist yet; seen that way it isn't a simple accessor. If you ask if Instance() should be a property or not, you're likely to get different answers from different people. Arguably, there are cases even more ambiguous than this one. The issue I want to bring is that without having a formal definition of what is a property and what is not -- one the compiler can enforce -- people will make different choices, not always coherent with each others, and you may very well find yourself having to call Instance() with parenthesis in one D lib and without parenthesis in another.
 But in a language like C#, you would define Instance as a property, not a
 function.  So it would never look like that.

I could, but should I? I'm very interested in your criterions about why Instance would qualify to be a property and not as a function in this case. It's not just me. Just as an example, this article's author doesn't want to decide if Instance() should be a property or not: <http://www.yoda.arachsys.com/csharp/singleton.html> "Note that all of these implementations also use a public static property Instance as the means of accessing the instance. In all cases, the property could easily be converted to a method, with no impact on thread-safety or performance."
 In D, properties are equivalent to functions, so you have a choice as to
 whether to use the parentheses or not.  But that isn't the problem, the
 problem is really that you can call functions as properties.

But what is the difference between a property and a function (beside the syntax?). I can find countless discussions on the net about this; interesting discussions but with no definitive answer. In the end, it all boils down to the author's feeling about his function/property, which may be the opposite from its user's. I'd like very much to quote Bruce Wood from this discussion: <http://bytes.com/forum/thread478064.html> (feel free to comment the quote) """ You see, it's not always so cut-and-dried. On the one hand, if you make something a property, you are implying that getting the information will be quick. So, if getting the information is slow, that is a hint that it shouldn't be a property. On the other hand, you don't want to determine what is a property or not based upon implementation details. By doing so you're letting the internal guts of how your class works leak out into its interface with the outside world. That's bad style. So, something should be a property if it makes sense that it be a property. Certainly, anything that affects the state of the object in a manner more sophisticated than just "set this to that" should be a method. That's painfully obvious. Similarly, an aspect of your class that can be read and set, even if setting it does more than just set a variable, should probably be a property. Reasonably straightfoward. However, sometimes the two design goals above-properties that return results quickly and not letting implementation determine interface-are at odds with each other, and you have to make a judgement call. In the case of my system, I had things that logically should be properties (from an interface point of view) but the object held only keys, not references to the objects themselves, so the "get" on the property has to fetch the object in question from the database. It was an open question whether to code: Supplier theSupplier = stockItem.Supplier; or Supplier theSupplier = stockItem.GetSupplier(); I chose the former, preferring to hide the implementation (holding supplier UID, not supplier itself) rather than let it leak out that getting the supplier was actually a lot of work. In our case it's no problem, because we're a very small shop. In a different situation I might have gone for door #2. """ With the current approach in D, you just don't need to choose, just document what your function/property is doing and be done with it... well you may still have to choose about the name :-), so in a way the argument is still there, but what I mean is that I don't think we really need a black and a white syntax for one concept that may take many shades of gray.
 In my opinion, "newline" isn't a good function name for what it does
 because it has no verb. Having no verb makes it look like an accessor much
 more than the absence or presence of parenthesis.

So newline() seems like a property to you more than 'start a new line'? I suppose I can see that, but consider if you were used to a system where properties *prohibited* parentheses, and functions *required* them. It becomes less about interpretation of English vocabulary and more about compiler-enforced interface. I like that system better.

But I'm still puzzled by what being a property means in opposition as if it was a function. The thing is that the compiler just can't guaranty anything about properties: it's really just another syntax for calling a function, a function the author has classified as a property by some subjective standard, a standard which will often differ between authors. Properties are often touted as an indicator for low-cost functions -- whatever "low-cost" means --, but even this fuzzy standard somewhat hinder the often seeked goal of separating interface and implementation. Without aggreement on a formal definition for properties and compiler enforcability, I find it very difficult to justify a formal syntax for properties. -- Michel Fortin michel.fortin michelf.com http://michelf.com/
Oct 01 2008
next sibling parent reply Michel Fortin <michel.fortin michelf.com> writes:
On 2008-10-02 01:25:34 -0400, "Bill Baxter" <wbaxter gmail.com> said:

 Interesting post.  My first thought is what is the formal definition
 for what should be a struct and what should be a class.  That's not a
 cut-and-dried clear decision either.  Either one will generally work.
 I may think X should be a struct and my users may think it should have
 been a class.  We can both be right.  Seems a pretty similar
 situation.

Yes, indeed, often both can be right in many situations. That's a very interesting observation in this context. If this were C++, I'd agree that we'd be better with just one of the two because in C++ the *only* difference between a struct and a class is syntaxic (if not to say cosmetic): it's the default protection attribute which you generally override explicity anyway. I sometime find myself want to forward-declare a class from some library (to avoid extra includes in the header file) only to have the compiler tell me it's a struct, or vice-versa, with an error I could live without. In C++, I don't think the addition of the "class" keyword was justified: it doesn't add anything useful beside perhaps marketing advantages -- because "class" sounds more object-oriented than "struct". But we are talking about D, where the situation is different. D classes are well suited for inheritance hierarchies, they can only be passed by reference and are always allocated on the heap[^1]. They are also easy to synchronize with due to their built-in monitor. D structs don't have implicit hidden members (monitor, classinfo pointer) so you can use them to represent any fixed-length memory layout. Structs are easily copied and passed by value too, unlike classes, thanks to the lack of inheritance. Basically, what we have here is two different tools each with different attributes. Sometime, they're easily interchangeable, sometime not and that's why we need both. But each time you have to choose, your choice will (or should) go down to the performance, memory, and flexibility implications of each. Choosing what's best will probably involve evaluating which approach better fits your needs, and this may even be measurable. Compare this with properties which have exactly the same performance, memory, and flexibility implications as regular functions. The only difference between a property and a function is the meaning the author give to it, and that meaning vary between authors and contexts. Which means that from the API user point of view, something being a property or a function doesn't mean much... except of course that the user has to remember if Singleton.Instance is a property or a function before using it, or else suffer some useless compiler errors. [^1]: Except scope classes which are allocated on the stack. But allocating scope classes on the stack is a mere optimisation which happen to be possible when you create a scope object, it doesn't affect the semantics of the language at all. -- Michel Fortin michel.fortin michelf.com http://michelf.com/
Oct 02 2008
parent reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
Bill Baxter wrote:
 On Thu, Oct 2, 2008 at 8:26 PM, Michel Fortin <michel.fortin michelf.com>
wrote:
 Compare this with properties which have exactly the same performance,
 memory, and flexibility implications as regular functions. The only
 difference between a property and a function is the meaning the author give
 to it, and that meaning vary between authors and contexts. Which means that
 from the API user point of view, something being a property or a function
 doesn't mean much... except of course that the user has to remember if
 Singleton.Instance is a property or a function before using it, or else
 suffer some useless compiler errors.

Good points. I think this discussion is diverging a little far from the real raison d'etre for property syntax, though. They are there in the first place not to save the typing of a few () here and there, but to allow a library designer to start with a plain field when that suffices and transition to a function later if that becomes necessary, without breaking client code. And there's where we can invoke a grand principle of software design as Andrei would have us do: * Changing implementation details should not affect client code. Ok I guess that's a software design principle, not language design but I guess I can say the underlying language principle is that the a good language should facilitate good software design. This one has to trump principles about economy of syntax and such, because the point of having a language in the first place is to make writing software easier/better/faster. More specifically this is a case of where we want syntax to facilitate making the transition from a simple field to something with some logic too. So what can we hope to get from the language to support this in the best way possible. These two conclusions seem pretty obvious to me: 1) The "fake" field's behavior should be indistinguishable from real field behavior in as many circumstances as possible. This prevents client code from having to change. This is why things like += on a property should be transformed by the compiler. 2) In situations where behavior does differ from that of a real field, a compiler error should occur at the point of usage. This prevents nasty surprises where code silently starts behaving incorrectly. This suggests that something like attempting to take the address of the field should be a syntax error if the field is turned into a property function. (This probably already happens in most cases since a function pointer won't usually be interchangeable with an int pointer or whatever it was before) Anyway, the point I want to make is that while it's neat that you can save typing a few parentheses, there are more important things, like being able to refactor code locally and knowing with reasonable certainty that all client code will either still work correctly or fail to compile. Going by these principles could lead to some interesting conclusions like for instance an argument that you shouldn't be able to take the address of a property function, or do anything else to it that exposes the fact that it is not, in fact, a field.

Yes, I entirely agree. I mentioned this a couple of times, but never explained it so well and thoroughly. Andrei
Oct 02 2008
parent Benji Smith <dlanguage benjismith.net> writes:
Andrei Alexandrescu wrote:
 Bill Baxter wrote:
 On Thu, Oct 2, 2008 at 8:26 PM, Michel Fortin 
 <michel.fortin michelf.com> wrote:
 Compare this with properties which have exactly the same performance,
 memory, and flexibility implications as regular functions. The only
 difference between a property and a function is the meaning the 
 author give
 to it, and that meaning vary between authors and contexts. Which 
 means that
 from the API user point of view, something being a property or a 
 function
 doesn't mean much... except of course that the user has to remember if
 Singleton.Instance is a property or a function before using it, or else
 suffer some useless compiler errors.

Good points. I think this discussion is diverging a little far from the real raison d'etre for property syntax, though. They are there in the first place not to save the typing of a few () here and there, but to allow a library designer to start with a plain field when that suffices and transition to a function later if that becomes necessary, without breaking client code. And there's where we can invoke a grand principle of software design as Andrei would have us do: * Changing implementation details should not affect client code. Ok I guess that's a software design principle, not language design but I guess I can say the underlying language principle is that the a good language should facilitate good software design. This one has to trump principles about economy of syntax and such, because the point of having a language in the first place is to make writing software easier/better/faster. More specifically this is a case of where we want syntax to facilitate making the transition from a simple field to something with some logic too. So what can we hope to get from the language to support this in the best way possible. These two conclusions seem pretty obvious to me: 1) The "fake" field's behavior should be indistinguishable from real field behavior in as many circumstances as possible. This prevents client code from having to change. This is why things like += on a property should be transformed by the compiler. 2) In situations where behavior does differ from that of a real field, a compiler error should occur at the point of usage. This prevents nasty surprises where code silently starts behaving incorrectly. This suggests that something like attempting to take the address of the field should be a syntax error if the field is turned into a property function. (This probably already happens in most cases since a function pointer won't usually be interchangeable with an int pointer or whatever it was before) Anyway, the point I want to make is that while it's neat that you can save typing a few parentheses, there are more important things, like being able to refactor code locally and knowing with reasonable certainty that all client code will either still work correctly or fail to compile. Going by these principles could lead to some interesting conclusions like for instance an argument that you shouldn't be able to take the address of a property function, or do anything else to it that exposes the fact that it is not, in fact, a field.

Yes, I entirely agree. I mentioned this a couple of times, but never explained it so well and thoroughly. Andrei

Me too. My previous use of the word "consistency" was perhaps a little sloppy, but I think Bill explains my thoughts to a tee. I'd be happiest if we could strengthen the semantics of properties, to make them indistinguishable from fields (from the class consumer's perspective). If we can't accomplish that, I'd rather do without the optional-parentheses and "x.prop = 3" tricks, using functions instead of properties. --benji
Oct 02 2008
prev sibling next sibling parent Michel Fortin <michel.fortin michelf.com> writes:
On 2008-10-02 01:25:34 -0400, "Bill Baxter" <wbaxter gmail.com> said:

 My first thought is what is the formal definition
 for what should be a struct and what should be a class.

Hum, I think I forgot to answer that part. The reason I say we need a formal definition is so we can actually imply things from something being a property. Pure and nothrow functions are (or will) be compile-time checked against a definition of what they should be: these offer garanties, which in turn add value to the user who know what it does. If we can't imply anything from them, properties offer no real value over mere functions since authors will decide what is a property based on the expectations they expect their users to have. To be frank, I don't expect we can agree on a formal definition. If we try and that is indeed the conclusion, it'll prove my point that you just can't imply anything from something being a property except the vague idea that the author thought it should be one according to his own definition. -- Michel Fortin michel.fortin michelf.com http://michelf.com/
Oct 02 2008
prev sibling next sibling parent Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
Michel Fortin wrote:
 Without aggreement on a formal definition for properties and compiler 
 enforcability, I find it very difficult to justify a formal syntax for 
 properties.

I agree. Moreover, there should be arguments on why the distinction useful. I liked it from day 1 that D made properties just a particular case of functions, and I'd love to keep it that way. It would obviate discussions such the one mentioned about C#. Andrei
Oct 02 2008
prev sibling parent "Steven Schveighoffer" <schveiguy yahoo.com> writes:
"Michel Fortin" wrote
 On 2008-09-30 09:42:43 -0400, "Steven Schveighoffer" said:

 "Michel Fortin" wrote
 On 2008-09-29 12:55:18 -0400, "Steven Schveighoffer" said:

 Stdout.newline.formatln("Hi there {}", var);

 Is newline a property of Stdout, on which I'm calling formatln, or is
 newline a function that I'm calling, and then using the result of that
 function to call formatln?  Compare with:

 Stdout.newline().formatln("Hi there {}", var);

 There is no ambiguity.  newline is clearly a function, not a property.

Interesting. To me both of these things have exactly the same meaning. Perhaps it's because I'm used to D, but I think it's more because I can't count how many times I've seen this pattern in C++: SomeSimgleton::Instance().doSomething(); where Instance() is simply an accessor.

Yes, that is a workaround for not having property constructs available. The intuitive label of 'property' comes from the name of the getter 'Instance'. It screams that you are getting the instance of the class, and it's well accepted as a property. What has happened is that instead of relying on the compiler to enforce what is a property and what is not, you are relying on the interpretation of English words.

But one problem is that the distinction between a accessor (of a property) and a function is often matter to interpretation. In the case above, most likely the Instance() function will create the singleton object if it doesn't exist yet; seen that way it isn't a simple accessor. If you ask if Instance() should be a property or not, you're likely to get different answers from different people.

The difference is syntax-implied meaning. A property is a field implemented as a function. It is a form of information hiding and imposing access on a field, which could be a virtual field. By specifically defining properties, I'm telling you, 'this is a field. Access it like a field. pay no attention to how the field is constructed, or whether it is actually a field or not'. This allows me to later on make it a true field if it seems like the right decision. Or to specify a guarantee about properties. The other way around is already possible. I can take a field that is already in use and make it into a property. But the reverse is not so easy. Anyone who has accessed the field using parentheses would now have to go back and change all their code to remove the parentheses. For example, with the magic of static constructors, I can change Instance from a property to an actual field, by initializing it in a static constructor. Now every place you use Instance() (and there's probably a lot of them) is now invalid code. If the *required* method of accessing Instance is without parens, then you notice no differences, and the interface to Instance remains the same. As far as interpreting code, with the current state of affairs a function can be called like a field, which makes things more difficult to decipher. What is this symbol supposed to be? It becomes an interpretation of the actual words used, which I don't argue isn't part of the equation anyways, but leaving it completely up to that is not as precise. I have a real world example where someone thought a static constructor method I wrote was actually a property setter. See this bug in Tango: http://www.dsource.org/projects/tango/ticket/1184 I never intended the static constructor to be a property, but it just works that way because the compiler matches the developer's request for a setter property by finding a static function which does nothing like the developer intended. So it's a very hard bug to solve, and we couldn't solve it in the way I wanted, because the compiler cannot be forced not to look at all functions as possible properties. I agree that there is a subjective line as to what makes a property and what does not. What I propose is that we put the control of that line in the hands of the author, not the hands of the user. If the author thinks it's better used as a property, that is their call. It shouldn't be up to the user to say 'hey, I thought you meant function() here, you can't change it now!' It's the same concept behind interfaces.
 Arguably, there are cases even more ambiguous than this one. The issue I 
 want to bring is that without having a formal definition of what is a 
 property and what is not -- one the compiler can enforce -- people will 
 make different choices, not always coherent with each others, and you may 
 very well find yourself having to call Instance() with parenthesis in one 
 D lib and without parenthesis in another.

You end up with that situation anyways. If you have a project written by more than one person, one developer might use parens, one might not for the *same library* not just different libraries. Hell, I use the same function sometimes as property or function depending on how much I'm paying attention. I don't like the inconsistency that allowing all noarg functions to potentially be properties fosters. At least with a formal definition, the user of the lib knows what the author was trying to create, and uses the property in a forwards-compatible fashion.
 But in a language like C#, you would define Instance as a property, not a
 function.  So it would never look like that.

I could, but should I? I'm very interested in your criterions about why Instance would qualify to be a property and not as a function in this case. It's not just me. Just as an example, this article's author doesn't want to decide if Instance() should be a property or not: <http://www.yoda.arachsys.com/csharp/singleton.html> "Note that all of these implementations also use a public static property Instance as the means of accessing the instance. In all cases, the property could easily be converted to a method, with no impact on thread-safety or performance."

I guess what I should have said is that I would make it a property ;) Sometimes it's hard to envision a different point of view, but you are absolutely right, it could be considered a function instead. My definition of what I would like to see in an accessor (mind you, this is my definition, and would only be valid for libraries I wrote. Someone else could have a different definition, and that's fine with me) is something that consistently returns a value. So if I called it twice without anything changing, it would return the same thing, and accessing it doesn't change what it returns next time. So Instance qualifies because once it is constructed, it always returns the same thing. Perfect example of a use for an accessor property (to me anyway). If two different libraries use different accessing methods for Instance, that's fine. At least it's consistent. You are going to have a hard time enforcing design decisions over all the different libraries you use to be consistent. But at least they should be consistent every time you use them.
 In D, properties are equivalent to functions, so you have a choice as to
 whether to use the parentheses or not.  But that isn't the problem, the
 problem is really that you can call functions as properties.

But what is the difference between a property and a function (beside the syntax?). I can find countless discussions on the net about this; interesting discussions but with no definitive answer. In the end, it all boils down to the author's feeling about his function/property, which may be the opposite from its user's.

The difference is the interface. Which is the syntax, and that is important to me at least. If you don't like the authors usage of properties, don't use the lib. I don't see any other aspect of library design that leaves it up to the user as to how their functions/objects should be interfaced. Why should the user be allowed to decide the property/function interface?
 I'd like very much to quote Bruce Wood from this discussion:
 <http://bytes.com/forum/thread478064.html>

 (feel free to comment the quote)

 """
 You see, it's not always so cut-and-dried.

 On the one hand, if you make something a property, you are implying
 that getting the information will be quick. So, if getting the
 information is slow, that is a hint that it shouldn't be a property.

This is the opinion of the author (and I have the same opinion), but it's not written in stone. The only things that are written in stone (or I guess binary) are what the compiler guarantees. These kinds of debates have no real true answer, so there is no way to enforce. How would you enforce it anyways? But you can enforce the interface, and that's what I want.
 On the other hand, you don't want to determine what is a property or
 not based upon implementation details. By doing so you're letting the
 internal guts of how your class works leak out into its interface with
 the outside world. That's bad style. So, something should be a property
 if it makes sense that it be a property.

 Certainly, anything that affects the state of the object in a manner
 more sophisticated than just "set this to that" should be a method.
 That's painfully obvious.

I disagree with that ;), but then again that's my opinion and there is no right answer.
 Similarly, an aspect of your class that can be read and set, even if
 setting it does more than just set a variable, should probably be a
 property. Reasonably straightfoward.

 However, sometimes the two design goals above-properties that return
 results quickly and not letting implementation determine
 interface-are at odds with each other, and you have to make a
 judgement call.

 In the case of my system, I had things that logically should be
 properties (from an interface point of view) but the object held only
 keys, not references to the objects themselves, so the "get" on the
 property has to fetch the object in question from the database. It was
 an open question whether to code:

 Supplier theSupplier = stockItem.Supplier;

 or

 Supplier theSupplier = stockItem.GetSupplier();

 I chose the former, preferring to hide the implementation (holding
 supplier UID, not supplier itself) rather than let it leak out that
 getting the supplier was actually a lot of work. In our case it's no
 problem, because we're a very small shop. In a different situation I
 might have gone for door #2.
 """

In my opinion, I'd always go for door #1 in that case. But I'd probably use some sort of cache, where the lookup was implemented as a function, and the access of the value was done as a property afterwards.
 With the current approach in D, you just don't need to choose, just 
 document what your function/property is doing and be done with it... well 
 you may still have to choose about the name :-), so in a way the argument 
 is still there, but what I mean is that I don't think we really need a 
 black and a white syntax for one concept that may take many shades of 
 gray.

We don't need a formal definition of what should or should not be a property. But we need to allow the author to define what should or should not be a property. Without doing this, you are making the interface inconsistent or ambiguous -- even if only from a syntax point of view.
 In my opinion, "newline" isn't a good function name for what it does
 because it has no verb. Having no verb makes it look like an accessor 
 much
 more than the absence or presence of parenthesis.

So newline() seems like a property to you more than 'start a new line'? I suppose I can see that, but consider if you were used to a system where properties *prohibited* parentheses, and functions *required* them. It becomes less about interpretation of English vocabulary and more about compiler-enforced interface. I like that system better.

But I'm still puzzled by what being a property means in opposition as if it was a function. The thing is that the compiler just can't guaranty anything about properties: it's really just another syntax for calling a function, a function the author has classified as a property by some subjective standard, a standard which will often differ between authors. Properties are often touted as an indicator for low-cost functions -- whatever "low-cost" means --, but even this fuzzy standard somewhat hinder the often seeked goal of separating interface and implementation. Without aggreement on a formal definition for properties and compiler enforcability, I find it very difficult to justify a formal syntax for properties.

But you have no problems allowing a library author to define what is an interface and what is a class? Or what is a struct? Or if a function should return by value or by reference? Defining the interface is the job of the author, and the very very good reason behind that is so the author can change details without changing the interface to make upgrades easier for him *and* you. If he cannot define the interface, then he cannot make changes without affecting someone. -Steve
Oct 02 2008
prev sibling parent "Bill Baxter" <wbaxter gmail.com> writes:
On Wed, Oct 1, 2008 at 10:58 PM, Steven Schveighoffer
<schveiguy yahoo.com> wrote:
 "KennyTM~" wrote
 You cannot return a double& when calling .minutes because it simply does
 not exist. Therefore, the interpreter have to clever enough to convert

   t.minutes += 2;

 into

   t.minutes = t.minutes + 2;

 i.e.

   t.minutes(t.minutes() + 2);

 if .minutes does not return a reference. A similar problem also exist with
 .opIndexAssign and .opSliceAssign.

Yes yes! vote++ -Steve

Me too++ --bb
Oct 01 2008
prev sibling parent Michel Fortin <michel.fortin michelf.com> writes:
On 2008-09-26 13:47:55 -0400, "Steven Schveighoffer" 
<schveiguy yahoo.com> said:

 I routinely use Tango's Stdout.newline; which I admit goes against what 
 I am saying, but it's because I know what Stdout.newline does, and that 
 it is a function.

I'd argue that for readability of the code "newline" is a bad function name since it has no verb. It should be something like "startLine" or "printNewline" or... "writeln"! Then there would be much less confusion over what it does. -- Michel Fortin michel.fortin michelf.com http://michelf.com/
Sep 27 2008
prev sibling next sibling parent "Bill Baxter" <wbaxter gmail.com> writes:
On Thu, Oct 2, 2008 at 8:26 PM, Michel Fortin <michel.fortin michelf.com> wrote:
 Compare this with properties which have exactly the same performance,
 memory, and flexibility implications as regular functions. The only
 difference between a property and a function is the meaning the author give
 to it, and that meaning vary between authors and contexts. Which means that
 from the API user point of view, something being a property or a function
 doesn't mean much... except of course that the user has to remember if
 Singleton.Instance is a property or a function before using it, or else
 suffer some useless compiler errors.

Good points. I think this discussion is diverging a little far from the real raison d'etre for property syntax, though. They are there in the first place not to save the typing of a few () here and there, but to allow a library designer to start with a plain field when that suffices and transition to a function later if that becomes necessary, without breaking client code. And there's where we can invoke a grand principle of software design as Andrei would have us do: * Changing implementation details should not affect client code. Ok I guess that's a software design principle, not language design but I guess I can say the underlying language principle is that the a good language should facilitate good software design. This one has to trump principles about economy of syntax and such, because the point of having a language in the first place is to make writing software easier/better/faster. More specifically this is a case of where we want syntax to facilitate making the transition from a simple field to something with some logic too. So what can we hope to get from the language to support this in the best way possible. These two conclusions seem pretty obvious to me: 1) The "fake" field's behavior should be indistinguishable from real field behavior in as many circumstances as possible. This prevents client code from having to change. This is why things like += on a property should be transformed by the compiler. 2) In situations where behavior does differ from that of a real field, a compiler error should occur at the point of usage. This prevents nasty surprises where code silently starts behaving incorrectly. This suggests that something like attempting to take the address of the field should be a syntax error if the field is turned into a property function. (This probably already happens in most cases since a function pointer won't usually be interchangeable with an int pointer or whatever it was before) Anyway, the point I want to make is that while it's neat that you can save typing a few parentheses, there are more important things, like being able to refactor code locally and knowing with reasonable certainty that all client code will either still work correctly or fail to compile. Going by these principles could lead to some interesting conclusions like for instance an argument that you shouldn't be able to take the address of a property function, or do anything else to it that exposes the fact that it is not, in fact, a field. --bb
Oct 02 2008
prev sibling parent reply Bruno Medeiros <brunodomedeiros+spam com.gmail> writes:
Andrei Alexandrescu wrote:
 
 I routinely use writeln; without 
 feeling it makes for inferior style.
 

Dear Gods! O_o -- Bruno Medeiros - Software Developer, MSc. in CS/E graduate http://www.prowiki.org/wiki4d/wiki.cgi?BrunoMedeiros#D
Oct 02 2008
parent Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
Bruno Medeiros wrote:
 Andrei Alexandrescu wrote:
 I routinely use writeln; without feeling it makes for inferior style.

Dear Gods! O_o

What are you gonna do? People are different. If the best you can say against it is that it's "hideous", we should visit a modern art gallery :o). Andrei
Oct 02 2008
prev sibling next sibling parent reply Christopher Wright <dhasenan gmail.com> writes:
Steven Schveighoffer wrote:
 "Andrei Alexandrescu" wrote
 P.S. If src.next() is too lengthy, why not just adopt ++src?

legitimately expect that to mean "increment all elements of src".

So in one case, you believe people's assumptions aren't important, i.e. an assumption that .next without parens will not change anything on the object, yet in another case you believe people's assumptions are the main argument. This doesn't sound consistent.

What if it were src.advance instead? Since it's a verb, it's easier to associate it with an action that might change state rather than simply reading the state of src. As for ++src, I really dislike using that operator overload for an "advance" function. I'd sooner think that src was a fat reference of some sort and calling ++src would increment its value. That said, when I'm working with C or C++, I prefer indexing to pointer manipulation, and I don't use STL collections when I can avoid it, so it's largely a matter of experience.
 Not that I care too much :)  I am also in the camp of 'defined properties 
 should be a language feature,' but I admit to using D-properties quite 
 often.

Properties are good. For one thing, you can get better error messages, like "No set accessor for Foo.Bar" rather than "Foo.Bar() does not match the given parameters (int)".
 The two things that bug me the most about D property syntax:
 
 stuff like this:
 
 x;
 
 What the hell does this mean?  It looks like it does nothing.  But it could 
 be a function call.  If explicit properties were required, and x was defined 
 as a function, not a property, then x; would be a syntax error.

For me, I use the no-parentheses version in either of these cases: 1. I'm using the function as a property (auto name = field.name). 2. The function name is a verb (mocks.replay). This seems reasonably unambiguous in my personal experience, though it helps that I have few locals in most of my methods and use a prefix for private members.
Sep 26 2008
parent Michel Fortin <michel.fortin michelf.com> writes:
On 2008-09-27 00:01:05 -0400, Christopher Wright <dhasenan gmail.com> said:

 Steven Schveighoffer wrote:
 "Andrei Alexandrescu" wrote
 P.S. If src.next() is too lengthy, why not just adopt ++src?

may legitimately expect that to mean "increment all elements of src".

So in one case, you believe people's assumptions aren't important, i.e. an assumption that .next without parens will not change anything on the object, yet in another case you believe people's assumptions are the main argument. This doesn't sound consistent.

What if it were src.advance instead? Since it's a verb, it's easier to associate it with an action that might change state rather than simply reading the state of src.

Very true.
 For me, I use the no-parentheses version in either of these cases:
 1. I'm using the function as a property (auto name = field.name).
 2. The function name is a verb (mocks.replay).
 
 This seems reasonably unambiguous in my personal experience, though it 
 helps that I have few locals in most of my methods and use a prefix for 
 private members.

Indeed. I think all function names which are not simple getters or setters should have a verb in their name, especially in D where there is no syntax difference between a property and a function. And I like pretty much the feature of not having to put parenthesis for function calls with no arguments. The only problem I have with it is that it makes working with delegates a little messier, especially when dealing with delegate properties which just *can't* behave as normal variables. -- Michel Fortin michel.fortin michelf.com http://michelf.com/
Sep 27 2008
prev sibling parent "Bill Baxter" <wbaxter gmail.com> writes:
On Tue, Sep 30, 2008 at 4:08 AM, KennyTM~ <kennytm gmail.com> wrote:
 Bill Baxter wrote:
 On Tue, Sep 30, 2008 at 3:36 AM, Steven Schveighoffer
 <schveiguy yahoo.com> wrote:
 There is no ambiguity either case. You evaluate Stdout.newline. The
 evaluation yields a value of some type. Then you evaluate formatln
 against
 that value.

OK, then tell me what this does: x.y.z(); Is y a property/field of x or a function call with no args? I see a benefit to being able to understand a line of code without requiring lots of extra context. I have to do less lookups of the source of a function or property.

The problem with this argument is that unless you disallow properties altogether, you still won't know whether y is actually a field or a call to a property method without looking it up. --bb

I think the distinction of with and without () is pretty stylistic, because the same argument can even be applied to operator overloading (does a=b means pointing the variable a to b, or calling a.opAssign(b)?) For me, I would use () if the function do modify the object itself.

I think the counter-argument to what I just said is that if properties had to be declared explicitly then at least we would know that .y is either a field or else something the designer *intended* to behave like a property. Which does tell us something. It most likely means that it's not an extremely heavy-weight operation, just a lightweight accessor with maybe a little logic on top. That could be wrong, but in that case it's the class designer's fault for misleading us. --bb
Sep 29 2008
prev sibling next sibling parent reply KennyTM~ <kennytm gmail.com> writes:
Andrei Alexandrescu wrote:
 KennyTM~ wrote:
 Andrei Alexandrescu wrote:
 Bruno Medeiros wrote:
 As a matter of coding style conventions, I would say that using the 
 implicit property function call feature on a function that changes 
 state is *bad* style, and surely hope the community would agree on 
 that.

I sure hope they won't agree to an unsupported assertion.

Actually I *do* hate this "feature" ^_^. And this "feature" has been put to challenged before: * Bug 2159. (http://d.puremagic.com/issues/show_bug.cgi?id=2159) * "Omitting Parens is an Evil" (http://www.digitalmars.com/d/archives/digitalmars/D/Omittable_parens_is an_evil_73881.html) *http://www.digitalmars.com/pnews/read.php?server=news.digitalmars.com& roup=D&artnum=17579 But nothing got changed.

I don't think the bug report has much strength.

I mean the discussion within the bug report. That's a voice from the community isn't it?!
 
 To the very least, when using next as a property I assume the call 
 won't produce any secondary effect on src from the syntax, since I'm 
 just reading a state! Not so if one calls src.next() as the "()" is an 
 alarming sign that src _may_ change afterwards. This could is just a 
 problem of personal taste since the compiler does not forbid 
 src.next(), but not so if you're reading other's code.

 P.S. If src.next() is too lengthy, why not just adopt ++src?

Because people (in wake of the recently introduced array operations) may legitimately expect that to mean "increment all elements of src".

It's fine not to use ++src since C++ is the only language that uses this syntax for iterators anyway, but may I mention that, to increase all elements of an array you use ++a[], note that there is a [] in the end. ++a is syntax error anyway. Also you should complain about ++p vs ++*p for a pointer p ;) Like the () after the function call, which says I want to return the value after the function execution instead of the function object, the [] to my mind also signify I'm working on the content of the array, not the array itself.
 
 Andrei

Sep 26 2008
parent Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
KennyTM~ wrote:
 Andrei Alexandrescu wrote:
 KennyTM~ wrote:
 Andrei Alexandrescu wrote:
 Bruno Medeiros wrote:
 As a matter of coding style conventions, I would say that using the 
 implicit property function call feature on a function that changes 
 state is *bad* style, and surely hope the community would agree on 
 that.

I sure hope they won't agree to an unsupported assertion.

Actually I *do* hate this "feature" ^_^. And this "feature" has been put to challenged before: * Bug 2159. (http://d.puremagic.com/issues/show_bug.cgi?id=2159) * "Omitting Parens is an Evil" (http://www.digitalmars.com/d/archives/digitalmars/D/Omittable_parens_is an_evil_73881.html) *http://www.digitalmars.com/pnews/read.php?server=news.digitalmars.com& roup=D&artnum=17579 But nothing got changed.

I don't think the bug report has much strength.

I mean the discussion within the bug report. That's a voice from the community isn't it?!

I agree. But then should we take arguments at face value, or just consider them valid because they exist?
 To the very least, when using next as a property I assume the call 
 won't produce any secondary effect on src from the syntax, since I'm 
 just reading a state! Not so if one calls src.next() as the "()" is 
 an alarming sign that src _may_ change afterwards. This could is just 
 a problem of personal taste since the compiler does not forbid 
 src.next(), but not so if you're reading other's code.

 P.S. If src.next() is too lengthy, why not just adopt ++src?

Because people (in wake of the recently introduced array operations) may legitimately expect that to mean "increment all elements of src".

It's fine not to use ++src since C++ is the only language that uses this syntax for iterators anyway, but may I mention that, to increase all elements of an array you use ++a[], note that there is a [] in the end. ++a is syntax error anyway. Also you should complain about ++p vs ++*p for a pointer p ;) Like the () after the function call, which says I want to return the value after the function execution instead of the function object, the [] to my mind also signify I'm working on the content of the array, not the array itself.

I agree, hence my "far-fetched" qualification in the reply to Steve. Andrei
Sep 26 2008
prev sibling parent "Bill Baxter" <wbaxter gmail.com> writes:
On Thu, Oct 2, 2008 at 1:22 PM, Michel Fortin <michel.fortin michelf.com> wrote:
 On 2008-09-30 09:42:43 -0400, "Steven Schveighoffer" <schveiguy yahoo.com>
 said:

 "Michel Fortin" wrote
 On 2008-09-29 12:55:18 -0400, "Steven Schveighoffer" said:

 Stdout.newline.formatln("Hi there {}", var);

 Is newline a property of Stdout, on which I'm calling formatln, or is
 newline a function that I'm calling, and then using the result of that
 function to call formatln?  Compare with:

 Stdout.newline().formatln("Hi there {}", var);

 There is no ambiguity.  newline is clearly a function, not a property.

Interesting. To me both of these things have exactly the same meaning. Perhaps it's because I'm used to D, but I think it's more because I can't count how many times I've seen this pattern in C++: SomeSimgleton::Instance().doSomething(); where Instance() is simply an accessor.

Yes, that is a workaround for not having property constructs available. The intuitive label of 'property' comes from the name of the getter 'Instance'. It screams that you are getting the instance of the class, and it's well accepted as a property. What has happened is that instead of relying on the compiler to enforce what is a property and what is not, you are relying on the interpretation of English words.

But one problem is that the distinction between a accessor (of a property) and a function is often matter to interpretation. In the case above, most likely the Instance() function will create the singleton object if it doesn't exist yet; seen that way it isn't a simple accessor. If you ask if Instance() should be a property or not, you're likely to get different answers from different people. Arguably, there are cases even more ambiguous than this one. The issue I want to bring is that without having a formal definition of what is a property and what is not -- one the compiler can enforce -- people will make different choices, not always coherent with each others, and you may very well find yourself having to call Instance() with parenthesis in one D lib and without parenthesis in another.
 But in a language like C#, you would define Instance as a property, not a
 function.  So it would never look like that.

I could, but should I? I'm very interested in your criterions about why Instance would qualify to be a property and not as a function in this case. It's not just me. Just as an example, this article's author doesn't want to decide if Instance() should be a property or not: <http://www.yoda.arachsys.com/csharp/singleton.html> "Note that all of these implementations also use a public static property Instance as the means of accessing the instance. In all cases, the property could easily be converted to a method, with no impact on thread-safety or performance."
 In D, properties are equivalent to functions, so you have a choice as to
 whether to use the parentheses or not.  But that isn't the problem, the
 problem is really that you can call functions as properties.

But what is the difference between a property and a function (beside the syntax?). I can find countless discussions on the net about this; interesting discussions but with no definitive answer. In the end, it all boils down to the author's feeling about his function/property, which may be the opposite from its user's. I'd like very much to quote Bruce Wood from this discussion: <http://bytes.com/forum/thread478064.html> (feel free to comment the quote) """ You see, it's not always so cut-and-dried. On the one hand, if you make something a property, you are implying that getting the information will be quick. So, if getting the information is slow, that is a hint that it shouldn't be a property. On the other hand, you don't want to determine what is a property or not based upon implementation details. By doing so you're letting the internal guts of how your class works leak out into its interface with the outside world. That's bad style. So, something should be a property if it makes sense that it be a property. Certainly, anything that affects the state of the object in a manner more sophisticated than just "set this to that" should be a method. That's painfully obvious. Similarly, an aspect of your class that can be read and set, even if setting it does more than just set a variable, should probably be a property. Reasonably straightfoward. However, sometimes the two design goals above-properties that return results quickly and not letting implementation determine interface-are at odds with each other, and you have to make a judgement call. In the case of my system, I had things that logically should be properties (from an interface point of view) but the object held only keys, not references to the objects themselves, so the "get" on the property has to fetch the object in question from the database. It was an open question whether to code: Supplier theSupplier = stockItem.Supplier; or Supplier theSupplier = stockItem.GetSupplier(); I chose the former, preferring to hide the implementation (holding supplier UID, not supplier itself) rather than let it leak out that getting the supplier was actually a lot of work. In our case it's no problem, because we're a very small shop. In a different situation I might have gone for door #2. """ With the current approach in D, you just don't need to choose, just document what your function/property is doing and be done with it... well you may still have to choose about the name :-), so in a way the argument is still there, but what I mean is that I don't think we really need a black and a white syntax for one concept that may take many shades of gray.
 In my opinion, "newline" isn't a good function name for what it does
 because it has no verb. Having no verb makes it look like an accessor
 much
 more than the absence or presence of parenthesis.

So newline() seems like a property to you more than 'start a new line'? I suppose I can see that, but consider if you were used to a system where properties *prohibited* parentheses, and functions *required* them. It becomes less about interpretation of English vocabulary and more about compiler-enforced interface. I like that system better.

But I'm still puzzled by what being a property means in opposition as if it was a function. The thing is that the compiler just can't guaranty anything about properties: it's really just another syntax for calling a function, a function the author has classified as a property by some subjective standard, a standard which will often differ between authors. Properties are often touted as an indicator for low-cost functions -- whatever "low-cost" means --, but even this fuzzy standard somewhat hinder the often seeked goal of separating interface and implementation. Without aggreement on a formal definition for properties and compiler enforcability, I find it very difficult to justify a formal syntax for properties.

Interesting post. My first thought is what is the formal definition for what should be a struct and what should be a class. That's not a cut-and-dried clear decision either. Either one will generally work. I may think X should be a struct and my users may think it should have been a class. We can both be right. Seems a pretty similar situation. --bb
Oct 01 2008
prev sibling next sibling parent "Bill Baxter" <wbaxter gmail.com> writes:
On Tue, Sep 30, 2008 at 3:36 AM, Steven Schveighoffer
<schveiguy yahoo.com> wrote:
 There is no ambiguity either case. You evaluate Stdout.newline. The
 evaluation yields a value of some type. Then you evaluate formatln against
 that value.

OK, then tell me what this does: x.y.z(); Is y a property/field of x or a function call with no args? I see a benefit to being able to understand a line of code without requiring lots of extra context. I have to do less lookups of the source of a function or property.

The problem with this argument is that unless you disallow properties altogether, you still won't know whether y is actually a field or a call to a property method without looking it up. --bb
Sep 29 2008
prev sibling next sibling parent "Simen Kjaeraas" <simen.kjaras gmail.com> writes:
On Wed, 01 Oct 2008 07:58:20 +0200, Benji Smith <dlanguage benjismith.net>  
wrote:

 Bill Baxter wrote:
 On Tue, Sep 30, 2008 at 3:36 AM, Steven Schveighoffer
 <schveiguy yahoo.com> wrote:
 There is no ambiguity either case. You evaluate Stdout.newline. The
 evaluation yields a value of some type. Then you evaluate formatln  
 against
 that value.

x.y.z(); Is y a property/field of x or a function call with no args? I see a benefit to being able to understand a line of code without requiring lots of extra context. I have to do less lookups of the source of a function or property.

altogether, you still won't know whether y is actually a field or a call to a property method without looking it up. --bb

One thing that I've found especially annoying about the current implementation is this: x.y = 3; // Working code x.y += 3; // Broken, if x.y is actually a function x.y = x.y + 3; // Ugly work-around I've been bitten by this at least a dozen different times. Having a real property syntax would eliminate cases like that. --benji

It seems we will soon get reference return values, in which case this would no longer be a problem. I have also written a property template that enables things such as that, but it would also benefit immensely from reference return values. -- Simen
Oct 01 2008
prev sibling next sibling parent "Simen Kjaeraas" <simen.kjaras gmail.com> writes:
On Wed, 01 Oct 2008 15:28:36 +0200, Andrei Alexandrescu  
<SeeWebsiteForEmail erdani.org> wrote:

 Simen Kjaeraas wrote:
  It seems we will soon get reference return values, in which case this  
 would
 no longer be a problem. I have also written a property template that  
 enables
 things such as that, but it would also benefit immensely from reference
 return values.

Not quite. Sorry, I woke up all negative this morning :o). Consider the .length property for built-in arrays. Better yet, consider you implement your own array and want to define a .length property. You don't want it to return a reference! You want to have control over the assignment so that you execute the actual array change. What I'd proposed to Walter for a long time is to allow a.b = c to work if a.b = a.b c works. I'm sure people around here will find reasons to dislike that :o). Andrei

Ah, of course. So that leaves us with the property template. I've got a fairly nice implementation running, but I dislike the syntax. class foo { int _bar; myStruct _baz; mixin property!(_bar, properties.readwrite, barChanged) bar; // This is ugly. mixin property!(_baz, properties.read) baz; void barChanged(int value) { //... } } This supports all manners of operator overloading at the moment. -- Simen
Oct 01 2008
prev sibling parent "Simen Kjaeraas" <simen.kjaras gmail.com> writes:
On Thu, 02 Oct 2008 03:38:51 +0200, Simen Kjaeraas  
<simen.kjaras gmail.com> wrote:

 On Wed, 01 Oct 2008 15:28:36 +0200, Andrei Alexandrescu  
 <SeeWebsiteForEmail erdani.org> wrote:

 Simen Kjaeraas wrote:
  It seems we will soon get reference return values, in which case this  
 would
 no longer be a problem. I have also written a property template that  
 enables
 things such as that, but it would also benefit immensely from reference
 return values.

Not quite. Sorry, I woke up all negative this morning :o). Consider the .length property for built-in arrays. Better yet, consider you implement your own array and want to define a .length property. You don't want it to return a reference! You want to have control over the assignment so that you execute the actual array change. What I'd proposed to Walter for a long time is to allow a.b = c to work if a.b = a.b c works. I'm sure people around here will find reasons to dislike that :o). Andrei

Ah, of course. So that leaves us with the property template. I've got a fairly nice implementation running, but I dislike the syntax. class foo { int _bar; myStruct _baz; mixin property!(_bar, properties.readwrite, barChanged) bar; // This is ugly. mixin property!(_baz, properties.read) baz; void barChanged(int value) { //... } } This supports all manners of operator overloading at the moment.

Though now that I look at it again (it's been a few months since last I worked on it), I see problems with more complex properties. Ah, more for me to code, then. :p -- Simen
Oct 01 2008
prev sibling next sibling parent "Denis Koroskin" <2korden gmail.com> writes:
On Sat, 27 Sep 2008 17:56:42 +0400, Andrei Alexandrescu  
<SeeWebsiteForEmail erdani.org> wrote:

 Sergey Gromov wrote:
 In article <gbiqbo$m1e$1 digitalmars.com>,  
 SeeWebsiteForEmail erdani.org says...
 Sergey Gromov wrote:
 In article <gbgu0h$5sq$1 digitalmars.com>,  
 SeeWebsiteForEmail erdani.org says...
 Sergey Gromov wrote:
 I think that property function call feature in general adds an  
 unnecessary ambiguity to the language.  I'd prefer functions to be  
 callable only with regular function call syntax, and properties be  
 usable only with member access syntax.  The same stands for  
 'unified function call' feature: if you want to inject a method  
 into an 'array of chars' class you do so explicitly, and only the  
 member call syntax is allowed on that method.  Otherwise code tends  
 to become ambiguous and unreadable.

syntax for genuine member access and member function access helps maintainability because it allows a type implementer to switch back and forth between implementing a property as a direct member or as a function, transparently to that type's use.

between properties and property accessor methods. I'm against using property accessors as methods, and against using methods as if they were property accessors. The current situation actually breaks maintainability because after somebody used .length() you cannot replace it with a public variable anymore. And you cannot replace public void delegate(int) foo; with an accessor method because the code 'instance.foo();' will stop working as it used to.

examples what you mean, also defining all terms (e.g. what does "property accessor" mean?) Thanks.

access a data field. They are called getters and setters in many languages. In current D, any function or method with zero or one argument is considered a property accessor: class Foo { int prop() { return field + 1; } int prop(int value) { return field = value + 3; } private int field; } void useFoo(Foo f) { auto x = f.prop; f.prop = 5; auto y = f.prop(); // also allowed f.prop(8); // ditto }

Ok, so far so good.
 The 'prop' family of methods are property accessors in my terminology,  
 and they can be replaced with an actual data field:
  class Foo
 {
   int prop;
 }
  void useFoo(Foo f)
 {
   auto x = f.prop;  // fine!
   f.prop = 5;  // works
    auto y = f.prop();  // Error: function expected
   f.prop(8);  // Error: function expected
 }
  You see, properties and methods are *not* interchangeable in current  
 D.  Therefore your correct thesis:

 Experience with other languages has shown that using identical  
 syntax for genuine member access and member function access helps  
 maintainability because it allows a type implementer to switch back  
 and forth between implementing a property as a direct member or as a  
 function, transparently to that type's use.




It does apply, just only one direction. From the above, it looks like a good guideline is to always use the syntax: auto x = f.prop; f.prop = x; because it is more general. And indeed, consider something as innocuous as the empty member for a range. Some here said I should write range.empty() throughout. But that, aside from wrist and eye pain, precludes infinite ranges from implementing empty as simple as: struct Generator { ... enum empty = false; } But if that implementation were allowed, that would be a boon for generic code because it can easily detect infinite ranges statically.
 Here's another example:
  class Bar
 {
   int delegate() meth;
 }
  int useBar(Bar b)
 {
   return b.meth();
 }
  Can you replace 'meth' with a getter?
  class Bar
 {
   int delegate() meth()
   {
     return {return 5;};
   }
  }
  int useBar(Bar b)
 {
   return b.meth();  // Error: cannot implicitly convert expression
   // (b.meth()) of type int delegate() to int
 }
  No you cannot.  But you could be able to do all those nice things if  
 you had an explicit syntax for getters and setters:
  class Foo
 {
   property int prop() {...}
   property int prop(int value) {...}
   property int prop(int v, char c) {...} // syntax error, 2 arguments
   property int delegate() meth() {...}
 }

I agree. But there's a simpler solution: int useBar(Bar crystal) { return (crystal.meth)(); } Looks a bit uneasy on the eye? I agree. But count the lines you use member delegates in; then count the lines you use non-delegate member access; divide the first by the second; multiply by 100; and... is that worth a language feature?

It is. A duality like this is bad for language. Should you write foo.bar or foo.bar()? If the former, as you insist, then why not force it? If the latter, then it is not a property anymore. It is a language hack right now! Sergey's proposal is great because it is simple, expressive, sound and it removes the ambiguities that are present now.
Sep 27 2008
prev sibling parent "Denis Koroskin" <2korden gmail.com> writes:
On Sun, 28 Sep 2008 05:45:58 +0400, Andrei Alexandrescu  
<SeeWebsiteForEmail erdani.org> wrote:

 Sergey Gromov wrote:
 Sat, 27 Sep 2008 15:19:01 -0500,
 Andrei Alexandrescu wrote:
 My point is that I agree with all concerns you are raising but I am not
 sure they warrant adding a language feature.

compiler. Now when I think about it I conclude that I was probably wrong. Explicit properties is definitely a feature, even though it seems easy to implement. Injectons could help if Walter were forced into supporting different scoping rules for unified call syntax, but if a.f(b) stays strictly a sugar for f(a,b) this feature helps nothing from a compiler standpoint. So I'll probably agree that these features don't add much to the language, as D doesn't add much to C except safety, productivity, maintainability and claritiy. The clarity/maintainability vs genericity is a tradeoff which is completely in Walter's hands.

Very wise words. I think we all agree that there are some annoyances related to the whole property business, among which the main one is: writeln = 4; That is quite indefensible :o|. I consider the others rather minor, but that's just a personal opinion. How about this. Maybe if we attacked this annoyance in particular, that would be a large bang for the buck without a landslide change in the compiler. We only need some way to inform the compiler, "yes, it's ok to call a.b(c) as a.b = c". Ideas? Andrei

I think property syntax should be disallowed by default. Most of the functions aren't expected to be used as a property, anyway. It would break the code, yes, but it is very easy to fix. Solution of my preference: class Foo { // allow property syntax: property void bar(int baz); // allows "foo.bar = 42;" // allow omitting parens property int bar(); // allows writing "int x = foo.bar;" } Think long term, don't be afraid to break existing code and introduce new features if they improve the language signaficantly. Wrong decision made today may make huge negative impact over time.
Sep 28 2008
prev sibling next sibling parent "Bill Baxter" <wbaxter gmail.com> writes:
On Mon, Sep 29, 2008 at 8:32 AM, Andrei Alexandrescu
<SeeWebsiteForEmail erdani.org> wrote:
 Andrei Alexandrescu wrote:
 How about this. Maybe if we attacked this annoyance in particular, that
 would be a large bang for the buck without a landslide change in the
 compiler. We only need some way to inform the compiler, "yes, it's ok to
 call a.b(c) as a.b = c". Ideas?

I actually did have something in mind when I wrote this, just didn't want to bias anyone. My thinking is that the syntax "a.b = c" in lieu of a.b(c) for a function a.b(T x) should be allowed if and only if there also exists a function a.b() that returns a value of type T. Example: struct S1 { void prop(int x); } S1 s1; s1 = x; // error, prop is not a property struct S2 { void prop(int x); int prop(); } S2 s2; s2.prop = 42; // fine, prop is a property because it also has a getter This solution does not require any syntactic addition. Its drawback is that it makes it hard to define write-only properties. Are they important?

Seems a little too subtle to me. Maybe no problem for the compiler, but yet another kooky rule the human has to hold in their brain when trying to read D code. The rule is trivial, you may say. But how about co-variant return types? If FooSub is a subclass of Foo, then is it ok for the getter to return one and setter to take the other? Or vice versa? Or how about implicit conversions. If my getter takes a long, but my setter returns an int, is that ok? How do const variations fit in? Probably there's a way to fit those all neatly in an automatic rule (or just disallow them), but it just makes the rule longer and even harder to keep in mind (or cuts off functionality people may need). Also how about preventing the calling of property setters as functions? s2.next(42) looks like it will do something quite different from s2.next = 42. I would like for property syntax to also disallow function call syntax. --- Somewhat unrelated, but there still exists the annoyance in D that if you have to functions with the same name and you want to take the address of one of them, you can't. Furthermore I can't think of a reasonable syntax to do that easily. For that reason, I really think the best getter and setter functionality in D would be something where you have distinctly named *functions* getProp and setProp for when you want/need functions mirroring samely-named *properties* for which only property syntax would work. Basically there's no convenient way to take the address of one of a getter/setter function pair, currently. I think that should factor in the solution here. --bb
Sep 28 2008
prev sibling next sibling parent "Bill Baxter" <wbaxter gmail.com> writes:
On Mon, Sep 29, 2008 at 12:53 PM, Andrei Alexandrescu
<SeeWebsiteForEmail erdani.org> wrote:
 Bill Baxter wrote:
 Basically there's no convenient way to take the address of one of a
 getter/setter function pair, currently.  I think that should factor in
 the solution here.

Overloading is the issue, and that's quite a different story.

It's a different, but intertwined story.
 If you know
 the exact type, you can take the address of something.

 struct S
 {
    int foo() {}
    void foo(int) {}
 }

 void main()
 {
    S s;
    void delegate(int) x = &s.foo;
    int delegate() y = &s.foo;
 }

Having to write out "delegate(argumenttype) doesn't qualify as "convenient" in my book. --bb
Sep 28 2008
prev sibling parent "Bill Baxter" <wbaxter gmail.com> writes:
On Mon, Sep 29, 2008 at 1:16 PM, Andrei Alexandrescu
<SeeWebsiteForEmail erdani.org> wrote:
 Bill Baxter wrote:
 On Mon, Sep 29, 2008 at 12:53 PM, Andrei Alexandrescu
 <SeeWebsiteForEmail erdani.org> wrote:
 Bill Baxter wrote:
 Basically there's no convenient way to take the address of one of a
 getter/setter function pair, currently.  I think that should factor in
 the solution here.

Overloading is the issue, and that's quite a different story.

It's a different, but intertwined story.
 If you know
 the exact type, you can take the address of something.

 struct S
 {
   int foo() {}
   void foo(int) {}
 }

 void main()
 {
   S s;
   void delegate(int) x = &s.foo;
   int delegate() y = &s.foo;
 }

Having to write out "delegate(argumenttype) doesn't qualify as "convenient" in my book.

You first used "can't". That doesn't qualify as "can" in my book :o). The "can't" made me honestly think you didn't know about the ability to take the address of an overloaded function/method into a specifically-typed function or delegate respectively. One more instance of the proverbial no good deed that goes unpunished... :o)

Yeh, sorry about that. I vaguely remembered that there was some way to do it, but that it wasn't something I would want to be forced to use whenever I wanted to get a delegate pointer. --bb
Sep 28 2008