digitalmars.D.announce - Revised RFC on range design for D2
- Andrei Alexandrescu (7/7) Sep 11 2008 In wake of the many excellent comments and suggestions made here, I made...
- Benji Smith (24/32) Sep 11 2008 Well done. I think the design has come together very nicely. I'm
- Andrei Alexandrescu (15/53) Sep 12 2008 Consider iterating over an array of int[], specifically an int[][]. Then...
- Benji Smith (6/16) Sep 12 2008 Gotcha. I understand now.
- Bruno Medeiros (9/69) Sep 25 2008 How about having the element type of the
- Sergey Gromov (3/11) Sep 12 2008 I propose to finally move to D group from D.announce. The best way
- Bill Baxter (4/10) Sep 12 2008 Reactor? You meant "refactor" maybe?
- Andrei Alexandrescu (5/17) Sep 12 2008 I think the original bicicle shed story was about a nuclear plant design...
- bearophile (4/4) Sep 12 2008 Andrei Alexandrescu:
- Andrei Alexandrescu (3/9) Sep 12 2008 Fixed, thanks.
- Pablo Ripolles (7/18) Sep 12 2008 Well, it looks prety clean! :D
- Andrei Alexandrescu (8/34) Sep 12 2008 Thanks. One problem in coding with first and last was that sometimes the...
- Pablo Ripolles (6/42) Sep 12 2008 Well, in the first place thanks to you! I really enjoy this enthusiasti...
- Andrei Alexandrescu (4/5) Sep 12 2008 isDone is great, I just wanted to keep the one-word streak going. Let's
- Bill Baxter (12/17) Sep 12 2008 Hmm. std.algorithm does have an "isSorted" function. So I guess I
- Andrei Alexandrescu (7/25) Sep 12 2008 Thing is, people will call isSorted much less often than (isD|d)one. In
- Bill Baxter (18/48) Sep 12 2008 Hmm. One semantic issue I have is that the tip usually refers to the
- Pablo Ripolles (6/60) Sep 12 2008 neither the tip of the tail, nor the tip of the wing, nor the tip of the...
- Bill Baxter (9/31) Sep 12 2008 Nope. I'm pretty sure that the discussion is about replacing "head"
- Sean Kelly (5/32) Sep 12 2008 There's also a song entitled "tiptoe through the tulips," which will
- Jerry Quinn (6/40) Sep 12 2008 Another few possibilities
- downs (3/44) Sep 12 2008 Bow/stern?
- Bill Baxter (10/23) Sep 12 2008 PeanutButter/Jelly?
- Sergey Gromov (9/43) Sep 13 2008 AFAIK, after Andrei switched to isEmpty/next/first for an Input range
- Pablo Ripolles (15/43) Sep 12 2008 brilliant! I think "tip" is just fantastic!
- KennyTM~ (8/29) Sep 13 2008 I don't think people know what rng.tip means at the first glance. I
- Pablo Ripolles (4/33) Sep 14 2008 Well, me too although I misinterpreted as an alternative to .toe which w...
- Benji Smith (7/9) Sep 12 2008 When I see "x.tip", it makes me think of the verb ("a function that tips...
- Pablo Ripolles (5/33) Sep 12 2008 A perhaps more aeronautical term but short and nice as an alternative to...
- Bill Baxter (9/42) Sep 12 2008 According to http://en.wikipedia.org/wiki/Aft
- Pablo Ripolles (3/54) Sep 12 2008 Ok, that sounds more convincing than "head"/"toe", that's for sure.
- Leandro Lucarella (9/14) Sep 12 2008 I much prefer done, because is shorter, and, well, maybe because I hate
- Pablo Ripolles (3/12) Sep 12 2008 I do not like it either, we already have it though. I definitely prefer...
- Pablo Ripolles (3/12) Sep 12 2008 I do not like it either, we already have it though. I definitely prefer...
- Walter Bright (4/11) Sep 12 2008 One consideration when selecting names is how it would interact with
- Andrei Alexandrescu (6/18) Sep 12 2008 I agree. Sorry tipsters. I think head/toe has one established thing and
- Bill Baxter (5/24) Sep 12 2008 Head / foot!
- Dave (10/50) Sep 12 2008 From the limited posts I've had the time to read, it seems this topic ha...
- Andrei Alexandrescu (4/58) Sep 14 2008 Intersect is good but doesn't reveal an important detail: the operation
- Pablo Ripolles (4/62) Sep 15 2008 What about wedge(r, s) or r.wedge(s)? Perhaps meet(r, s) or r.meet(s)?
- KennyTM~ (6/66) Sep 15 2008 .cross ?
- Andrei Alexandrescu (4/73) Sep 15 2008 In the forward ranges realm, if you start from the wrong range, you
- Lars Kyllingstad (4/12) Sep 16 2008 How about 'overlap', then? Still not perfect, but at least it doesn't
- Russell Lewis (5/7) Sep 15 2008 When I first read this, I thought it said
- Ary Borenszweig (11/22) Sep 12 2008 Just a little typo, in the "Output range" example:
- Andrei Alexandrescu (3/28) Sep 12 2008 Fixed, thanks Ary.
- Sergey Gromov (39/47) Sep 12 2008 I've got a heap of virtual sticky notes all over my monitor while
- Andrei Alexandrescu (37/87) Sep 12 2008 The art is to find something that's reasonably evocative without being
- Sergey Gromov (2/11) Sep 12 2008 Then, in Input range primitives, e=r.next must be just r.next.
- Andrei Alexandrescu (5/16) Sep 12 2008 Oh, now I see. Fixed.
- Bill Baxter (70/76) Sep 12 2008 Looking better!
- Sergey Gromov (20/57) Sep 12 2008 I've got a bit of insight! XD
- Bill Baxter (19/76) Sep 12 2008 So basically you changed
- Sergey Gromov (8/85) Sep 12 2008 The insight was about get/put ==> next. That's the most significant
- Bill Baxter (14/23) Sep 12 2008 Ah ok. Your switching to declaration syntax instead of usage syntax
- Sergey Gromov (6/32) Sep 12 2008 Exactly, I wanted it to be
- Andrei Alexandrescu (6/33) Sep 12 2008 Hmm, let's see. So:
- Sergey Gromov (11/47) Sep 12 2008 Not quite.
- Andrei Alexandrescu (11/58) Sep 12 2008 Given that in D const is transitive, we can't operate with const the
- Sergey Gromov (6/61) Sep 12 2008 If Src is an input range you must make a deep copy. This holds true for...
- Andrei Alexandrescu (4/64) Sep 12 2008 It is doable, and if not, Walter will make it so :o).
- Sergey Gromov (51/69) Sep 13 2008 Then I'll continue to think aloud.
- Michel Fortin (6/8) Sep 14 2008 But then, why not .next() and .pull()? They're the same length too. :-)
- Bill Baxter (20/24) Sep 14 2008 My reasoning was that if you pick any pair of antonyms then its going
- Andrei Alexandrescu (40/59) Sep 14 2008 [snip]
- Sergey Gromov (23/76) Sep 16 2008 Thinking aloud is fun. :P
- Andrei Alexandrescu (41/128) Sep 16 2008 I figured a deceptively simple(r) solution. With apologies to Abba, I
- Sergey Gromov (3/109) Sep 17 2008 Yes it should work. I wish it were less complex though. BTW here's
- Andrei Alexandrescu (23/36) Sep 12 2008 That's exactly right. "Empty" is a state, and "done", while also a
- Andrei Alexandrescu (9/48) Sep 12 2008 Are you kidding? I wrote the best swap in the world. Check the source of...
- Bill Baxter (13/27) Sep 12 2008 I mean like stdvector.swap(stdvector0), not the algorithm swap. Or
- Steven Schveighoffer (16/22) Sep 12 2008 You know my position ;) But here are some things:
- Andrei Alexandrescu (12/41) Sep 12 2008 http://www.merriam-webster.com/dictionary/organic
- Jason House (3/11) Sep 12 2008 The colorful diagram is nice, but it doesn't belong at the top of the ar...
- Andrei Alexandrescu (6/23) Sep 12 2008 I agree. I just plopped it in there in an intermediate revision and left
- Sergey Gromov (15/23) Sep 12 2008 I've found superdan's post that convinced you. I honestly cannot see
- Andrei Alexandrescu (5/33) Sep 12 2008 He did convince me. With your API you can't write efficient algorithms
- Ary Borenszweig (21/32) Sep 12 2008 Two questions:
- Sergey Gromov (5/12) Sep 12 2008 The idea is that a container is responsible for mutators, not range.
- Andrei Alexandrescu (6/42) Sep 12 2008 Never. Ranges never grow. You need access to the "mother" container,
- Walter Bright (3/12) Sep 12 2008 I agree. I don't think it is ever a good idea to try to add/remove
- Steven Schveighoffer (11/23) Sep 12 2008 I allow removal during foreach in dcollections. However, it is done by ...
- Sean Kelly (18/31) Sep 12 2008 I've found this invaluable at times. And it's actually supported by C++...
- Walter Bright (4/6) Sep 12 2008 It's a bad idea, as it can screw up optimization. For example, if you
- Sean Kelly (4/11) Sep 12 2008 That's fine... the optimizer has to impose certain requirements. For
- Andrei Alexandrescu (8/15) Sep 12 2008 I like the erase(remove) idiom used by STL, see
- Sergey Gromov (5/27) Sep 13 2008 I think that the Input Range abstraction works fine here. That is, the
- Pablo Ripolles (6/44) Sep 12 2008 I agree with that! indeed it is.
- Pablo Ripolles (5/59) Sep 13 2008 After having had some sleep, I kind of support Bill's proposal, that is,...
- Michel Fortin (6/16) Sep 14 2008 Anyone suggested head/rear yet?
- Pablo Ripolles (2/16) Sep 14 2008 Hello, if I remember well, yes. I also like it! I don't recall the arg...
- Bill Baxter (5/21) Sep 14 2008 I think it was that if you're thinking body parts, then "rear" means
- bearophile (35/36) Sep 16 2008 I have re-read the page again, and I am now starting to understand its c...
- Andrei Alexandrescu (23/65) Sep 16 2008 That's a good point. At the time I wrote that document, inserting the
- Bruno Medeiros (20/31) Sep 25 2008 """
- Sergey Gromov (10/21) Sep 25 2008 I think that property function call feature in general adds an
- Andrei Alexandrescu (12/34) Sep 25 2008 Experience with other languages has shown that using identical syntax
- Sergey Gromov (11/26) Sep 25 2008 Sorry I may have been unclear. I'm not against interchangeability
- Andrei Alexandrescu (5/33) Sep 26 2008 I am a bit confused about terminology. Could you please clarify with
- Sergey Gromov (108/146) Sep 26 2008 Property accessor is a method which is invoked when you syntactically
- Andrei Alexandrescu (28/198) Sep 27 2008 It does apply, just only one direction. From the above, it looks like a
- Sergey Gromov (45/157) Sep 27 2008 Now you say that for maintainability to work all users of your library
- Jason House (4/20) Sep 27 2008 I would really hope that the D2 compiler would flag Foo.foo.even as a
- Andrei Alexandrescu (4/170) Sep 27 2008 My point is that I agree with all concerns you are raising but I am not
- Sergey Gromov (13/15) Sep 27 2008 I hoped for some reason that these features could simplify the compiler....
- Andrei Alexandrescu (12/29) Sep 27 2008 Very wise words.
- Chris R. Miller (14/47) Sep 27 2008 Just trying to think of something that could be easily parsed...
- Sergey Gromov (4/33) Sep 28 2008 I cannot see how trickery with an existing syntax is less a feature than...
- Chris R. Miller (6/39) Sep 28 2008 Awe, but making the syntax more complex makes the code look so much more...
- Jason House (9/21) Sep 28 2008 That seems like a bad idea if it allows a forgetful/lazy/overworked libr...
- Andrei Alexandrescu (3/26) Sep 28 2008 But I'm talking about property set syntax, not get.
- torhu (9/22) Sep 28 2008 Using an equals sign to say that assignment syntax is allowed seems natu...
- torhu (2/4) Sep 28 2008 Oops, I meant "can be called as 'prop;' or 'prop = 7;'
- Chris R. Miller (6/34) Sep 28 2008 My concern is that this could break existing code. If the presence of
- torhu (4/24) Sep 28 2008 Well, isn't this just for D 2.0? The implicit property setters could be...
- Chris R. Miller (9/34) Sep 28 2008 D 2.0 isn't so different that every line of code will have to be
- Denis Koroskin (16/43) Sep 28 2008 I think property syntax should be disallowed by default. Most of the
- Andrei Alexandrescu (5/61) Sep 28 2008 Without solid argument, all this is just talk. What is the negative
- Chris R. Miller (19/83) Sep 28 2008 Improper use of properties make code that looks ambiguous. Is that a
- Andrei Alexandrescu (15/39) Sep 28 2008 How? Why?
- Andrei Alexandrescu (15/39) Sep 28 2008 How? Why?
- Chris R. Miller (30/73) Sep 28 2008 My lack of example situations is offset by my fear of one day finding
- Andrei Alexandrescu (4/8) Sep 28 2008 That was an interesting thread. I am glad you decided to learn STL, you
- Leandro Lucarella (15/23) Sep 29 2008 In Java you have the exact same problem. Since it doesn't support
- Chris R. Miller (28/45) Sep 29 2008 In Java for some reason the "Industry Standard" was that using
- Andrei Alexandrescu (8/60) Sep 29 2008 Great point. This getFoo/setFoo crap is so retarded. It is a good
- Andrei Alexandrescu (4/91) Sep 28 2008 Oh, besides - if computed properties are allowed, they also can perform
- KennyTM~ (5/61) Sep 29 2008 Or just issue a warning if property syntax is used without the
- Sergey Gromov (5/7) Sep 29 2008 Right now warnings in DMD are either not shown or are considered errors....
- Andrei Alexandrescu (23/27) Sep 28 2008 I actually did have something in mind when I wrote this, just didn't
- Kenny B (3/12) Sep 28 2008 For the record, you have my vote on this idea. it would greatly simplify...
- Bill Baxter (30/57) Sep 28 2008 Seems a little too subtle to me. Maybe no problem for the compiler,
- Andrei Alexandrescu (24/90) Sep 28 2008 Yah, I was thinking of all these and then some more while I was posting.
- Bill Baxter (6/25) Sep 28 2008 It's a different, but intertwined story.
- Andrei Alexandrescu (7/35) Sep 28 2008 You first used "can't". That doesn't qualify as "can" in my book :o).
- Bill Baxter (6/44) Sep 28 2008 Yeh, sorry about that. I vaguely remembered that there was some way
- Sergey Gromov (13/34) Sep 29 2008 D has a simple rule for property methods. This rule has side effects.
- Andrei Alexandrescu (6/41) Sep 29 2008 A function can return an object that allows assignment even today with
- Sergey Gromov (4/22) Sep 29 2008 Today the compiler tries to call Range.head with one argument and fails....
- Andrei Alexandrescu (3/23) Sep 29 2008 Well I'd rather say, an awkwardness that needs fixing.
- KennyTM~ (11/57) Sep 29 2008 Just wondering... What about opCall's? Will it be considered while
- Bruno Medeiros (26/37) Oct 02 2008 Hum, that reminds me of an idea I once had for properties: not using a
- Andrei Alexandrescu (4/44) Oct 02 2008 What's going on now is pretty much that except that there's no more
- Ary Borenszweig (4/51) Oct 02 2008 Pretty much, but not exactly the same, because in this way you can't
- Michel Fortin (57/69) Sep 28 2008 I think having a function returing *the same type* is probably too
- Andrei Alexandrescu (34/116) Sep 28 2008 Great point. I think the rule suggested in my latest post convers this.
- Michel Fortin (93/212) Sep 29 2008 Ok, so you determine if a function can be called by "=" before checking
- Andrei Alexandrescu (28/152) Sep 29 2008 That means indentLevel is not a property and consequently "generic"
- Michel Fortin (47/78) Sep 29 2008 Ok, lets look back at what I wrote and what you replied in your previous...
- Andrei Alexandrescu (16/105) Sep 29 2008 It's my fault, I misunderstood you. Sorry. This case could go either
- Michel Fortin (50/52) Sep 30 2008 And I think this is why binding the availability of the proprety syntax
- Andrei Alexandrescu (6/64) Sep 30 2008 I think the argument is overstated and a corner case is overblown, but I...
- Steven Schveighoffer (10/20) Sep 30 2008 What about functions with default parameters?
- Lionello Lunesu (3/4) Sep 27 2008 I think that's a valid concern. C/C++ code is full of "()"-pairs and the...
- Denis Koroskin (8/142) Sep 27 2008 It is. A duality like this is bad for language. Should you write foo.bar...
- Yigal Chripun (28/66) Sep 25 2008 I've got a few questions regarding this:
- Andrei Alexandrescu (41/107) Sep 25 2008 It would solve the problem by adding a language feature. It is unclear
- Bruno Medeiros (13/43) Sep 26 2008 Yet, as Sergey mentioned, the switching back cannot be made safely if
- Bruno Medeiros (10/32) Sep 26 2008 Yes, I full agree, as many others do, as this has been discussed before
- Andrei Alexandrescu (10/41) Sep 26 2008 Following that link I see there is a problem with accessing .mangleof.
- Bent Rasmussen (3/11) Sep 26 2008 Or abstracted and generic.
- Andrei Alexandrescu (3/13) Sep 27 2008 Well put!
- Andrei Alexandrescu (5/38) Sep 25 2008 With me it's the opposite, particularly after I've written and stared at...
- Bruno Medeiros (10/53) Sep 26 2008 It's unsupported because it is not an assertion, but more of a
- Andrei Alexandrescu (11/63) Sep 26 2008 I also don't like broccoli, but I eat it because there are reasons to do...
- Bruno Medeiros (31/100) Oct 02 2008 Yes, if there where good reasons, I'd use it. But what are those reasons...
- KennyTM~ (15/30) Sep 26 2008 Actually I *do* hate this "feature" ^_^.
- Andrei Alexandrescu (5/32) Sep 26 2008 Because people (in wake of the recently introduced array operations) may...
- Steven Schveighoffer (35/38) Sep 26 2008 So in one case, you believe people's assumptions aren't important, i.e. ...
- Andrei Alexandrescu (9/60) Sep 26 2008 Of course it is. One expectation has to do with operational consistency
- Steven Schveighoffer (37/66) Sep 26 2008 You are assuming that the C language decision to require parentheses for...
- Andrei Alexandrescu (16/84) Sep 26 2008 I understand your argument, but it hinges on its own definitions and
- 0ffh (12/20) Sep 26 2008 Yum. I think the problem is that when C was designed, K&R didn't conside...
- Sergey Gromov (4/24) Sep 26 2008 Functions in D are first-class values. It's awkward that a first-class
- 0ffh (4/14) Sep 26 2008 Awkward my sed! Why should a parameterless call be *the* natural meaning
- KennyTM~ (36/51) Sep 27 2008 It may not be "the" natural thing, but it is confusing.
- Andrei Alexandrescu (13/36) Sep 27 2008 I agree. But then use of functions for invocation dwarfs use of
- Sergey Gromov (5/11) Sep 27 2008 Because I use class and struct fields, I use properties, I use
- Andrei Alexandrescu (3/15) Sep 27 2008 enforce :o)
- Yigal Chripun (12/28) Sep 27 2008 the above POV seems skewed to me. D is not C! instead of relying on old
- Don (13/45) Sep 29 2008 By the way, D supports declarations of function type (not function
- Andrei Alexandrescu (14/64) Sep 29 2008 Heh. That is eerily similar to the way C handles it - here's an example:
- Don (3/74) Sep 29 2008 Is there a use case for this? I've only ever found it to be a nuisance,
- Bruno Medeiros (10/86) Oct 02 2008 What exactly are you trying to discard? Just the
- Bruno Medeiros (11/22) Oct 03 2008 What's the problem with lazy? I mean, concretely?
- Andrei Alexandrescu (12/32) Sep 27 2008 No, that decision didn't make sense regardless of classes and functions....
- Steven Schveighoffer (51/134) Sep 29 2008 Forgive me, but I must have missed it. I saw only your assumption (or
- Andrei Alexandrescu (39/189) Sep 29 2008 My post on 25 Sep 2008 17:58:52 -0500 mentions:
- Steven Schveighoffer (42/237) Sep 29 2008 Hidden in this statement is that you consider () to be semantically
- Bill Baxter (6/14) Sep 29 2008 The problem with this argument is that unless you disallow properties
- KennyTM~ (5/23) Sep 29 2008 I think the distinction of with and without () is pretty stylistic,
- Andrei Alexandrescu (4/33) Sep 29 2008 But range.next does modify the object. On what basis do I need to add
- KennyTM~ (2/37) Sep 29 2008 Well that's why *I* would use range.next() ;)
- Bill Baxter (9/39) Sep 29 2008 I think the counter-argument to what I just said is that if properties
- Andrei Alexandrescu (24/60) Sep 29 2008 So the logic is:
- Bruno Medeiros (7/72) Oct 02 2008 Uh, of course, and that's why we want this convention to be enforced by
- Andrei Alexandrescu (4/62) Oct 02 2008 A generic function writing obj.symbol cannot work across objects that
- Bruno Medeiros (8/52) Oct 02 2008 I'm not following. If 'obj.symbol' is syntactically available and is
- Andrei Alexandrescu (9/63) Oct 02 2008 What I meant is very simple. Generic code benefits from unified syntax.
- Bruno Medeiros (9/69) Oct 03 2008 Ah, got it now. I misunderstood the original "So the logic is:" post.
- Jason House (3/29) Sep 29 2008 I assume you'd also want parens if the function modified global state? I...
- KennyTM~ (5/34) Sep 30 2008 Right. Therefore if I were to implement property getters I would require...
- Jason House (3/39) Sep 30 2008 Const and immutable functions can modify global state... So I don't thin...
- KennyTM~ (10/24) Sep 30 2008 Does "pure" in an object method takes the object itself as input also?
- Sergey Gromov (9/11) Oct 01 2008 I don't know whether it ever was invariant, but the current
- Steven Schveighoffer (12/32) Sep 29 2008 Yes, but that is not in question. A property method is *meant* to be us...
- Benji Smith (9/27) Sep 30 2008 One thing that I've found especially annoying about the current
- Simen Kjaeraas (9/38) Oct 01 2008 It seems we will soon get reference return values, in which case this wo...
- KennyTM~ (16/59) Oct 01 2008 It's still an issue if we want to support property setters, say
- Andrei Alexandrescu (3/73) Oct 01 2008 Ah, didn't see your post before I posted a similar argument.
- Steven Schveighoffer (4/64) Oct 01 2008 Yes yes!
- Bill Baxter (4/23) Oct 01 2008 Me too++
- Andrei Alexandrescu (10/52) Oct 01 2008 Not quite. Sorry, I woke up all negative this morning :o).
- Simen Kjaeraas (19/35) Oct 01 2008 Ah, of course. So that leaves us with the property template. I've got a
- Simen Kjaeraas (7/46) Oct 01 2008 Though now that I look at it again (it's been a few months since last I ...
- Andrei Alexandrescu (42/110) Sep 29 2008 Sheesh, do I really need to spoonfeed all of this? First I mentioned the...
- Steven Schveighoffer (24/63) Sep 29 2008 Sorry, I look at things more simply, and I'm not really into language de...
- Steven Schveighoffer (15/49) Sep 29 2008 This is what happens when you can't explain yourself correctly. Stop ty...
- Andrei Alexandrescu (14/65) Sep 29 2008 "State" is very accurate, because aside from stating the point you bring...
- Steven Schveighoffer (14/80) Sep 29 2008 Again with the 'nothing to support it'. If you're not going to listen t...
- Andrei Alexandrescu (31/48) Sep 29 2008 You said something wrong. I understood what you said, I understood in
- Bruno Medeiros (40/62) Oct 02 2008 Good post.
- Andrei Alexandrescu (26/62) Oct 02 2008 Yah, he made great points. There are ambiguities. The counter-argument
- Bruno Medeiros (20/80) Oct 02 2008 Yes, if we solved the ambiguity problem that way, we would make a lot of...
- Steven Schveighoffer (56/62) Oct 02 2008 In fact, C++.net did something similar in that if you defined a property...
- Bruno Medeiros (8/36) Oct 03 2008 Just for the record, I didn't present that suggestion because it didn't
- Andrei Alexandrescu (14/35) Oct 02 2008 So there is a cabal. I knew it! :o)
- Fawzi Mohamed (21/60) Oct 02 2008 Here comes another person that would be unhappy without omittable parent...
- Bill Baxter (4/10) Oct 02 2008 Fortunately this is an orthogonal issue. This could (and should IMO)
- Andrei Alexandrescu (7/19) Oct 02 2008 Yah, overloaded ops are due for an overhaul. I'm almost afraid to ask......
- Sergey Gromov (3/9) Oct 02 2008 What's wrong with a.opIndexAssign(b, a.opIndex(b) + c)?
- Bill Baxter (3/12) Oct 02 2008 Indeed. I thought there wasn't a lot of debate needed on this, just act...
- Bill Baxter (8/21) Oct 02 2008 ... except these extras do have the same issue that plain property
- Sergey Gromov (6/29) Oct 02 2008 I think that any expression "a @= b" should first check whether the
- Andrei Alexandrescu (3/31) Oct 03 2008 That's a good rule, but a[b] @= c on a hash is not helped by it.
- Steven Schveighoffer (25/59) Oct 03 2008 I think Sergey didn't write his rules correctly. If a is an lvalue, use...
- Steven Schveighoffer (8/67) Oct 03 2008 After thinking about this some more, I don't even thing the rvalue/lvalu...
- Bill Baxter (5/8) Oct 02 2008 Doh, obviously writefln is a bad example there. Imagine a function
- Christopher Wright (4/15) Oct 02 2008 How about this? Start trusting programmers not to do nonsensical things....
- KennyTM~ (7/17) Oct 03 2008 Probably performance.
- Andrei Alexandrescu (5/26) Oct 03 2008 Glad you brought opIndexAddAssign up. I think a good solution would
- KennyTM~ (2/30) Oct 03 2008 Of course I don't want opIndexAddAssign either ;p.
- Christopher Wright (26/58) Oct 04 2008 That would be hell on generic programming, too. These would only be
- Sergey Gromov (8/20) Oct 03 2008 No, if you want performance in this particular case you define
- Bill Baxter (6/26) Oct 03 2008 Yeh, but then you lose encapsulation. That's fine for some cases,
- Sergey Gromov (54/83) Oct 03 2008 Your two last posts are very close to what I have on my mind myself, and...
- Charles Hixson (6/27) Oct 03 2008 But if the requirement is that it should have the same *meaning* as
- Andrei Alexandrescu (4/32) Oct 03 2008 That's a good point. Compiler magic can make sure stuff like that gets
- Andrei Alexandrescu (6/16) Oct 03 2008 One problem is that for a hashtable that does not have b yet, opIndex
- Sergey Gromov (14/31) Oct 03 2008 The latter is not a problem if you always try to use a[b] as an lvalue
- Andrei Alexandrescu (9/41) Oct 03 2008 Yah, Walter, Bartosz and I discussed this (under the name
- Bill Baxter (43/60) Oct 03 2008 I don't see why you expect a[b] += c to work on a key for which a[b]
- Andrei Alexandrescu (21/83) Oct 03 2008 Because accessing it as an rvalue is different than accessing it as an
- KennyTM~ (3/20) Oct 03 2008 Actually I did use it once to count things, so I could just use a[b]++
- Bill Baxter (5/34) Oct 03 2008 But D hashmaps don't work like that.
- Andrei Alexandrescu (5/32) Oct 03 2008 No, it's not std::map. Actually, much to everyone's confusion, Walter
- Bill Baxter (4/47) Oct 03 2008 Wierd. But yet plain a[b] will still throw an exception?
- Bruno Medeiros (5/29) Oct 03 2008 Agh, this was a clear and good opportunity to fork a new thread.
- Bruno Medeiros (7/14) Oct 03 2008 Like I said before to Andrei, the idea wasn't simply to drop the
- Bruno Medeiros (23/58) Oct 03 2008 If there is a cabal, it's a pretty lame one... Walter's Inner Circle are...
- Andrei Alexandrescu (5/25) Oct 03 2008 What I'd consider is not that important. I do know what Walter's
- Benji Smith (3/19) Oct 03 2008 If I understand the proposal correctly, it would be a compile-time error...
- Bruno Medeiros (13/43) Oct 06 2008 By "taking into consideration" I don't mean that he would implemented it...
- KennyTM~ (28/47) Oct 02 2008 I think someone should start a new thread on this? The argument is not
- Bill Baxter (14/26) Oct 02 2008 Yeh, and that's because in math there *are* no zero argument functions
- Bruno Medeiros (5/7) Oct 02 2008 Correction, Ruby allows it as well. Hum.
- Andrei Alexandrescu (29/106) Sep 29 2008 I'd also disagree with that, and I never said that either. What I said
- Benji Smith (14/26) Sep 30 2008 I'd like to propose another principle of language design:
- Andrei Alexandrescu (15/42) Oct 01 2008 Consistency is good, but that's not consistency.
- Bruno Medeiros (17/59) Oct 02 2008 The concept of "consistency" has a more specialized meaning when applied...
- Andrei Alexandrescu (21/81) Oct 02 2008 No. Not at all. For consistency you must assert together something with
- Ian Sakkis (20/26) Oct 02 2008 Consistency or economy of syntax I think you are avoiding the issue whic...
- Andrei Alexandrescu (5/8) Oct 02 2008 Then revision before posting might have been an option. There's no "me"
- Bruno Medeiros (28/114) Oct 02 2008 I wasn't talking about the two cases of calling a zero-args function
- Andrei Alexandrescu (6/49) Oct 02 2008 It's really simple in fact. For example one desideratum is to not
- Sean Kelly (34/41) Oct 02 2008 I apologize if this has been brought up before, but I'm not sure that si...
- Fawzi Mohamed (6/55) Oct 02 2008 Ok this is a good reason to have real properties, a somewhat exotic
- Andrei Alexandrescu (7/51) Oct 02 2008 Oh how I am with you on this. I've always thought mentioning a delegate
- Bill Baxter (5/17) Oct 02 2008 He mentions your name right there on the description of lazy, though.
- Andrei Alexandrescu (12/31) Oct 02 2008 I suggested that a function taking a delegate should accept an unadorned...
- Tom S (20/32) Oct 02 2008 I guess that someone was me (and at the NG, so you remember right). But
- Bruno Medeiros (10/70) Oct 03 2008 Hold it right there! Are you saying that the existence of 'lazy' somehow...
- Fawzi Mohamed (8/72) Oct 03 2008 I think that he meant that lazy automatically evaluates a delegate, and
- Andrei Alexandrescu (6/12) Oct 03 2008 The connection is indirect. Lazy showed that omitting trailing parens
- Bruno Medeiros (8/22) Oct 06 2008 So your point before was, that you are criticizing lazy because it
- Fawzi Mohamed (14/70) Oct 03 2008 I think here the problem is different
- Michel Fortin (29/32) Oct 03 2008 Hum... it could work a little like references (&) in C++. Basically, if
- Yigal Chripun (28/61) Oct 01 2008 I was about to post a very similar post (it's still in my drafts folder)
- Andrei Alexandrescu (6/69) Oct 01 2008 A delegate /is/ a function pointer, more precisely, a pointer to
- Yigal Chripun (17/90) Oct 01 2008 I understand a delegate as a struct of two pointers as you said so
- Andrei Alexandrescu (3/81) Oct 01 2008 What is the signature of that function?
- Yigal Chripun (12/99) Oct 01 2008 say we have:
- Andrei Alexandrescu (5/106) Oct 01 2008 C++ pointers to member functions aren't that well designed. But at least...
- Yigal Chripun (11/126) Oct 01 2008 if I understand it correctly, in C++ the type of this is encoded in the
- Andrei Alexandrescu (5/129) Oct 01 2008 It's great that you are now equipped to appreciate the advantages and
- Yigal Chripun (8/142) Oct 01 2008 my main issue was with the syntax. and I still think it's inconsistent.
- Andrei Alexandrescu (3/146) Oct 01 2008 I think we're better off with the current state of affairs.
- Yigal Chripun (8/169) Oct 01 2008 so if I understand you correctly, you don't see any other disadvantages.
- superdan (2/174) Oct 01 2008 way i read it he was tryin' to save you face, but of course you missed t...
- Christopher Wright (15/30) Oct 01 2008 &obj.method returns a pointer to a method for this instance.
- Walter Bright (48/50) Oct 01 2008 One flag that something wacky is going is to look at the compiler code
- Michel Fortin (14/23) Sep 29 2008 Interesting. To me both of these things have exactly the same meaning.
- Steven Schveighoffer (18/36) Sep 30 2008 Yes, that is a workaround for not having property constructs available. ...
- Michel Fortin (88/129) Oct 01 2008 But one problem is that the distinction between a accessor (of a
- Bill Baxter (8/137) Oct 01 2008 Interesting post. My first thought is what is the formal definition
- Michel Fortin (44/50) Oct 02 2008 Yes, indeed, often both can be right in many situations. That's a very
- Bill Baxter (43/51) Oct 02 2008 Good points.
- Andrei Alexandrescu (4/63) Oct 02 2008 Yes, I entirely agree. I mentioned this a couple of times, but never
- Benji Smith (10/79) Oct 02 2008 Me too.
- Michel Fortin (19/21) Oct 02 2008 Hum, I think I forgot to answer that part.
- Andrei Alexandrescu (6/9) Oct 02 2008 I agree. Moreover, there should be arguments on why the distinction
- Steven Schveighoffer (91/222) Oct 02 2008 The difference is syntax-implied meaning. A property is a field impleme...
- Michel Fortin (10/13) Sep 27 2008 I'd argue that for readability of the code "newline" is a bad function
- Bruno Medeiros (5/9) Oct 02 2008 Dear Gods! O_o
- Andrei Alexandrescu (4/10) Oct 02 2008 What are you gonna do? People are different. If the best you can say
- Christopher Wright (19/40) Sep 26 2008 What if it were src.advance instead? Since it's a verb, it's easier to
- Michel Fortin (14/35) Sep 27 2008 Indeed. I think all function names which are not simple getters or
- KennyTM~ (12/49) Sep 26 2008 I mean the discussion within the bug report. That's a voice from the
- Andrei Alexandrescu (5/56) Sep 26 2008 I agree. But then should we take arguments at face value, or just
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
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
Benji Smith wrote:Andrei Alexandrescu wrote:Fixed, thanks.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?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
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
Andrei Alexandrescu wrote:Benji Smith wrote: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#DAndrei Alexandrescu wrote:Fixed, thanks.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?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 25 2008
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
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
Bill Baxter wrote:On Fri, Sep 12, 2008 at 2:44 PM, Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> wrote: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. AndreiIn 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?
Sep 12 2008
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
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, bearophileFixed, thanks. Andrei
Sep 12 2008
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). AndreiWell, 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
Pablo Ripolles 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) AndreiIn 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). AndreiWell, 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
Andrei Alexandrescu Wrote:Pablo Ripolles wrote: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!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) AndreiIn 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). AndreiWell, 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
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
On Fri, Sep 12, 2008 at 11:39 PM, Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> wrote:Pablo Ripolles wrote: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. --bbWhat about "isDone"?isDone is great, I just wanted to keep the one-word streak going. Let's see what everyone else says.
Sep 12 2008
Bill Baxter wrote:On Fri, Sep 12, 2008 at 11:39 PM, Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> wrote: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.Pablo Ripolles wrote: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.What about "isDone"?isDone is great, I just wanted to keep the one-word streak going. Let's see what everyone else says.
Sep 12 2008
On Sat, Sep 13, 2008 at 12:03 AM, Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> wrote:Bill Baxter 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! --bbOn Fri, Sep 12, 2008 at 11:39 PM, Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> wrote: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.Pablo Ripolles wrote: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.What about "isDone"?isDone is great, I just wanted to keep the one-word streak going. Let's see what everyone else says.
Sep 12 2008
Bill Baxter Wrote:On Sat, Sep 13, 2008 at 12:03 AM, Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> wrote: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!Bill Baxter 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! --bbOn Fri, Sep 12, 2008 at 11:39 PM, Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> wrote: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.Pablo Ripolles wrote: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.What about "isDone"?isDone is great, I just wanted to keep the one-word streak going. Let's see what everyone else says.
Sep 12 2008
On Sat, Sep 13, 2008 at 1:03 AM, Pablo Ripolles <in-call gmx.net> wrote: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.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! --bbneither 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.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
Bill Baxter wrote:On Sat, Sep 13, 2008 at 1:03 AM, Pablo Ripolles <in-call gmx.net> wrote: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.Nope. I'm pretty sure that the discussion is about replacing "head" with "tip". There's an expression "from tip to toe".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! --bbneither 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"...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
Sean Kelly Wrote:Bill Baxter wrote:Another few possibilities fore/aft front/back start/end begin/endOn Sat, Sep 13, 2008 at 1:03 AM, Pablo Ripolles <in-call gmx.net> wrote: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.Nope. I'm pretty sure that the discussion is about replacing "head" with "tip". There's an expression "from tip to toe".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! --bbneither 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"...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?
Sep 12 2008
Jerry Quinn wrote:Sean Kelly Wrote:Bow/stern? Of course, if we ever get a tree implementation, we have to use port/starboard :pBill Baxter wrote:Another few possibilities fore/aft front/back start/end begin/endOn Sat, Sep 13, 2008 at 1:03 AM, Pablo Ripolles <in-call gmx.net> wrote: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.Nope. I'm pretty sure that the discussion is about replacing "head" with "tip". There's an expression "from tip to toe".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! --bbneither 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"...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?
Sep 12 2008
On Sat, Sep 13, 2008 at 12:16 PM, downs <default_357-line yahoo.de> wrote:Jerry Quinn wrote: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. --bbSean Kelly Wrote: Another few possibilities fore/aft front/back start/end begin/endBow/stern? Of course, if we ever get a tree implementation, we have to use port/starboard :p
Sep 12 2008
Sean Kelly <sean invisibleduck.org> wrote:Bill Baxter wrote: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.On Sat, Sep 13, 2008 at 1:03 AM, Pablo Ripolles <in-call gmx.net> wrote: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.Nope. I'm pretty sure that the discussion is about replacing "head" with "tip". There's an expression "from tip to toe".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! --bbneither 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"...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?
Sep 13 2008
Andrei Alexandrescu Wrote:Bill Baxter wrote: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!On Fri, Sep 12, 2008 at 11:39 PM, Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> wrote: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.Pablo Ripolles wrote: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.What about "isDone"?isDone is great, I just wanted to keep the one-word streak going. Let's see what everyone else says.
Sep 12 2008
Pablo Ripolles wrote:Andrei Alexandrescu Wrote: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. :)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!
Sep 13 2008
KennyTM~ Wrote:Pablo Ripolles wrote: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!Andrei Alexandrescu Wrote:>> >> -- snip --Bill Baxter wrote: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".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!
Sep 14 2008
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
Andrei Alexandrescu Wrote:Bill Baxter wrote: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!On Fri, Sep 12, 2008 at 11:39 PM, Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> wrote: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.Pablo Ripolles wrote: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.What about "isDone"?isDone is great, I just wanted to keep the one-word streak going. Let's see what everyone else says.
Sep 12 2008
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: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!On Fri, Sep 12, 2008 at 11:39 PM, Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> wrote: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.Pablo Ripolles wrote: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.What about "isDone"?isDone is great, I just wanted to keep the one-word streak going. Let's see what everyone else says.
Sep 12 2008
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: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!On Fri, Sep 12, 2008 at 11:39 PM, Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> wrote: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.Pablo Ripolles wrote: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.What about "isDone"?isDone is great, I just wanted to keep the one-word streak going. Let's see what everyone else says.
Sep 12 2008
Andrei Alexandrescu, el 12 de septiembre a las 09:39 me escribiste:Pablo Ripolles wrote: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 fasterWhat about "isDone"?isDone is great, I just wanted to keep the one-word streak going. Let's see what everyone else says.
Sep 12 2008
Leandro Lucarella Wrote:Andrei Alexandrescu, el 12 de septiembre a las 09:39 me escribiste:I do not like it either, we already have it though. I definitely prefer CamelCase than inconsistency. Cheers!Pablo Ripolles wrote:I much prefer done, because is shorter, and, well, maybe because I hate CamelCase too ;)What about "isDone"?isDone is great, I just wanted to keep the one-word streak going. Let's see what everyone else says.
Sep 12 2008
Leandro Lucarella Wrote:Andrei Alexandrescu, el 12 de septiembre a las 09:39 me escribiste:I do not like it either, we already have it though. I definitely prefer CamelCase than inconsistency. Cheers!Pablo Ripolles wrote:I much prefer done, because is shorter, and, well, maybe because I hate CamelCase too ;)What about "isDone"?isDone is great, I just wanted to keep the one-word streak going. Let's see what everyone else says.
Sep 12 2008
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
Walter Bright wrote:Andrei Alexandrescu wrote: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. AndreiThanks. 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
On Sat, Sep 13, 2008 at 5:11 AM, Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> wrote:Walter Bright wrote:Head / foot! No cutesie == even better! --bbAndrei Alexandrescu wrote: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. AndreiThanks. 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
"Andrei Alexandrescu" <SeeWebsiteForEmail erdani.org> wrote in message news:gadn7c$oe5$4 digitalmars.com...Pablo Ripolles wrote: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, - DaveAndrei 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) AndreiIn 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). AndreiWell, 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
Dave wrote:"Andrei Alexandrescu" <SeeWebsiteForEmail erdani.org> wrote in message news:gadn7c$oe5$4 digitalmars.com...Intersect is good but doesn't reveal an important detail: the operation is not commutative. AndreiPablo Ripolles wrote: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?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) AndreiIn 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). AndreiWell, 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 14 2008
Andrei Alexandrescu Wrote:Dave wrote: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!"Andrei Alexandrescu" <SeeWebsiteForEmail erdani.org> wrote in message news:gadn7c$oe5$4 digitalmars.com...Intersect is good but doesn't reveal an important detail: the operation is not commutative.Pablo Ripolles wrote: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?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) AndreiIn 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). AndreiWell, 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 15 2008
Andrei Alexandrescu wrote:Dave wrote:.cross ? .across ? .join ? .onlyAlso ? By the way, how does "yellow" not commutative?"Andrei Alexandrescu" <SeeWebsiteForEmail erdani.org> wrote in message news:gadn7c$oe5$4 digitalmars.com...Intersect is good but doesn't reveal an important detail: the operation is not commutative. AndreiPablo Ripolles wrote: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?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) AndreiIn 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). AndreiWell, 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 15 2008
KennyTM~ wrote:Andrei Alexandrescu wrote:In the forward ranges realm, if you start from the wrong range, you never get to compute the "yellow". AndreiDave wrote:.cross ? .across ? .join ? .onlyAlso ? By the way, how does "yellow" not commutative?"Andrei Alexandrescu" <SeeWebsiteForEmail erdani.org> wrote in message news:gadn7c$oe5$4 digitalmars.com...Intersect is good but doesn't reveal an important detail: the operation is not commutative. AndreiPablo Ripolles wrote: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?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) AndreiIn 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). AndreiWell, 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 15 2008
Andrei Alexandrescu wrote:Dave wrote:How about 'overlap', then? Still not perfect, but at least it doesn't carry the name of an inherently commutative mathematical operation. -LarsAnd 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.
Sep 16 2008
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
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). AndreiJust 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
Ary Borenszweig wrote:Andrei Alexandrescu wrote:Fixed, thanks Ary. AndreiIn 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). AndreiJust 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
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
Sergey Gromov wrote:Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> wrote: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.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?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
Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> wrote:Sergey Gromov wrote:Then, in Input range primitives, e=r.next must be just r.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.Neither of next and retreat should return a value. In wake of constructors/destructors, we shouldn't return by-value lightly.
Sep 12 2008
Sergey Gromov wrote:Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> wrote: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. AndreiSergey Gromov wrote:Then, in Input range primitives, e=r.next must be just r.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.Neither of next and retreat should return a value. In wake of constructors/destructors, we shouldn't return by-value lightly.
Sep 12 2008
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
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
On Fri, Sep 12, 2008 at 11:22 PM, Sergey Gromov <snake.scaly gmail.com> wrote:Bill Baxter <wbaxter gmail.com> wrote: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"). --bbBut 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
Bill Baxter <wbaxter gmail.com> wrote:On Fri, Sep 12, 2008 at 11:22 PM, Sergey Gromov <snake.scaly gmail.com> wrote: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".Bill Baxter <wbaxter gmail.com> wrote:So basically you changed done ==> empty head ==> tip retreat ==> prev ?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."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
On Fri, Sep 12, 2008 at 11:58 PM, Sergey Gromov <snake.scaly gmail.com> wrote: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. --bbSo 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".
Sep 12 2008
Bill Baxter <wbaxter gmail.com> wrote:On Fri, Sep 12, 2008 at 11:58 PM, Sergey Gromov <snake.scaly gmail.com> wrote:Exactly, I wanted it to be while (!i.done) o.next = i.next; because I didn't want any head in an input range.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.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".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
Sergey Gromov wrote:Bill Baxter <wbaxter gmail.com> wrote: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? AndreiOn Fri, Sep 12, 2008 at 11:58 PM, Sergey Gromov <snake.scaly gmail.com> wrote:Exactly, I wanted it to be while (!i.done) o.next = i.next;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.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".
Sep 12 2008
Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> wrote:Sergey Gromov wrote: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.Bill Baxter <wbaxter gmail.com> wrote: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?On Fri, Sep 12, 2008 at 11:58 PM, Sergey Gromov <snake.scaly gmail.com> wrote:Exactly, I wanted it to be while (!i.done) o.next = i.next;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.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".
Sep 12 2008
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.Sergey Gromov wrote: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.Bill Baxter <wbaxter gmail.com> wrote: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?On Fri, Sep 12, 2008 at 11:58 PM, Sergey Gromov <snake.scaly gmail.com> wrote:Exactly, I wanted it to be while (!i.done) o.next = i.next;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.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".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
Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> wrote:Sergey Gromov wrote: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. ;)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.Sergey Gromov wrote: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.Bill Baxter <wbaxter gmail.com> wrote: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?On Fri, Sep 12, 2008 at 11:58 PM, Sergey Gromov <snake.scaly gmail.com> wrote:Exactly, I wanted it to be while (!i.done) o.next = i.next;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.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".
Sep 12 2008
Sergey Gromov wrote:Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> wrote:Great point. I need to sleep on this some more.Sergey Gromov wrote: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.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.Sergey Gromov wrote: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.Bill Baxter <wbaxter gmail.com> wrote: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?On Fri, Sep 12, 2008 at 11:58 PM, Sergey Gromov <snake.scaly gmail.com> wrote:Exactly, I wanted it to be while (!i.done) o.next = i.next;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.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".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
Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> wrote:Sergey Gromov wrote: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.Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> wrote:Great point. I need to sleep on this some more.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.
Sep 13 2008
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
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: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.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. :-)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
Sergey Gromov wrote:Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> wrote:[snip] 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! AndreiSergey Gromov wrote:Then I'll continue to think aloud.Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> wrote:Great point. I need to sleep on this some more.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.
Sep 14 2008
Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> wrote:Sergey Gromov wrote: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.Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> wrote:[snip] 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; } }Sergey Gromov wrote:Then I'll continue to think aloud.Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> wrote:Great point. I need to sleep on this some more.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.
Sep 16 2008
Sergey Gromov wrote:Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> wrote: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. AndreiSergey Gromov wrote: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.Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> wrote:[snip] 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; } }Sergey Gromov wrote:Then I'll continue to think aloud.Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> wrote:Great point. I need to sleep on this some more.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.
Sep 16 2008
Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> wrote:Sergey Gromov wrote:Yes it should work. I wish it were less complex though. BTW here's another variant of your solution: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; } }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
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
Bill Baxter wrote:On Fri, Sep 12, 2008 at 2:44 PM, Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> wrote:I hear you! I'll amend the doc later.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.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
On Sat, Sep 13, 2008 at 12:29 AM, Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> wrote:Bill Baxter wrote:I mean like stdvector.swap(stdvector0), not the algorithm swap. Or maybe your swap subsumes both kinds?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.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
"Andrei Alexandrescu" wroteIn 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
Steven Schveighoffer wrote:"Andrei Alexandrescu" wroteFixed, thanks. You see, it's your influence :o).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.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
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
Jason House wrote:Andrei Alexandrescu Wrote:I agree. I just plopped it in there in an intermediate revision and left it there. Moved it now.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.Thanks, fixed. I keep on hoping someone develops a simple way for emacs to spellcheck comments... Andrei
Sep 12 2008
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
Sergey Gromov wrote:Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> wrote: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. AndreiIn 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
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). AndreiTwo 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
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
Ary Borenszweig wrote:Andrei Alexandrescu wrote:Never. Ranges never grow. You need access to the "mother" container, which will offer primitives for insertion and removal of elements.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). AndreiTwo 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.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
Andrei Alexandrescu wrote:Ary Borenszweig wrote: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.- 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.
Sep 12 2008
"Walter Bright" wroteAndrei Alexandrescu wrote: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. -SteveAry Borenszweig wrote: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.- 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.
Sep 12 2008
Walter Bright wrote:Andrei Alexandrescu wrote: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. SeanAry Borenszweig wrote: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.- 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.
Sep 12 2008
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
Walter Bright wrote:Sean Kelly wrote:That's fine... the optimizer has to impose certain requirements. For such things I'll simply continue using for loops. SeanI'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
Walter Bright wrote:Sean Kelly wrote: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. AndreiI'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
Sean Kelly <sean invisibleduck.org> wrote:Walter Bright wrote: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.Andrei Alexandrescu wrote: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.Ary Borenszweig wrote: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.- 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.
Sep 13 2008
Bill Baxter Wrote:On Sat, Sep 13, 2008 at 1:03 AM, Pablo Ripolles <in-call gmx.net> wrote::D yes! ok! I missed that! sorry.Nope. I'm pretty sure that the discussion is about replacing "head" with "tip". There's an expression "from tip to toe".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! --bbneither 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"...But I think your confusion (or is it mine?) about which end would be the tip is pretty damning.I agree with that! indeed it is.Exactly!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.Yes, thanks, I didn't really now that was the reason but I do agree. My last idea was the pair "head" and "aft"... Cheers!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.
Sep 12 2008
Bill Baxter Wrote:On Sat, Sep 13, 2008 at 12:03 AM, Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> wrote:After having had some sleep, I kind of support Bill's proposal, that is, head/foot, definitely better than head/toe. It's like the header and the footer! sounds very reasonable. Of course I also like fore/aft, but thats because my aeronautical engineering bias. tip is too ambiguous. tail refers to all what comes after the head, not to its tip. Cheers!Bill Baxter 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! --bbOn Fri, Sep 12, 2008 at 11:39 PM, Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> wrote: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.Pablo Ripolles wrote: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.What about "isDone"?isDone is great, I just wanted to keep the one-word streak going. Let's see what everyone else says.
Sep 13 2008
On 2008-09-13 04:18:20 -0400, Pablo Ripolles <in-call gmx.net> said:After having had some sleep, I kind of support Bill's proposal, that is, head/foot, definitely better than head/toe. It's like the header and the footer! sounds very reasonable. Of course I also like fore/aft, but thats because my aeronautical engineering bias. tip is too ambiguous. tail refers to all what comes after the head, not to its tip. Cheers!Anyone suggested head/rear yet? -- Michel Fortin michel.fortin michelf.com http://michelf.com/
Sep 14 2008
Michel Fortin Wrote:On 2008-09-13 04:18:20 -0400, Pablo Ripolles <in-call gmx.net> said:Hello, if I remember well, yes. I also like it! I don't recall the argument against it though...After having had some sleep, I kind of support Bill's proposal, that is, head/foot, definitely better than head/toe. It's like the header and the footer! sounds very reasonable. Of course I also like fore/aft, but thats because my aeronautical engineering bias. tip is too ambiguous. tail refers to all what comes after the head, not to its tip. Cheers!Anyone suggested head/rear yet?
Sep 14 2008
On Sun, Sep 14, 2008 at 9:29 PM, Pablo Ripolles <in-call gmx.net> wrote:Michel Fortin Wrote:I think it was that if you're thinking body parts, then "rear" means someone's bottom. There's an expression -- "get your head out of your rear" -- that comes to mind. --bbOn 2008-09-13 04:18:20 -0400, Pablo Ripolles <in-call gmx.net> said:Hello, if I remember well, yes. I also like it! I don't recall the argument against it though...After having had some sleep, I kind of support Bill's proposal, that is, head/foot, definitely better than head/toe. It's like the header and the footer! sounds very reasonable. Of course I also like fore/aft, but thats because my aeronautical engineering bias. tip is too ambiguous. tail refers to all what comes after the head, not to its tip. Cheers!Anyone suggested head/rear yet?
Sep 14 2008
Andrei Alexandrescu:http://ssli.ee.washington.edu/~aalexand/d/tmp/std_range.htmlI have re-read the page again, and I am now starting to understand its contents. When I'll understand 90-95% of that page then I think it's dumbed enough for most programmers ;-) In lines like: t=r.before(s) I suggest to add a more natural spacing: t = range.before(s) But maybe adding an "all" to that method name you it can show better that it generates a new range: t = range.allbefore(s) This is one of the functions shown in that page: // Increments all elements in a range void bump(R)(R r) { for (; !r.done; r.next) { ++r.head; } } But that's not readable. This improves readability 5X (note current instead of head and isdone instead of done): /// Increments all items of a Forward range void bump(R)(R range) { while (!range.isdone) { range.current++; range.next(); } } If the D language will keep the foreach(), then where possible I suggest to add a version that uses foreach() too: /// Increments all items of a Forward range void bump(R)(R range) { foreach (ref item; range) item++; } In that document you can also show 1 example for all functions/ methods/ attributes, like r.after(s), etc. I think that good names and some sugar may make this stuff usable (not easy, but usable). Bye, bearophile
Sep 16 2008
bearophile wrote:Andrei Alexandrescu:That's a good point. At the time I wrote that document, inserting the spaces would really mess up the table by breaking the expression. Since then I fixed the style, but not (yet) the document.http://ssli.ee.washington.edu/~aalexand/d/tmp/std_range.htmlI have re-read the page again, and I am now starting to understand its contents. When I'll understand 90-95% of that page then I think it's dumbed enough for most programmers ;-) In lines like: t=r.before(s) I suggest to add a more natural spacing: t = range.before(s)But maybe adding an "all" to that method name you it can show better that it generates a new range: t = range.allbefore(s)So many good proposals for names have been put forth, it will be definitely impossible to find one set of names that will please everybody. So everybody, please expect to be disappointed. I *do* need to do some actual work instead of getting ready to do it.This is one of the functions shown in that page: // Increments all elements in a range void bump(R)(R r) { for (; !r.done; r.next) { ++r.head; } } But that's not readable. This improves readability 5X (note current instead of head and isdone instead of done): /// Increments all items of a Forward range void bump(R)(R range) { while (!range.isdone) { range.current++; range.next(); } }Maybe 5X is a bit exaggerated, but if the point was that good names are important, I entirely agree. One problem with the done/current approach to naming is that it does not dovetail well with non-temporal collections (I'm computing a fresh range; how come I have to query whether it's "done"? And what's "current" to an array?) But then there are problems with, and advantages of, any naming scheme ever mentioned here. At the end of the day I'll have to choose one. Again, everybody, prepare to be disappointed.If the D language will keep the foreach(), then where possible I suggest to add a version that uses foreach() too: /// Increments all items of a Forward range void bump(R)(R range) { foreach (ref item; range) item++; }Walter will have foreach automatically detect and use ranges.In that document you can also show 1 example for all functions/ methods/ attributes, like r.after(s), etc. I think that good names and some sugar may make this stuff usable (not easy, but usable).I agree that good names and some sugar are helpful. Now, if you could tell how I can make things actually easy, that would be huge. I hoped three functions would be easy. True, defining their semantics in excruciating detail will not be as easy, but it will have the advantage to be rigorous enough to produce reliable implementations. Andrei
Sep 16 2008
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
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
Sergey Gromov wrote:In article <gbgpak$2q10$1 digitalmars.com>, brunodomedeiros+spam com.gmail says...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(!). AndreiAlso, 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
In article <gbgu0h$5sq$1 digitalmars.com>, SeeWebsiteForEmail erdani.org says...Sergey Gromov wrote: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 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.
Sep 25 2008
Sergey Gromov wrote:In article <gbgu0h$5sq$1 digitalmars.com>, SeeWebsiteForEmail erdani.org says...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. AndreiSergey Gromov wrote: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 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.
Sep 26 2008
In article <gbiqbo$m1e$1 digitalmars.com>, SeeWebsiteForEmail erdani.org says...Sergey Gromov wrote: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:In article <gbgu0h$5sq$1 digitalmars.com>, SeeWebsiteForEmail erdani.org says...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.Sergey Gromov wrote: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 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.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.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.
Sep 26 2008
Sergey Gromov wrote:In article <gbiqbo$m1e$1 digitalmars.com>, SeeWebsiteForEmail erdani.org says...Ok, so far so good.Sergey Gromov wrote: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 }In article <gbgu0h$5sq$1 digitalmars.com>, SeeWebsiteForEmail erdani.org says...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.Sergey Gromov wrote: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 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.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: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.does not apply to D.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.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
Sat, 27 Sep 2008 08:56:42 -0500, Andrei Alexandrescu wrote:Sergey Gromov wrote: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.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: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.does not apply to D.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.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.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.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?Explicit properties solve a much wider range of problems than introduced by naked delegate members. They just happen to solve this issue, too.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?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 identifierI can imagine you want your personal hell, but I hardly can understand its usefulness to you or the rest of us :o).
Sep 27 2008
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
Sergey Gromov wrote:Sat, 27 Sep 2008 08:56:42 -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. AndreiSergey Gromov wrote: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.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: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.does not apply to D.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.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.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.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?Explicit properties solve a much wider range of problems than introduced by naked delegate members. They just happen to solve this issue, too.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?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 identifierI can imagine you want your personal hell, but I hardly can understand its usefulness to you or the rest of us :o).
Sep 27 2008
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
Sergey Gromov wrote:Sat, 27 Sep 2008 15:19:01 -0500, Andrei Alexandrescu wrote: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? AndreiMy 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
Andrei Alexandrescu wrote:Sergey Gromov wrote: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.Sat, 27 Sep 2008 15:19:01 -0500, Andrei Alexandrescu wrote: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?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
Sat, 27 Sep 2008 23:08:43 -0700, Chris R. Miller wrote:Andrei Alexandrescu wrote:I cannot see how trickery with an existing syntax is less a feature than a new keyword.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.
Sep 28 2008
Sergey Gromov wrote:Sat, 27 Sep 2008 23:08:43 -0700, Chris R. Miller wrote: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!) ;-)Andrei Alexandrescu wrote:I cannot see how trickery with an existing syntax is less a feature than a new keyword.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.
Sep 28 2008
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
Jason House wrote:Andrei Alexandrescu wrote:But I'm talking about property set syntax, not get. AndreiI 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
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
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
torhu wrote:Andrei Alexandrescu wrote: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?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
Chris R. Miller wrote:torhu wrote: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.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
torhu wrote:Chris R. Miller wrote: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(); }torhu wrote: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.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
On Sun, 28 Sep 2008 05:45:58 +0400, Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> wrote:Sergey Gromov wrote: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.Sat, 27 Sep 2008 15:19:01 -0500, Andrei Alexandrescu wrote: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? AndreiMy 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 28 2008
Denis Koroskin wrote:On Sun, 28 Sep 2008 05:45:58 +0400, Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> wrote:By whom aren't they expected?Sergey Gromov wrote:I think property syntax should be disallowed by default. Most of the functions aren't expected to be used as a property, anyway.Sat, 27 Sep 2008 15:19:01 -0500, Andrei Alexandrescu wrote: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? AndreiMy 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.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
Andrei Alexandrescu wrote:Denis Koroskin wrote: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.On Sun, 28 Sep 2008 05:45:58 +0400, Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> wrote:By whom aren't they expected?Sergey Gromov wrote:I think property syntax should be disallowed by default. Most of the functions aren't expected to be used as a property, anyway.Sat, 27 Sep 2008 15:19:01 -0500, Andrei Alexandrescu wrote: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? AndreiMy 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.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?
Sep 28 2008
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 coherencyHow? 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
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 coherencyHow? 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
Andrei Alexandrescu wrote:Chris R. Miller wrote:My lack of example situations is offset by my fear of one day finding one as a problem.Improper use of properties make code that looks ambiguous. Is that a public member or a method?Under what circumstances is that distinction important?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.It reduces codebase coherencyHow? Why?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., 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?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. ;-)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.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.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.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).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.
Sep 28 2008
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
Chris R. Miller, el 28 de septiembre a las 19:50 me escribiste: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 TVIn 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.It reduces codebase coherencyHow? Why?
Sep 29 2008
Leandro Lucarella wrote:Chris R. Miller, el 28 de septiembre a las 19:50 me escribiste: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.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, 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.It reduces codebase coherencyHow? Why?
Sep 29 2008
Chris R. Miller wrote:Leandro Lucarella wrote: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. AndreiChris R. Miller, el 28 de septiembre a las 19:50 me escribiste: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.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, 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.It reduces codebase coherencyHow? Why?
Sep 29 2008
Chris R. Miller wrote:Andrei Alexandrescu wrote:Oh, besides - if computed properties are allowed, they also can perform arbitrary amounts of work. No? AndreiDenis Koroskin wrote: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.On Sun, 28 Sep 2008 05:45:58 +0400, Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> wrote:By whom aren't they expected?Sergey Gromov wrote:I think property syntax should be disallowed by default. Most of the functions aren't expected to be used as a property, anyway.Sat, 27 Sep 2008 15:19:01 -0500, Andrei Alexandrescu wrote: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? AndreiMy 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.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?
Sep 28 2008
Denis Koroskin wrote:On Sun, 28 Sep 2008 05:45:58 +0400, Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> wrote: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?Sergey Gromov wrote: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.Sat, 27 Sep 2008 15:19:01 -0500, Andrei Alexandrescu wrote: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? AndreiMy 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 29 2008
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
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
Andrei Alexandrescu wrote:Andrei Alexandrescu wrote: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) +1How 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.
Sep 28 2008
On Mon, Sep 29, 2008 at 8:32 AM, Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> wrote:Andrei Alexandrescu wrote: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. --bbHow 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?
Sep 28 2008
Bill Baxter wrote:On Mon, Sep 29, 2008 at 8:32 AM, Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> wrote: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.Andrei Alexandrescu wrote: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).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?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
On Mon, Sep 29, 2008 at 12:53 PM, Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> wrote:Bill Baxter wrote:It's a different, but intertwined story.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; }Having to write out "delegate(argumenttype) doesn't qualify as "convenient" in my book. --bb
Sep 28 2008
Bill Baxter wrote:On Mon, Sep 29, 2008 at 12:53 PM, Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> wrote: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) AndreiBill Baxter wrote:It's a different, but intertwined story.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; }Having to write out "delegate(argumenttype) doesn't qualify as "convenient" in my book.
Sep 28 2008
On Mon, Sep 29, 2008 at 1:16 PM, Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> wrote:Bill Baxter wrote: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. --bbOn Mon, Sep 29, 2008 at 12:53 PM, Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> wrote: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)Bill Baxter wrote:It's a different, but intertwined story.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; }Having to write out "delegate(argumenttype) doesn't qualify as "convenient" in my book.
Sep 28 2008
Sun, 28 Sep 2008 22:53:13 -0500, Andrei Alexandrescu wrote:Bill Baxter 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; // errorThe 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.
Sep 29 2008
Sergey Gromov wrote:Sun, 28 Sep 2008 22:53:13 -0500, Andrei Alexandrescu wrote: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". AndreiBill Baxter 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; // errorThe 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.
Sep 29 2008
Mon, 29 Sep 2008 12:23:58 -0500, Andrei Alexandrescu wrote:Sergey Gromov wrote:Today the compiler tries to call Range.head with one argument and fails. Another side effect needs hacking.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; // errorA 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".
Sep 29 2008
Sergey Gromov wrote:Mon, 29 Sep 2008 12:23:58 -0500, Andrei Alexandrescu wrote:Well I'd rather say, an awkwardness that needs fixing. AndreiSergey Gromov wrote:Today the compiler tries to call Range.head with one argument and fails. Another side effect needs hacking.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; // errorA function can return an object that allows assignment even today with opAssign.
Sep 29 2008
Andrei Alexandrescu wrote:Sergey Gromov wrote: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).Sun, 28 Sep 2008 22:53:13 -0500, Andrei Alexandrescu wrote: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". AndreiBill Baxter 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; // errorThe 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.
Sep 29 2008
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
Bruno Medeiros wrote:Bill Baxter wrote: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--- 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?... :(
Oct 02 2008
Andrei Alexandrescu wrote:Bruno Medeiros wrote: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".Bill Baxter wrote:What's going on now is pretty much that except that there's no more symbol changing - it's just someBar() and someBar(SomeBar).--- 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?... :(Andrei
Oct 02 2008
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
Michel Fortin wrote:On 2008-09-28 19:32:43 -0400, Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> said: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.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.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
On 2008-09-29 00:07:28 -0400, Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> said:Michel Fortin wrote:Ok, so you determine if a function can be called by "=" before checking for access rights.On 2008-09-28 19:32:43 -0400, Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> said: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.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.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.)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) ?Seems coherant.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.And "out"?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))Seems coherant, again.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.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?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.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.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?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/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.
Sep 29 2008
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.That means indentLevel is not a property and consequently "generic" cannot work with that object.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?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.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).And "out"?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))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.s3.prop(s3.prop) would not compile, so neither does the assignment form.No answer to that one? Take this case: struct S3 { int prop() invariant; void prop(int x); } S3 s3; s3.prop = x; // error?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?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
On 2008-09-29 09:48:31 -0400, Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> said:Ok, lets look back at what I wrote and what you replied in your previous post: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.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/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.)
Sep 29 2008
Michel Fortin wrote:On 2008-09-29 09:48:31 -0400, Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> said: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.Ok, lets look back at what I wrote and what you replied in your previous post: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.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.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.)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
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
Michel Fortin wrote:On 2008-09-29 23:12:44 -0400, Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> said:I think the argument is overstated and a corner case is overblown, but I agree there is an issue.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! }The coupling part I agree with, it's unfortunate. Well I guess I'll just drop the matter. Andrei
Sep 30 2008
"Andrei Alexandrescu" wroteAndrei Alexandrescu wrote: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; -SteveHow 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.
Sep 30 2008
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
On Sat, 27 Sep 2008 17:56:42 +0400, Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> wrote:Sergey Gromov wrote: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.In article <gbiqbo$m1e$1 digitalmars.com>, SeeWebsiteForEmail erdani.org says...Ok, so far so good.Sergey Gromov wrote: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 }In article <gbgu0h$5sq$1 digitalmars.com>, SeeWebsiteForEmail erdani.org says...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.Sergey Gromov wrote: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 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.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: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.does not apply to D.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.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?
Sep 27 2008
Andrei Alexandrescu wrote:Sergey Gromov wrote:I've got a few questions regarding this: 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) ?In article <gbgpak$2q10$1 digitalmars.com>, brunodomedeiros+spam com.gmail says...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(!). AndreiAlso, 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
Yigal Chripun wrote:Andrei Alexandrescu wrote: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.Sergey Gromov wrote:I've got a few questions regarding this: 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.In article <gbgpak$2q10$1 digitalmars.com>, brunodomedeiros+spam com.gmail says...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(!). AndreiAlso, 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.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
Andrei Alexandrescu wrote:Sergey Gromov wrote: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#DIn article <gbgpak$2q10$1 digitalmars.com>, brunodomedeiros+spam com.gmail says...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.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 26 2008
Sergey Gromov wrote:In article <gbgpak$2q10$1 digitalmars.com>, brunodomedeiros+spam com.gmail says...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#DAlso, 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 26 2008
Bruno Medeiros wrote:Sergey Gromov wrote: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. AndreiIn article <gbgpak$2q10$1 digitalmars.com>, brunodomedeiros+spam com.gmail says...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).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 26 2008
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
Bent Rasmussen wrote:Well put! AndreiI 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.
Sep 27 2008
== Quote from Andrei Alexandrescu (SeeWebsiteForEmail erdani.org)'s articleBent Rasmussen wrote: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.htmI 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.
Sep 27 2008
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.htmJust in case things got lost in translation, ...
Sep 28 2008
Bruno Medeiros wrote:Andrei Alexandrescu wrote:I sure hope they won't agree to an unsupported assertion.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.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
Andrei Alexandrescu wrote:Bruno Medeiros wrote: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.Andrei Alexandrescu wrote:I sure hope they won't agree to an unsupported assertion.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'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#DSo "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
Bruno Medeiros wrote:Andrei Alexandrescu wrote: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?Bruno Medeiros wrote: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.Andrei Alexandrescu wrote:I sure hope they won't agree to an unsupported assertion.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 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. AndreiI'm not imagining what that situation would be, could you explain a bit more?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
Agh, got busy in these last days, so I was missing the discussion. Andrei Alexandrescu wrote:Bruno Medeiros wrote:Yes, if there where good reasons, I'd use it. But what are those reasons?Andrei Alexandrescu wrote: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?Bruno Medeiros wrote: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.Andrei Alexandrescu wrote:I sure hope they won't agree to an unsupported assertion.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.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#DI 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. AndreiI'm not imagining what that situation would be, could you explain a bit more?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
Oct 02 2008
Andrei Alexandrescu wrote:Bruno Medeiros wrote: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?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 26 2008
KennyTM~ wrote:Andrei Alexandrescu wrote:I don't think the bug report has much strength.Bruno Medeiros wrote: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.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.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
"Andrei Alexandrescu" wroteSo 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. -SteveP.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".
Sep 26 2008
Steven Schveighoffer wrote:"Andrei Alexandrescu" wroteOf 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.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.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".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
"Andrei Alexandrescu" wroteSteven 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. 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."Andrei Alexandrescu" wroteOf 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.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.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".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. -SteveNot 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.
Sep 26 2008
Steven Schveighoffer wrote:"Andrei Alexandrescu" wroteI have stated my assumption and its basis. What is the basis of yours?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."Andrei Alexandrescu" wroteOf 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.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.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".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.Aside from just saying it, do you have any substantive argument?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.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.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
Andrei Alexandrescu wrote:Steven Schveighoffer wrote: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, frankYou 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?
Sep 26 2008
In article <gbjihf$2803$1 digitalmars.com>, frank youknow.what.todo.interNETz says...Andrei Alexandrescu wrote:Functions in D are first-class values. It's awkward that a first-class value cannot be accessed by its identifier.Steven Schveighoffer wrote: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.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?
Sep 26 2008
Sergey Gromov wrote:In article <gbjihf$2803$1 digitalmars.com>, frank youknow.what.todo.interNETz says...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.[...] 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
0ffh wrote:Sergey Gromov wrote: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] 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). 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... ;pIn article <gbjihf$2803$1 digitalmars.com>, frank youknow.what.todo.interNETz says...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.[...] 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 27 2008
Sergey Gromov wrote:In article <gbjihf$2803$1 digitalmars.com>, frank youknow.what.todo.interNETz says...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. AndreiAndrei Alexandrescu wrote:Functions in D are first-class values. It's awkward that a first-class value cannot be accessed by its identifier.Steven Schveighoffer wrote: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.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?
Sep 27 2008
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
Sergey Gromov wrote:Sat, 27 Sep 2008 09:13:54 -0500, Andrei Alexandrescu wrote:enforce :o) AndreiIf 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
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
Andrei Alexandrescu wrote:Sergey Gromov wrote: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.In article <gbjihf$2803$1 digitalmars.com>, frank youknow.what.todo.interNETz says...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.Andrei Alexandrescu wrote:Functions in D are first-class values. It's awkward that a first-class value cannot be accessed by its identifier.Steven Schveighoffer wrote: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.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?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
Don wrote:Andrei Alexandrescu wrote: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. AndreiSergey Gromov wrote: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.In article <gbjihf$2803$1 digitalmars.com>, frank youknow.what.todo.interNETz says...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.Andrei Alexandrescu wrote:Functions in D are first-class values. It's awkward that a first-class value cannot be accessed by its identifier.Steven Schveighoffer wrote: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.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?
Sep 29 2008
Andrei Alexandrescu wrote:Don wrote: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.Andrei Alexandrescu wrote: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.Sergey Gromov wrote: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.In article <gbjihf$2803$1 digitalmars.com>, frank youknow.what.todo.interNETz says...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.Andrei Alexandrescu wrote:Functions in D are first-class values. It's awkward that a first-class value cannot be accessed by its identifier.Steven Schveighoffer wrote: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.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?
Sep 29 2008
Don wrote:Andrei Alexandrescu wrote: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#DDon wrote: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.Andrei Alexandrescu wrote: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.Sergey Gromov wrote: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.In article <gbjihf$2803$1 digitalmars.com>, frank youknow.what.todo.interNETz says...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.Andrei Alexandrescu wrote:Functions in D are first-class values. It's awkward that a first-class value cannot be accessed by its identifier.Steven Schveighoffer wrote: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.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?
Oct 02 2008
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. AndreiWhat'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
0ffh wrote:Andrei Alexandrescu wrote: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!Steven Schveighoffer wrote: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.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?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
"Andrei Alexandrescu" wroteSteven Schveighoffer wrote:Forgive me, but I must have missed it. I saw only your assumption (or supposition) that the C designers made a mistake."Andrei Alexandrescu" wroteI have stated my assumption and its basis.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."Andrei Alexandrescu" wroteOf 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.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.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".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.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.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?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.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.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.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.The arguments have been made. As I stated, it's my opinion.Aside from just saying it, do you have any substantive argument?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.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 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 ;)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.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 bit, I prefer that method of requiring a formal property definition. -SteveThe 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.
Sep 29 2008
Steven Schveighoffer wrote:"Andrei Alexandrescu" wroteMy 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."Steven Schveighoffer wrote:Forgive me, but I must have missed it. I saw only your assumption (or supposition) that the C designers made a mistake."Andrei Alexandrescu" wroteI have stated my assumption and its basis.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."Andrei Alexandrescu" wroteOf 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.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.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".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_assertionWhat 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.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.Const takes care of that.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.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?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?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 ()?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.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.But you do use Stdout.newline and the alleged loss in clarity doesn't seem to be phasing you.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.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.The arguments have been made. As I stated, it's my opinion.Aside from just saying it, do you have any substantive argument?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.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 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.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.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?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. AndreiNeither 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 bit, I prefer that method of requiring a formal property definition.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.
Sep 29 2008
"Andrei Alexandrescu" wroteSteven 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. 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."Andrei Alexandrescu" wroteMy 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."Steven Schveighoffer wrote:Forgive me, but I must have missed it. I saw only your assumption (or supposition) that the C designers made a mistake."Andrei Alexandrescu" wroteI have stated my assumption and its basis.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."Andrei Alexandrescu" wroteOf 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.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.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".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.This is no logic. Your proof is just stating the conclusion. It's a logical fallacy called proof by assertion.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.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'.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.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.Const takes care of that.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.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?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.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?I have given good arguments. You seem to dismiss them as failed proofs, but that is your call.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 ()?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.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.It does phase me. I sometimes catch myself doing it and add the parentheses.But you do use Stdout.newline and the alleged loss in clarity doesn't seem to be phasing you.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.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.The arguments have been made. As I stated, it's my opinion.Aside from just saying it, do you have any substantive argument?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.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 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.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.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.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.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.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?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?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. -SteveI 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.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 property definition.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.
Sep 29 2008
On Tue, Sep 30, 2008 at 3:36 AM, Steven Schveighoffer <schveiguy yahoo.com> wrote: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. --bbThere 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.
Sep 29 2008
Bill Baxter wrote:On Tue, Sep 30, 2008 at 3:36 AM, Steven Schveighoffer <schveiguy yahoo.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.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. --bbThere 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.
Sep 29 2008
KennyTM~ wrote:Bill Baxter wrote:But range.next does modify the object. On what basis do I need to add the trailing parens? AndreiOn Tue, Sep 30, 2008 at 3:36 AM, Steven Schveighoffer <schveiguy yahoo.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.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. --bbThere 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.
Sep 29 2008
Andrei Alexandrescu wrote:KennyTM~ wrote:Well that's why *I* would use range.next() ;)Bill Baxter wrote:But range.next does modify the object. On what basis do I need to add the trailing parens? AndreiOn Tue, Sep 30, 2008 at 3:36 AM, Steven Schveighoffer <schveiguy yahoo.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.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. --bbThere 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.
Sep 29 2008
On Tue, Sep 30, 2008 at 4:08 AM, KennyTM~ <kennytm gmail.com> wrote:Bill Baxter wrote: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. --bbOn Tue, Sep 30, 2008 at 3:36 AM, Steven Schveighoffer <schveiguy yahoo.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.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. --bbThere 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.
Sep 29 2008
Bill Baxter wrote:On Tue, Sep 30, 2008 at 4:08 AM, KennyTM~ <kennytm gmail.com> wrote: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."). AndreiBill Baxter wrote: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.On Tue, Sep 30, 2008 at 3:36 AM, Steven Schveighoffer <schveiguy yahoo.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.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. --bbThere 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.
Sep 29 2008
Andrei Alexandrescu wrote:Bill Baxter wrote:In what way does that convention/style go against facilitating generic code?On Tue, Sep 30, 2008 at 4:08 AM, KennyTM~ <kennytm gmail.com> wrote: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.Bill Baxter wrote: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.On Tue, Sep 30, 2008 at 3:36 AM, Steven Schveighoffer <schveiguy yahoo.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.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. --bbThere 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.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
Bruno Medeiros wrote:Andrei Alexandrescu wrote:A generic function writing obj.symbol cannot work across objects that implement symbol as a computed property vs. a field. AndreiBill Baxter wrote:In what way does that convention/style go against facilitating generic code?On Tue, Sep 30, 2008 at 4:08 AM, KennyTM~ <kennytm gmail.com> wrote: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.Bill Baxter wrote: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.On Tue, Sep 30, 2008 at 3:36 AM, Steven Schveighoffer <schveiguy yahoo.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.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. --bbThere 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.
Oct 02 2008
Andrei Alexandrescu wrote:Bruno Medeiros wrote: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#DAndrei Alexandrescu wrote:A generic function writing obj.symbol cannot work across objects that implement symbol as a computed property vs. a field. AndreiBill Baxter wrote:In what way does that convention/style go against facilitating generic code?On Tue, Sep 30, 2008 at 4:08 AM, KennyTM~ <kennytm gmail.com> wrote: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.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.
Oct 02 2008
Bruno Medeiros wrote:Andrei Alexandrescu wrote: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.Bruno Medeiros wrote: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?Andrei Alexandrescu wrote:A generic function writing obj.symbol cannot work across objects that implement symbol as a computed property vs. a field. AndreiBill Baxter wrote:In what way does that convention/style go against facilitating generic code?On Tue, Sep 30, 2008 at 4:08 AM, KennyTM~ <kennytm gmail.com> wrote: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.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.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
Andrei Alexandrescu wrote:Bruno Medeiros wrote: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#DAndrei Alexandrescu wrote: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.Bruno Medeiros wrote: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?Andrei Alexandrescu wrote:A generic function writing obj.symbol cannot work across objects that implement symbol as a computed property vs. a field. AndreiBill Baxter wrote:In what way does that convention/style go against facilitating generic code?On Tue, Sep 30, 2008 at 4:08 AM, KennyTM~ <kennytm gmail.com> wrote: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.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.
Oct 03 2008
KennyTM~ Wrote:Bill Baxter wrote: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.On Tue, Sep 30, 2008 at 3:36 AM, Steven Schveighoffer <schveiguy yahoo.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.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. --bbThere 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.
Sep 29 2008
Jason House wrote:KennyTM~ Wrote: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.Bill Baxter wrote: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.On Tue, Sep 30, 2008 at 3:36 AM, Steven Schveighoffer <schveiguy yahoo.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.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. --bbThere 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.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
KennyTM~ Wrote:Jason House wrote: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.KennyTM~ Wrote:Right. Therefore if I were to implement property getters I would require it as an immutable (or at least const) method itself.Bill Baxter wrote: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.On Tue, Sep 30, 2008 at 3:36 AM, Steven Schveighoffer <schveiguy yahoo.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.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. --bbThere 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.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
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
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
"Bill Baxter" wroteOn Tue, Sep 30, 2008 at 3:36 AM, Steven Schveighoffer <schveiguy yahoo.com> wrote: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. -SteveThe 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.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.
Sep 29 2008
Bill Baxter wrote:On Tue, Sep 30, 2008 at 3:36 AM, Steven Schveighoffer <schveiguy yahoo.com> wrote: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. --benjiThe 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. --bbThere 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.
Sep 30 2008
On Wed, 01 Oct 2008 07:58:20 +0200, Benji Smith <dlanguage benjismith.net> wrote:Bill Baxter 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. -- SimenOn Tue, Sep 30, 2008 at 3:36 AM, Steven Schveighoffer <schveiguy yahoo.com> wrote: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. --benjiThe 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. --bbThere 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.
Oct 01 2008
Simen Kjaeraas wrote:On Wed, 01 Oct 2008 07:58:20 +0200, Benji Smith <dlanguage benjismith.net> wrote: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.Bill Baxter 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.On Tue, Sep 30, 2008 at 3:36 AM, Steven Schveighoffer <schveiguy yahoo.com> wrote: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. --benjiThe 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. --bbThere 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.
Oct 01 2008
KennyTM~ wrote:Simen Kjaeraas wrote:Ah, didn't see your post before I posted a similar argument. AndreiOn Wed, 01 Oct 2008 07:58:20 +0200, Benji Smith <dlanguage benjismith.net> wrote: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.Bill Baxter 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.On Tue, Sep 30, 2008 at 3:36 AM, Steven Schveighoffer <schveiguy yahoo.com> wrote: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. --benjiThe 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. --bbThere 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.
Oct 01 2008
"KennyTM~" wroteSimen Kjaeraas wrote:Yes yes! vote++ -SteveOn Wed, 01 Oct 2008 07:58:20 +0200, Benji Smith <dlanguage benjismith.net> wrote: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.Bill Baxter 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.On Tue, Sep 30, 2008 at 3:36 AM, Steven Schveighoffer <schveiguy yahoo.com> wrote: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. --benjiThe 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. --bbThere 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.
Oct 01 2008
On Wed, Oct 1, 2008 at 10:58 PM, Steven Schveighoffer <schveiguy yahoo.com> wrote:"KennyTM~" wroteMe too++ --bbYou 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
Simen Kjaeraas wrote:On Wed, 01 Oct 2008 07:58:20 +0200, Benji Smith <dlanguage benjismith.net> wrote: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). AndreiBill Baxter 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.On Tue, Sep 30, 2008 at 3:36 AM, Steven Schveighoffer <schveiguy yahoo.com> wrote: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. --benjiThe 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. --bbThere 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.
Oct 01 2008
On Wed, 01 Oct 2008 15:28:36 +0200, Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> wrote:Simen Kjaeraas wrote: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. -- SimenIt 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
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: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 -- SimenSimen Kjaeraas wrote: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.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
Steven Schveighoffer wrote:"Andrei Alexandrescu" wroteSheesh, 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.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."Andrei Alexandrescu" wroteMy 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."Steven Schveighoffer wrote:Forgive me, but I must have missed it. I saw only your assumption (or supposition) that the C designers made a mistake."Andrei Alexandrescu" wroteI have stated my assumption and its basis.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."Andrei Alexandrescu" wroteOf 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.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.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".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.The decision was on purpose, I am sure it wasn't an oversight. It was a poor decision because it breaks the aforementioned principles.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.This is no logic. Your proof is just stating the conclusion. It's a logical fallacy called proof by assertion.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.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. AndreiNot 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'.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.
Sep 29 2008
"Andrei Alexandrescu" wroteSteven Schveighoffer wrote: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.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.Not really. You provided no proof that your principles apply to this debate, you just stated that they do.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.Respectfully disagree.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.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. -SteveNot 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() {}
Sep 29 2008
"Steven Schveighoffer" wrote"Andrei Alexandrescu" wroteThis 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. -SteveHow 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.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() {}
Sep 29 2008
Steven Schveighoffer wrote:"Steven Schveighoffer" wrote"State" is very accurate, because aside from stating the point you bring nothing to support it."Andrei Alexandrescu" wroteThis 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.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.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() {}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
"Andrei Alexandrescu" wroteSteven Schveighoffer wrote: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."Steven Schveighoffer" wrote"State" is very accurate, because aside from stating the point you bring nothing to support it."Andrei Alexandrescu" wroteThis 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.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.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() {}You are completely misunderstanding the scope of what I'm saying. I'll just leave it at that.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.Think and believe to me both mean 'This is true in my mind'. If you find one to be more appealing, so be it.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.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
Steven Schveighoffer wrote:"Andrei Alexandrescu" wroteYou 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?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.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. AndreiThink and believe to me both mean 'This is true in my mind'. If you find one to be more appealing, so be it.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".
Sep 29 2008
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. AndreiGood 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 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
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 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" - 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
Andrei Alexandrescu wrote:Bruno Medeiros wrote: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).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.Good, at least we agree on that after all.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).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.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 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" - 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).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
"Bruno Medeiros" wroteAndrei Alexandrescu wrote:In fact, C++.net did something similar in that if you defined a property x 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 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. 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 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. -SteveEiffel 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).
Oct 02 2008
Steven Schveighoffer wrote:"Bruno Medeiros" wroteJust 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#DAndrei Alexandrescu wrote:In fact, C++.net did something similar in that if you defined a property x 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 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.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).
Oct 03 2008
Bruno Medeiros wrote:Andrei Alexandrescu wrote: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. AndreiBruno Medeiros wrote: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).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.
Oct 02 2008
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: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. AndreiBruno Medeiros wrote: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).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.
Oct 02 2008
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
Bill Baxter wrote:On Fri, Oct 3, 2008 at 3:16 AM, Fawzi Mohamed <fmohamed mac.com> 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. AndreiThis 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.
Oct 02 2008
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
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:Indeed. I thought there wasn't a lot of debate needed on this, just action. --bbYah, 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
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:... 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. :-) --bbThu, 02 Oct 2008 15:03:42 -0500, Andrei Alexandrescu wrote:Indeed. I thought there wasn't a lot of debate needed on this, just action.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
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: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.On Fri, Oct 3, 2008 at 8:04 AM, Sergey Gromov <snake.scaly 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. 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. :-)Thu, 02 Oct 2008 15:03:42 -0500, Andrei Alexandrescu wrote:Indeed. I thought there wasn't a lot of debate needed on this, just action.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
Sergey Gromov wrote:Fri, 3 Oct 2008 09:37:46 +0900, Bill Baxter wrote:That's a good rule, but a[b] = c on a hash is not helped by it. AndreiOn Fri, Oct 3, 2008 at 9:32 AM, Bill Baxter <wbaxter gmail.com> wrote: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.On Fri, Oct 3, 2008 at 8:04 AM, Sergey Gromov <snake.scaly 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. 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. :-)Thu, 02 Oct 2008 15:03:42 -0500, Andrei Alexandrescu wrote:Indeed. I thought there wasn't a lot of debate needed on this, just action.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 03 2008
"Andrei Alexandrescu" wroteSergey Gromov wrote: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? -SteveFri, 3 Oct 2008 09:37:46 +0900, Bill Baxter wrote:That's a good rule, but a[b] = c on a hash is not helped by it.On Fri, Oct 3, 2008 at 9:32 AM, Bill Baxter <wbaxter gmail.com> wrote: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.On Fri, Oct 3, 2008 at 8:04 AM, Sergey Gromov <snake.scaly 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. 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. :-)Thu, 02 Oct 2008 15:03:42 -0500, Andrei Alexandrescu wrote:Indeed. I thought there wasn't a lot of debate needed on this, just action.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 03 2008
"Steven Schveighoffer" wrote"Andrei Alexandrescu" wroteAfter 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. -SteveSergey Gromov wrote: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?Fri, 3 Oct 2008 09:37:46 +0900, Bill Baxter wrote:That's a good rule, but a[b] = c on a hash is not helped by it.On Fri, Oct 3, 2008 at 9:32 AM, Bill Baxter <wbaxter gmail.com> wrote: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.On Fri, Oct 3, 2008 at 8:04 AM, Sergey Gromov <snake.scaly 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. 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. :-)Thu, 02 Oct 2008 15:03:42 -0500, Andrei Alexandrescu wrote:Indeed. I thought there wasn't a lot of debate needed on this, just action.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 03 2008
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
Bill Baxter wrote:On Fri, Oct 3, 2008 at 9:37 AM, Bill Baxter <wbaxter gmail.com> wrote: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.... 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
Sergey Gromov wrote:Thu, 02 Oct 2008 15:03:42 -0500, Andrei Alexandrescu wrote: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() )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 03 2008
KennyTM~ wrote:Sergey Gromov wrote:Glad you brought opIndexAddAssign up. I think a good solution would avoid adding all opIndexXxxAssign functions. I think even opXxxAssign are undesirable. AndreiThu, 02 Oct 2008 15:03:42 -0500, Andrei Alexandrescu wrote: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() )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 03 2008
Andrei Alexandrescu wrote:KennyTM~ wrote:Of course I don't want opIndexAddAssign either ;p.Sergey Gromov wrote:Glad you brought opIndexAddAssign up. I think a good solution would avoid adding all opIndexXxxAssign functions. I think even opXxxAssign are undesirable. AndreiThu, 02 Oct 2008 15:03:42 -0500, Andrei Alexandrescu wrote: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() )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 03 2008
KennyTM~ wrote:Andrei Alexandrescu wrote: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.KennyTM~ wrote:Of course I don't want opIndexAddAssign either ;p.Sergey Gromov wrote:Glad you brought opIndexAddAssign up. I think a good solution would avoid adding all opIndexXxxAssign functions. I think even opXxxAssign are undesirable. AndreiThu, 02 Oct 2008 15:03:42 -0500, Andrei Alexandrescu wrote: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() )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 04 2008
Fri, 03 Oct 2008 20:59:54 +0800, KennyTM~ wrote:Sergey Gromov wrote: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.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
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: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. --bbSergey Gromov wrote: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.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
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: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.Fri, 03 Oct 2008 20:59:54 +0800, KennyTM~ wrote: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.Sergey Gromov wrote: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.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
KennyTM~ wrote:Sergey Gromov wrote:But if the requirement is that it should have the same *meaning* as 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.Thu, 02 Oct 2008 15:03:42 -0500, Andrei Alexandrescu wrote: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() )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 03 2008
Charles Hixson wrote:KennyTM~ wrote:That's a good point. Compiler magic can make sure stuff like that gets optimized, but user-defined object cannot benefit of such equivalence. AndreiSergey Gromov wrote:But if the requirement is that it should have the same *meaning* as 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.Thu, 02 Oct 2008 15:03:42 -0500, Andrei Alexandrescu wrote: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() )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 03 2008
Sergey Gromov wrote:Thu, 02 Oct 2008 15:03:42 -0500, Andrei Alexandrescu wrote: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. AndreiYah, 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 03 2008
Fri, 03 Oct 2008 09:02:07 -0500, Andrei Alexandrescu wrote:Sergey Gromov wrote: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.Thu, 02 Oct 2008 15:03:42 -0500, Andrei Alexandrescu wrote: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.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 03 2008
Sergey Gromov wrote:Fri, 03 Oct 2008 09:02:07 -0500, Andrei Alexandrescu wrote: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. AndreiSergey Gromov wrote: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.Thu, 02 Oct 2008 15:03:42 -0500, Andrei Alexandrescu wrote: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.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 03 2008
On Fri, Oct 3, 2008 at 11:02 PM, Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> wrote:Sergey Gromov wrote: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.Thu, 02 Oct 2008 15:03:42 -0500, Andrei Alexandrescu wrote:One problem is that for a hashtable that does not have b yet, opIndex will throw an exception.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)?Another problem (assuming the above is fixed) is that b will be looked up twice in the hash.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
Bill Baxter wrote:On Fri, Oct 3, 2008 at 11:02 PM, Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> wrote: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!Sergey Gromov wrote: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?Thu, 02 Oct 2008 15:03:42 -0500, Andrei Alexandrescu wrote:One problem is that for a hashtable that does not have b yet, opIndex will throw an exception.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)?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.It's one of the concerns. And there are several solutions that make everything work, including performance.Another problem (assuming the above is fixed) is that b will be looked up twice in the hash.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.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
Bill Baxter wrote:On Fri, Oct 3, 2008 at 11:02 PM, Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> wrote: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;}.Sergey Gromov wrote: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?Thu, 02 Oct 2008 15:03:42 -0500, Andrei Alexandrescu wrote:One problem is that for a hashtable that does not have b yet, opIndex will throw an exception.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 03 2008
On Sat, Oct 4, 2008 at 11:57 AM, KennyTM~ <kennytm gmail.com> wrote:Bill Baxter wrote: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. --bbOn Fri, Oct 3, 2008 at 11:02 PM, Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> wrote: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;}.Sergey Gromov wrote: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?Thu, 02 Oct 2008 15:03:42 -0500, Andrei Alexandrescu wrote:One problem is that for a hashtable that does not have b yet, opIndex will throw an exception.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 03 2008
Bill Baxter wrote:On Sat, Oct 4, 2008 at 11:57 AM, KennyTM~ <kennytm gmail.com> wrote: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! AndreiBill Baxter wrote: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.On Fri, Oct 3, 2008 at 11:02 PM, Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> wrote: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;}.Sergey Gromov wrote: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?Thu, 02 Oct 2008 15:03:42 -0500, Andrei Alexandrescu wrote:One problem is that for a hashtable that does not have b yet, opIndex will throw an exception.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 03 2008
On Sat, Oct 4, 2008 at 12:07 PM, Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> wrote:Bill Baxter wrote:Wierd. But yet plain a[b] will still throw an exception? --bbOn Sat, Oct 4, 2008 at 11:57 AM, KennyTM~ <kennytm gmail.com> wrote: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! AndreiBill Baxter wrote: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.On Fri, Oct 3, 2008 at 11:02 PM, Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> wrote: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;}.Sergey Gromov wrote: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?Thu, 02 Oct 2008 15:03:42 -0500, Andrei Alexandrescu wrote:One problem is that for a hashtable that does not have b yet, opIndex will throw an exception.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 03 2008
Andrei Alexandrescu wrote:Bill Baxter wrote: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#DOn Fri, Oct 3, 2008 at 3:16 AM, Fawzi Mohamed <fmohamed mac.com> 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. AndreiThis 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.
Oct 03 2008
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
Andrei Alexandrescu wrote:Bruno Medeiros wrote:If there is a cabal, it's a pretty lame one... Walter's Inner Circle are the ones with all the power! :o)Andrei Alexandrescu wrote:So there is a cabal. I knew it! :o)Bruno Medeiros wrote: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).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.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á '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
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á '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
Andrei Alexandrescu wrote:Bruno Medeiros wrote:If I understand the proposal correctly, it would be a compile-time error. --benjiIn fact, I'm also not a fan of those complex property mechanisms, à lá '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
Oct 03 2008
Andrei Alexandrescu wrote:Bruno Medeiros wrote: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)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.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#DIn fact, I'm also not a fan of those complex property mechanisms, à lá '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 06 2008
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" - 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 (andWho 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. AndreiSorry, 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
On Fri, Oct 3, 2008 at 2:32 AM, KennyTM~ <kennytm gmail.com> wrote:Andrei Alexandrescu wrote: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.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.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
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
Steven Schveighoffer wrote:"Andrei Alexandrescu" wroteNo, you shouldn't read it that way. (Explanation below.)Steven Schveighoffer wrote: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.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.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.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.Not really. You provided no proof that your principles apply to this debate, you just stated that they do.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.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.Respectfully disagree.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.I am glad this partially took care of itself. AndreiHow 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.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() {}
Sep 29 2008
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
Benji Smith wrote:Andrei Alexandrescu wrote: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.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.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
Andrei Alexandrescu wrote:Benji Smith wrote: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.Andrei Alexandrescu wrote: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 contradictionI 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.An example of consistency is that user-defined operators have the same syntax and precedence as built-in operators.Then why are you not suggesting, or campaigning for, such change?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.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#DRequiring 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.
Oct 02 2008
Bruno Medeiros wrote: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. 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.Benji Smith wrote: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.)Andrei Alexandrescu wrote: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 contradictionI 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.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.Because language design is putting many desiderata in agreement, not following one blindly.An example of consistency is that user-defined operators have the same syntax and precedence as built-in operators.Then why are you not suggesting, or campaigning for, such change?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.No. You can't define a term to mean whatever you want. I mean you can, but then it's hard to communicate.It violates consistency, as defined above.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 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
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(): 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
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
Andrei Alexandrescu wrote:Bruno Medeiros wrote: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.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.Benji Smith wrote: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.)Andrei Alexandrescu wrote: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 contradictionI 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.I agree.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 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)Because language design is putting many desiderata in agreement, not following one blindly.Then why are you not suggesting, or campaigning for, such change?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.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.No. You can't define a term to mean whatever you want. I mean you can, but then it's hard to communicate.It violates consistency, as defined above.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 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#DIt 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
Bruno Medeiros wrote:Andrei Alexandrescu wrote: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.)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)I think keeping the language simple is also a good goal. AndreiBy "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.No. You can't define a term to mean whatever you want. I mean you can, but then it's hard to communicate.It violates consistency, as defined above.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 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?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
== Quote from Andrei Alexandrescu (SeeWebsiteForEmail erdani.org)'s articleBruno Medeiros wrote: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. SeanI 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.
Oct 02 2008
On 2008-10-02 20:06:32 +0200, Sean Kelly <sean invisibleduck.org> said:== Quote from Andrei Alexandrescu (SeeWebsiteForEmail erdani.org)'s articleOk 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. FawziBruno Medeiros wrote: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. SeanI 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.
Oct 02 2008
Sean Kelly wrote:== Quote from Andrei Alexandrescu (SeeWebsiteForEmail erdani.org)'s articleOh 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. AndreiBruno Medeiros wrote: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.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.
Oct 02 2008
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. --bbRunning 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
Bill Baxter 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. AndreiHe 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.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
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
Andrei Alexandrescu wrote:Sean Kelly 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. -- Bruno Medeiros - Software Developer, MSc. in CS/E graduate http://www.prowiki.org/wiki4d/wiki.cgi?BrunoMedeiros#D== Quote from Andrei Alexandrescu (SeeWebsiteForEmail erdani.org)'s articleOh 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. AndreiBruno Medeiros wrote: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.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.
Oct 03 2008
On 2008-10-03 12:05:17 +0200, Bruno Medeiros <brunodomedeiros+spam com.gmail> said:Andrei Alexandrescu wrote: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... FawziSean Kelly 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.== Quote from Andrei Alexandrescu (SeeWebsiteForEmail erdani.org)'s articleOh 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. AndreiBruno Medeiros wrote: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.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.
Oct 03 2008
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
Andrei Alexandrescu wrote:Bruno Medeiros wrote: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#DHold 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 06 2008
On 2008-10-02 21:13:16 +0200, Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> said:Sean Kelly wrote: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).== Quote from Andrei Alexandrescu (SeeWebsiteForEmail erdani.org)'s articleOh how I am with you on this. I've always thought mentioning a delegate name should NOT EVER evaluate the delegate.Bruno Medeiros wrote: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.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.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
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
Benji Smith wrote:Andrei Alexandrescu wrote: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. --YigalI 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
Oct 01 2008
Yigal Chripun wrote:Benji Smith wrote: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? AndreiAndrei Alexandrescu wrote: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.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
Oct 01 2008
Andrei Alexandrescu wrote:Yigal Chripun wrote: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.Benji Smith wrote: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? AndreiAndrei Alexandrescu wrote: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.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
Oct 01 2008
Yigal Chripun wrote:Andrei Alexandrescu wrote:What is the signature of that function? AndreiYigal Chripun wrote: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.Benji Smith wrote: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? AndreiAndrei Alexandrescu wrote: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.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
Oct 01 2008
Andrei Alexandrescu wrote:Yigal Chripun wrote: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?Andrei Alexandrescu wrote:What is the signature of that function? AndreiYigal Chripun wrote: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.Benji Smith wrote: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? AndreiAndrei Alexandrescu wrote: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.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
Oct 01 2008
Yigal Chripun wrote:Andrei Alexandrescu wrote:That doesn't stay glued. What is "this" inside the function?Yigal Chripun wrote: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);Andrei Alexandrescu wrote:What is the signature of that function? AndreiYigal Chripun wrote: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.Benji Smith wrote: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? AndreiAndrei Alexandrescu wrote: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.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. --benjiare 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
Andrei Alexandrescu wrote:Yigal Chripun wrote: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. -- YigalAndrei Alexandrescu wrote:That doesn't stay glued. What is "this" inside the function?Yigal Chripun wrote: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);Andrei Alexandrescu wrote:What is the signature of that function? AndreiYigal Chripun wrote: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.Benji Smith wrote: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? AndreiAndrei Alexandrescu wrote: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.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. --benjiare 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
Yigal Chripun wrote:Andrei Alexandrescu wrote: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. AndreiYigal Chripun wrote: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.Andrei Alexandrescu wrote:That doesn't stay glued. What is "this" inside the function?Yigal Chripun wrote: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);Andrei Alexandrescu wrote:What is the signature of that function? AndreiYigal Chripun wrote: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.Benji Smith wrote: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? AndreiAndrei Alexandrescu wrote: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.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. --benjiare 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
Andrei Alexandrescu wrote:Yigal Chripun wrote: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?Andrei Alexandrescu wrote: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. AndreiYigal Chripun wrote: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.Andrei Alexandrescu wrote:That doesn't stay glued. What is "this" inside the function?Yigal Chripun wrote: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);Andrei Alexandrescu wrote:What is the signature of that function? AndreiYigal Chripun wrote: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.Benji Smith wrote: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? AndreiAndrei Alexandrescu wrote: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.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. --benjiare 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
Yigal Chripun wrote:Andrei Alexandrescu wrote:I think we're better off with the current state of affairs. AndreiYigal Chripun wrote: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?Andrei Alexandrescu wrote: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. AndreiYigal Chripun wrote: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.Andrei Alexandrescu wrote:That doesn't stay glued. What is "this" inside the function?Yigal Chripun wrote: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);Andrei Alexandrescu wrote:What is the signature of that function? AndreiYigal Chripun wrote: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.Benji Smith wrote: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? AndreiAndrei Alexandrescu wrote: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.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. --benjiare 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
Andrei Alexandrescu wrote:Yigal Chripun wrote: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. YigalAndrei Alexandrescu wrote:I think we're better off with the current state of affairs. AndreiYigal Chripun wrote: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?Andrei Alexandrescu wrote: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. AndreiYigal Chripun wrote: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.Andrei Alexandrescu wrote:That doesn't stay glued. What is "this" inside the function?Yigal Chripun wrote: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);Andrei Alexandrescu wrote:What is the signature of that function? AndreiYigal Chripun wrote: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.Benji Smith wrote: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? AndreiAndrei Alexandrescu wrote: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.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. --benjiare 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
Yigal Chripun Wrote:Andrei Alexandrescu wrote: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.Yigal Chripun wrote: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. YigalAndrei Alexandrescu wrote:I think we're better off with the current state of affairs. AndreiYigal Chripun wrote: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?Andrei Alexandrescu wrote: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. AndreiYigal Chripun wrote: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.Andrei Alexandrescu wrote:That doesn't stay glued. What is "this" inside the function?Yigal Chripun wrote: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);Andrei Alexandrescu wrote:What is the signature of that function? AndreiYigal Chripun wrote: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.Benji Smith wrote: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? AndreiAndrei Alexandrescu wrote: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.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. --benjiare 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
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. --YigalI 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
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
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
"Michel Fortin" wroteOn 2008-09-29 12:55:18 -0400, "Steven Schveighoffer" said: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. 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.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.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
On 2008-09-30 09:42:43 -0400, "Steven Schveighoffer" <schveiguy yahoo.com> said:"Michel Fortin" wroteBut 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.On 2008-09-29 12:55:18 -0400, "Steven Schveighoffer" said: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.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.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 """ 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.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/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.
Oct 01 2008
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: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"Michel Fortin" wroteBut 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.On 2008-09-29 12:55:18 -0400, "Steven Schveighoffer" said: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.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.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 """ 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.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.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.
Oct 01 2008
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
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
Bill Baxter wrote:On Thu, Oct 2, 2008 at 8:26 PM, Michel Fortin <michel.fortin michelf.com> wrote:Yes, I entirely agree. I mentioned this a couple of times, but never explained it so well and thoroughly. AndreiCompare 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.
Oct 02 2008
Andrei Alexandrescu wrote:Bill Baxter wrote: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. --benjiOn Thu, Oct 2, 2008 at 8:26 PM, Michel Fortin <michel.fortin michelf.com> wrote:Yes, I entirely agree. I mentioned this a couple of times, but never explained it so well and thoroughly. AndreiCompare 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.
Oct 02 2008
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
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 Andrei
Oct 02 2008
"Michel Fortin" wroteOn 2008-09-30 09:42:43 -0400, "Steven Schveighoffer" said: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."Michel Fortin" wroteBut 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.On 2008-09-29 12:55:18 -0400, "Steven Schveighoffer" said: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.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.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.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.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."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?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.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 """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.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. -SteveBut 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.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.
Oct 02 2008
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
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
Bruno Medeiros wrote:Andrei Alexandrescu wrote: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). AndreiI routinely use writeln; without feeling it makes for inferior style.Dear Gods! O_o
Oct 02 2008
Steven Schveighoffer wrote:"Andrei Alexandrescu" wroteWhat 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.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.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".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
On 2008-09-27 00:01:05 -0400, Christopher Wright <dhasenan gmail.com> said:Steven Schveighoffer wrote:Very true."Andrei Alexandrescu" wroteWhat 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.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.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".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
Andrei Alexandrescu wrote:KennyTM~ wrote:I mean the discussion within the bug report. That's a voice from the community isn't it?!Andrei Alexandrescu wrote:I don't think the bug report has much strength.Bruno Medeiros wrote: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.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 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.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
KennyTM~ wrote:Andrei Alexandrescu wrote:I agree. But then should we take arguments at face value, or just consider them valid because they exist?KennyTM~ wrote:I mean the discussion within the bug report. That's a voice from the community isn't it?!Andrei Alexandrescu wrote:I don't think the bug report has much strength.Bruno Medeiros wrote: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.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.I agree, hence my "far-fetched" qualification in the reply to Steve. AndreiIt'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.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".
Sep 26 2008