digitalmars.D - try/catch idiom in std.datetime
- Andrei Alexandrescu (55/55) Nov 17 2013 I'm of a stance that good code should have many throws and few try/catch...
- Jonathan M Davis (88/103) Nov 17 2013 That code's like that just because I like to put empty lines before and ...
- Andrei Alexandrescu (17/53) Nov 18 2013 "Too much of a good thing" comes to mind (as does with the massive and
- Jonathan M Davis (42/102) Nov 18 2013 Well, my coding style has adjusted over time, and I'm tending to put few...
- Jacob Carlborg (5/10) Nov 18 2013 Asserts are supposed to be removed in release builds. There's also the
- Jonathan M Davis (7/17) Nov 18 2013 The aren't if the condition is statically known to be false, as is the c...
- Jacob Carlborg (6/11) Nov 18 2013 Then it supposed to turn it into a HLT instruction, at least on x86. I
- Jacob Carlborg (17/69) Nov 18 2013 I like the empty newlines as well. Otherwise it feels like reading a
- Jonathan M Davis (5/18) Nov 18 2013 I don't see much gain over simply putting scope(failure) at the top. You...
- Jacob Carlborg (5/8) Nov 18 2013 No. It would just return from the delegate.
- Jonathan M Davis (8/13) Nov 18 2013 True, but if the language did it, it could deal with it in a manner whic...
- Andrei Alexandrescu (4/21) Nov 18 2013 But three newlines for four lines? That's not paragraphs, it's a grocery...
- Jonathan M Davis (9/15) Nov 18 2013 Yeah. It falls apart on some level when there are so few lines. It's jus...
- Andrei Alexandrescu (11/25) Nov 18 2013 Well you must agree at some level that since you don't like short
- Jonathan M Davis (7/20) Nov 18 2013 I agree. My style has adjusted over time. It's just that I don't like an...
- Jacob Carlborg (9/11) Nov 18 2013 As Jonathan said, this example is quite bad in the empty lines vs code
- Andrei Alexandrescu (7/13) Nov 18 2013 So that would be two empty lines between statements :o). Anyhow,
- Jacob Carlborg (8/13) Nov 18 2013 I don't agree. And not having any empty newlines in the code would be
- Jonathan M Davis (25/40) Nov 19 2013 That looks pretty good overall, though there are probably a few places w...
- Jacob Carlborg (13/36) Nov 19 2013 That particular version block is from a pull request and doesn't follow
- Andrei Alexandrescu (3/14) Nov 19 2013 That doesn't have an empty line after each statement.
- Jacob Carlborg (5/6) Nov 19 2013 If an expression or another statement follows, then yes. But not if a
- Andrei Alexandrescu (4/7) Nov 19 2013 You're not operating with the correct definitions of "expression" and
- Jacob Carlborg (11/13) Nov 19 2013 I would consider this a statement:
- David Nadlinger (4/12) Nov 19 2013 http://dlang.org/statement.html#ExpressionStatement
- Jacob Carlborg (4/5) Nov 19 2013 Ok, I didn't know that.
- Andrei Alexandrescu (10/17) Nov 19 2013 That's not an expression, it's a statement - more precisely an
- John Colvin (43/46) Nov 19 2013 Doesn't look *that* sparse to me... I think the 8-space
- Jacob Carlborg (4/6) Nov 19 2013 I'm using one tab as indentation. It's Github formatting it like that.
- Walter Bright (3/7) Nov 19 2013 I've abandoned using tabs as every text rendering engine interprets them...
- Marco Leise (4/6) Nov 20 2013 Yes, that's one of the selling points of using tabs, as
- Jacob Carlborg (5/12) Nov 19 2013 Are you serious? Anyone caring about that doesn't know what he/she is do...
- Andrei Alexandrescu (16/28) Nov 19 2013 It's not a minor detail. Besides, people took time to explain this to
- growler (22/57) Nov 19 2013 Man, I thought these debates died out long ago. All that matters
- Jacob Carlborg (4/23) Nov 19 2013 Thank you.
- Dicebot (10/12) Nov 20 2013 Very true. I sometimes switch between 3 to 4 different code
- Andrei Alexandrescu (5/18) Nov 20 2013 Oh there are consistent styles that are worse than other consistent
- Ary Borenszweig (2/20) Nov 20 2013 Of course: the ones you don't like.
- Andrei Alexandrescu (4/25) Nov 20 2013 What I meant is there are consistent styles that are objectively worse.
- Dicebot (7/9) Nov 20 2013 And what I meant is this opinion of yours is wrong. Any
- =?UTF-8?B?U2ltZW4gS2rDpnLDpXM=?= (16/24) Nov 20 2013 I think you misunderstand Andrei here. There are styles that are
- Andrei Alexandrescu (13/19) Nov 20 2013 There are a bunch of styles that are consistent yet used by nobody,
- growler (13/16) Nov 20 2013 On Wednesday, 20 November 2013 at 20:47:10 UTC, Andrei
- Dicebot (11/17) Nov 20 2013 You see, the whole definition of term "objective comparison"
- Andrei Alexandrescu (3/11) Nov 20 2013 No, because the quantity being measured is "worse".
- Dicebot (9/11) Nov 20 2013 If it would have been an inherent well-defined trait of an object
- Andrei Alexandrescu (4/13) Nov 20 2013 In this context "objectively worse" means "all reasonable people would
- Dicebot (13/16) Nov 20 2013 Which is exactly hidden changing of word meaning to push own
- Andrei Alexandrescu (7/21) Nov 20 2013 Yah, and it's a fact that certain coding styles would not be used in
- xenon325 (22/28) Nov 20 2013 A sample of code from my very first job (needless to say that
- Walter Bright (4/6) Nov 20 2013 It could also be objectively measured as:
- Dicebot (17/25) Nov 21 2013 You need to prove first that it results in more bugs because of
- Walter Bright (10/17) Nov 21 2013 You're saying that human factors engineering is purely a matter of perso...
- deadalnix (3/21) Nov 20 2013 Ho please, we aren't in a schoolyard.
- Walter Bright (4/6) Nov 20 2013 To reiterate my analogy, some cockpit control designs caused more crashe...
- Walter Bright (16/23) Nov 20 2013 I'm going to very strongly disagree with this.
- MattCoder (3/11) Nov 21 2013 I strongly agree with Walter on this one.
- Jonathan M Davis (17/19) Nov 20 2013 I agree, but you do come across sometimes as seeming to think that some ...
- Andrei Alexandrescu (3/10) Nov 20 2013 That I agree with.
- Walter Bright (12/17) Nov 20 2013 Legibility can be objective, too.
- Jonathan M Davis (15/39) Nov 20 2013 Definitely, but almost all arguments over coding style seem to be very
- Walter Bright (13/27) Nov 21 2013 I've run across actual research on aspects of this now and then, and rea...
- deadalnix (29/55) Nov 22 2013 It is because it is really context sensitive. Let me take a
- John J (4/36) Nov 19 2013 This is great stuff for every developer to learn, but can someone please...
- growler (3/63) Nov 19 2013 I find astyle works very well for D.
- H. S. Teoh (17/32) Nov 19 2013 [...]
- Ary Borenszweig (18/44) Nov 20 2013 One day you should all stop doing what you are doing and focus on
- Dicebot (8/10) Nov 20 2013 You'll be surprised - https://github.com/yebblies/magicport2 ;)
- Ary Borenszweig (4/13) Nov 20 2013 Awesome :-)
- Daniel Murphy (3/12) Nov 20 2013 Compiler-as-a-library comes next.
- Jacob Carlborg (24/38) Nov 19 2013 I think so. I've not magically become a better programmer just because I...
- Andrei Alexandrescu (233/241) Nov 18 2013 I thought about this some more. So std.datetime accounts for > 1/3 of
- Walter Bright (5/20) Nov 18 2013 I agree. But I'll add that it would be even better to redesign FracSec.f...
- Jonathan M Davis (29/35) Nov 18 2013 I think that that depends. In some cases, it would just mean duplicating...
- Walter Bright (11/45) Nov 18 2013 I think this is exactly a case where you want to have data validation se...
- Jonathan M Davis (14/35) Nov 18 2013 But that would just duplicate the validation. You validate by parsing th...
- Walter Bright (18/30) Nov 18 2013 The point of asserts is that they are supposed to be redundant. (If they...
- Jonathan M Davis (55/71) Nov 18 2013 I understand this. The problem is that in some cases, in order to do the...
- Michel Fortin (14/18) Nov 18 2013 Very true.
- Regan Heath (10/27) Nov 19 2013 Why use templates at all? Lets just keep it simple..
- Jesse Phillips (8/11) Nov 20 2013 I feel like you and Walter are both saying the same thing.
- Jonathan M Davis (35/50) Nov 20 2013 As I understand it, when Walter is talking about a validator function, _...
- Jesse Phillips (8/12) Nov 21 2013 I doubt Walter first checks that a file is a valid D program
- Jonathan M Davis (16/30) Nov 21 2013 Of course not, which makes his argument all the odder. Maybe he misunder...
- Walter Bright (3/7) Nov 18 2013 I also have a design issue with having throwing constructors at all. I k...
- Jacob Carlborg (5/17) Nov 18 2013 I really don't understand the point in having some functions thrown and
- Walter Bright (4/6) Nov 18 2013 That's a red flag that there is a faulty design somewhere, in this case ...
- Andrei Alexandrescu (22/29) Nov 18 2013 The question is what's "program" and what's "input". Consider:
- Walter Bright (7/23) Nov 18 2013 Or:
- Timon Gehr (12/42) Nov 18 2013 1. Has the shortcoming that you may actually get assertion failures.
- Jonathan M Davis (3/40) Nov 18 2013 +1
- Jacob Carlborg (14/34) Nov 18 2013 In D, we can start by saying that all private and package functions
- Jouko Koski (8/10) Nov 19 2013 If the "user level" validation fails, shall we throw or assert or...?
- Jacob Carlborg (4/5) Nov 19 2013 Throw.
- Jouko Koski (16/19) Nov 19 2013 Ok. What if the "user" is writing a library code which calls other syste...
- Jacob Carlborg (10/24) Nov 19 2013 I neither have a definite answer. It depends on what that particular
- Dmitry Olshansky (21/46) Nov 19 2013 3. The standard library handles all input, the wrong input is eliminated...
- Jonathan M Davis (9/23) Nov 19 2013 True, though there can still be invalid days depending on the month and ...
- Jonathan M Davis (26/33) Nov 18 2013 Clearly, I need to take a look at this. I don't think that I ever made a...
- Daniel Murphy (3/4) Nov 18 2013 scope(failure) picks up Errors as well as Exceptions
- Jonathan M Davis (6/12) Nov 18 2013 Ouch. That's true, which more or less kills that idea - though according...
- Daniel Murphy (7/22) Nov 18 2013 Yeah. On the other hand, if we decide assert(0) means 'assume unreachab...
- Jonathan M Davis (6/13) Nov 18 2013 I was hoping that we could do something like that, since that would fix ...
- Jacob Carlborg (7/11) Nov 18 2013 "assume" isn't the same as the compile will do this. Currently the spec
- Jonathan M Davis (14/26) Nov 18 2013 assert(0) is intended specifically for use in cases where a line is supp...
- Jacob Carlborg (5/17) Nov 18 2013 Does all architectures support the HLT instruction or equivalent? The
- Jonathan M Davis (15/33) Nov 18 2013 I don't know. My knowledge of stuff at that level on x86 is already poor...
- Walter Bright (6/8) Nov 18 2013 Most architectures I've used supported a HLT, and there's always somethi...
- Jacob Carlborg (5/10) Nov 18 2013 In that case, would we want it to be legal to throw AssertError instead
- Walter Bright (2/11) Nov 18 2013 Sure.
- Jonathan M Davis (4/6) Nov 18 2013 For when you're not in release mode. You want the more informative Asser...
- Jacob Carlborg (4/6) Nov 18 2013 I'm talking about release mode.
- Dicebot (4/10) Nov 19 2013 I am pretty sure on my x86_64 Linux `assert(0)` results in
- Walter Bright (7/11) Nov 18 2013 I seriously object to this, as assert(0) is there for when you really do...
- Jonathan M Davis (16/33) Nov 18 2013 Then what would you suggest for dealing with cases where a nothrow funct...
- Walter Bright (4/16) Nov 18 2013 I believe this is solving the problem in the wrong place. The function b...
- Jonathan M Davis (10/29) Nov 18 2013 Sometimes that's true, but if you're asserting that it's not going to be...
- Walter Bright (18/26) Nov 18 2013 What I'm saying is it is bad API design to conflate data processing with...
- Andrei Alexandrescu (4/7) Nov 18 2013 I think a stance of unceremoniously halting the program upon passing the...
- Walter Bright (12/18) Nov 18 2013 Dang, I gotta argue this with you too? :-)
- Andrei Alexandrescu (4/14) Nov 18 2013 Yes. I agree with your fundamental point but it is missing important
- Walter Bright (3/5) Nov 18 2013 And I destroyed your destruction!
- Dmitry Olshansky (6/28) Nov 19 2013 There is a whole class of functions where validation is ~ the same
- Andrei Alexandrescu (22/26) Nov 18 2013 That's fine. What the compiler should do for a nothrow function goes as
- Daniel Murphy (6/18) Nov 18 2013 Yes.
- Marco Leise (6/20) Nov 18 2013 That's severe. It's not even a rethrow, but an independent
- Andrei Alexandrescu (9/18) Nov 18 2013 Now I got it, thanks. Far as I can tell in this case it doesn't make a
- Kagamin (3/3) Nov 18 2013 As I understand, nothrow is used for minor optimization of
- Walter Bright (4/5) Nov 18 2013 Revisit why removeUnitsFromHNSecs, etc., are throwing. Since the caller ...
- Shammah Chancellor (5/9) Nov 21 2013 Huh? Scope failure has no purpose here. It does not CATCH the
- growler (30/39) Nov 21 2013 It does if you return from the scope(failure) block. The problem
- Andrei Alexandrescu (3/17) Nov 21 2013 I was thinking that scope(failure) could throw an Error.
- growler (3/25) Nov 21 2013 Yes, throwing an error makes more sensee than just return;
- Shammah Chancellor (22/69) Nov 21 2013 What!? That shouldn't even be legal code! See below for why:
- growler (8/10) Nov 21 2013 On Friday, 22 November 2013 at 02:35:40 UTC, Shammah Chancellor
- Shammah Chancellor (11/15) Nov 22 2013 I think fixing it means disallowing return and assert(0) here. Since
- Jesse Phillips (16/25) Nov 21 2013 That does exactly as expected:
- Shammah Chancellor (6/32) Nov 22 2013 No. scope(failure) is supposed to re-throw the exception. Remember
- growler (17/19) Nov 22 2013 On Friday, 22 November 2013 at 21:48:14 UTC, Shammah Chancellor
- Andrei Alexandrescu (6/14) Nov 22 2013 It's implied. The scope is already being exited by means of an
- growler (3/23) Nov 22 2013 I stand corrected, cheers.
- Jesse Phillips (10/19) Nov 21 2013 If the code is:
- Shammah Chancellor (3/27) Nov 22 2013 Scope failure blocks should not be able to raise errors, or return
I'm of a stance that good code should have many throws and few try/catch statements. I was curious how Phobos does there, so I ran a few simple greps around. Seems like there's about 145 try statements in Phobos. Of these, more than a third (51) belong to std.datetime. Looks like the following idiom is often present in std.datetime: property FracSec fracSec() const nothrow { try { auto hnsecs = removeUnitsFromHNSecs!"days"(adjTime); if(hnsecs < 0) hnsecs += convert!("hours", "hnsecs")(24); hnsecs = removeUnitsFromHNSecs!"seconds"(hnsecs); return FracSec.from!"hnsecs"(cast(int)hnsecs); } catch(Exception e) assert(0, "FracSec.from!\"hnsecs\"() threw."); } (It may seem I chose this example to discuss the "let's insert one empty line every other line" idiom. I didn't.) Essentially the code relies on calls that may generally throw, but calls them with parameters that should guarantee there won't be any throwing. In wanting to offer as many "nothrow" guarantees as possible, the code ends up inserting these try/catch statements - seemingly needlessly. This is quite heavy-handed, so I was wondering what we could do to improve on this. I thought of the following possibility: property FracSec fracSec() const nothrow { scope(failure) assert(0, "FracSec.from!\"hnsecs\"() threw."); auto hnsecs = removeUnitsFromHNSecs!"days"(adjTime); if (hnsecs < 0) hnsecs += convert!("hours", "hnsecs")(24); hnsecs = removeUnitsFromHNSecs!"seconds"(hnsecs); return FracSec.from!"hnsecs"(cast(int)hnsecs); } This at least leaves the normal path unaltered and deals with the unexpected in a more isolated manner. I was pleased that the changed code compiled just fine. My happiness was short-lived because before long I figured this compiles as well: ... scope(failure) {} ... So it's not that the compiler cleverly figured the function will not throw. Instead, the compiler considers everything dandy as soon as it sees a scope(failure), no matter of the statement it controls. I'll report that bug soon. What would be the best approach here? 0. Do nothing. This is as good as it gets. 1. Fix scope(failure) and then use it. 2. Relax the nothrow guarantees. After all, nothrow is opt-in. 3. Look into API changes that add nothrow, narrower functions to the more general ones that may throw. 4. ...? Andrei
Nov 17 2013
On Sunday, November 17, 2013 22:32:46 Andrei Alexandrescu wrote:(It may seem I chose this example to discuss the "let's insert one empty line every other line" idiom. I didn't.)That code's like that just because I like to put empty lines before and after if statements and before return statements, as I think that that improves legibility. Short functions like that suffer as a result, because they end up with a larger proportion of the lines being empty than is normal. I'm always a bit torn on that, because I don't like having quite that many empty lines in so few lines, but I also don't like not having space around if statements and return statements. I never feel like I can find a nice, legible balance with short functions like that.Essentially the code relies on calls that may generally throw, but calls them with parameters that should guarantee there won't be any throwing. In wanting to offer as many "nothrow" guarantees as possible, the code ends up inserting these try/catch statements - seemingly needlessly.Yeah, I tried to use pure and nothrow fairly heavily in std.datetime and ran into a number of hurdles like this. Fortunately, the situation has improved somewhat (e.g. format can finally be pure at least some of the time), but it does show that it's not always easy to use pure or nothrow even when it arguably should be.What would be the best approach here? 0. Do nothing. This is as good as it gets.Well, it works just fine as-is, but it would be kind of nice to be able to solve the problem in a less verbose manner (though you're talking about saving only a few lines of code).1. Fix scope(failure) and then use it.Fine with me. I might still favor the try-catch in cases where you can clearly wrap it around one function call, because then you avoid problems where you've accidentally effectively marked the whole function as trusted-nothrow when you only want to mark one function call that way. But you could do the same thing with scope(failure) and a new scope. The main problem is when you can't really put the calls that need to be trusted-nothrow inside a new scope, in which case, you're forced to mark the whole function (or at least large portions of it) as trusted-nothrow by wrapping it all in a try-catch or scope(failure).2. Relax the nothrow guarantees. After all, nothrow is opt-in.I'm not quite sure what you're suggesting here. Make it so that nothrow does checking at runtime instead of compile time? That would be moving in the direction of C++ and throw specifiers (or more precisely, noexcept, I suppose). If that's what you're suggesting, I'd be very much against that. I think that the fact D's nothrow is statically checked is a huge advantage over C++'s noexcept. The fact that you have to sometimes use try-catch blocks (or scope(failure) if that works) to make it work is essentially the same as needing trusted to make some stuff safe. I wouldn't want to throw away trusted in favor of making safe more lax either (though that's almost all static checking which can't be done at runtime, unlike with noexcept). Of course, there's no way to verify trusted-nothrow except at runtime like std.datetime is doing with try-catch and assertions, but most code _can_ be checked statically (including the code that calls the functions that use the try-catch-assert idiom to be able to be nothrow), and that's much more pleasant, particularly because it's actually checked by the compiler that way rather than just blowing up on you at runtime. I suppose that if it were considered annoying enough to have to use try-catch blocks or scope(failure), we could add a nothrow equivalent to trusted to mark functions with, though it's already been argued that trusted should be on pieces of a function rather than on the whole function, and it would arguably be better to mark sections of a function as trusted-nothrow rather than the entire thing. try-catch lets us do that already, but it might be nice to be able to do the equivalent of property FracSec fracSec() const nothrow { trusted-nothrow { auto hnsecs = removeUnitsFromHNSecs!"days"(adjTime); if(hnsecs < 0) hnsecs += convert!("hours", "hnsecs")(24); hnsecs = removeUnitsFromHNSecs!"seconds"(hnsecs); return FracSec.from!"hnsecs"(cast(int)hnsecs); } } and have the compiler insert the catch and assertion for you.3. Look into API changes that add nothrow, narrower functions to the more general ones that may throw.In some cases (e.g. format) that's probably a good idea, but I don't think that really scales. In some cases, it would be well worth it, whereas in others, it would just be better to use try-catch in the few places where you know the function won't throw, because it would be quite rare to be able to guarantee that it wouldn't throw. It's also not pleasant to have to duplicate functions all over the place.4. ...?We now have std.exception.assumeWontThrow, which works reasonably well when you need to wrap a single call as opposed to several, but it has the same problem as enforce in that it uses lazy, which is definite performance hit. So, in most cases, I'd be more inclined to just use a try-catch, and if it's more than one expression, you pretty much need to use try-catch or scope(failure) instead anyway, since you wouldn't want to wrap whole function bodies in a call to assumeWontThrow (assuming that you even could). So, that's a partial solution, but not a perfomant one. However, we do really need to improve the performance of lazy, because enforce gets used all over the place, and it's definitely shown up as being costly in some of the profiling that's been shown in the newsgroup. And if that gets fixed, then using assumeWontThrow wouldn't be as bad. All in all, I find the need to use try-catch blocks to effectively do trusted- nothrow a bit annoying, but I haven't felt that it was a big enough deal to try and find another solution for it. Having to do trusted-purity is far worse, because that requires using a function pointer and casting it, but that would _should_ be hard to do because of how hard it is to actually guarantee that the function is acting like a pure function even though it isn't. And I'm not sure that I'd entirely trust myself with that, let alone the average D developer. trusted-nothrow on the other hand is something that the average programmer should be able to grasp. - Jonathan M Davis
Nov 17 2013
On 11/17/13 11:28 PM, Jonathan M Davis wrote:On Sunday, November 17, 2013 22:32:46 Andrei Alexandrescu wrote:"Too much of a good thing" comes to mind (as does with the massive and repetitive unittests in std.datetime). If code looks and feels meh, it probably is. Just apply good judgment instead of rote adherence to rules.(It may seem I chose this example to discuss the "let's insert one empty line every other line" idiom. I didn't.)That code's like that just because I like to put empty lines before and after if statements and before return statements, as I think that that improves legibility. Short functions like that suffer as a result, because they end up with a larger proportion of the lines being empty than is normal. I'm always a bit torn on that, because I don't like having quite that many empty lines in so few lines, but I also don't like not having space around if statements and return statements. I never feel like I can find a nice, legible balance with short functions like that.I'm also concerned about generated code size and overall efficiency. It looks like the assert(0) insertions are there simply to validate the design (they only fail if Phobos has an internal error), so there should be some means to remove them in release builds. We don't have such a possibility at the moment.0. Do nothing. This is as good as it gets.Well, it works just fine as-is, but it would be kind of nice to be able to solve the problem in a less verbose manner (though you're talking about saving only a few lines of code).What I meant is, not everything that is nothrow needs to be annotated as such.2. Relax the nothrow guarantees. After all, nothrow is opt-in.I'm not quite sure what you're suggesting here.property FracSec fracSec() const nothrow { trusted-nothrow { auto hnsecs = removeUnitsFromHNSecs!"days"(adjTime); if(hnsecs < 0) hnsecs += convert!("hours", "hnsecs")(24); hnsecs = removeUnitsFromHNSecs!"seconds"(hnsecs); return FracSec.from!"hnsecs"(cast(int)hnsecs); } }That can be done at library level.Again, one problem here (in addition to the blowup in code size and decay of readability) is we're talking about an internal design validation, not meaningful runtime semantics. There should be a way to remove the baggage. Andrei4. ...?We now have std.exception.assumeWontThrow, which works reasonably well when you need to wrap a single call as opposed to several, but it has the same problem as enforce in that it uses lazy, which is definite performance hit. So, in most cases, I'd be more inclined to just use a try-catch, and if it's more than one expression, you pretty much need to use try-catch or scope(failure) instead anyway, since you wouldn't want to wrap whole function bodies in a call to assumeWontThrow (assuming that you even could).
Nov 18 2013
On Monday, November 18, 2013 00:14:24 Andrei Alexandrescu wrote:On 11/17/13 11:28 PM, Jonathan M Davis wrote:Well, my coding style has adjusted over time, and I'm tending to put fewer blank lines in cases like this, precisely because it's not terribly pretty.On Sunday, November 17, 2013 22:32:46 Andrei Alexandrescu wrote:"Too much of a good thing" comes to mind (as does with the massive and repetitive unittests in std.datetime). If code looks and feels meh, it probably is. Just apply good judgment instead of rote adherence to rules.as does with the massive and repetitive unittests in std.datetimeThose are quite valuable IMHO, as being thorough has caught a lot of problems, and as we've discussed (and disagreed on) before, I prefer unit tests to be dead simple in order to reduce the odds of them being buggy, even if that means that they're more verbose. I have done some work however to move the tests toward using loops instead in order to reduce the number of lines of code, but I have quite a bit still to do there. I'll probably get back to that after I finish with splitting std.datetime, but I haven't finished that yet because of how busy I've been the past few months. So, everything takes forever... Oh well, I should get back to that soon enough.Well, the assertions are there to validate the design, but the try-catches are required to make the function nothrow, and I'd feel very funny having an empty catch block, though having it compiled out in -release wouldn't necessarily be a bad idea (which could probably be done if a function which returned false where used in the assertion instead of a constant).I'm also concerned about generated code size and overall efficiency. It looks like the assert(0) insertions are there simply to validate the design (they only fail if Phobos has an internal error), so there should be some means to remove them in release builds. We don't have such a possibility at the moment.0. Do nothing. This is as good as it gets.Well, it works just fine as-is, but it would be kind of nice to be able to solve the problem in a less verbose manner (though you're talking about saving only a few lines of code).So, you're suggesting that nothrow be inferred?What I meant is, not everything that is nothrow needs to be annotated as such.2. Relax the nothrow guarantees. After all, nothrow is opt-in.I'm not quite sure what you're suggesting here.Well, it is being done at the library level right now via try-catch.property FracSec fracSec() const nothrow { trusted-nothrow { auto hnsecs = removeUnitsFromHNSecs!"days"(adjTime); if(hnsecs < 0) hnsecs += convert!("hours", "hnsecs")(24); hnsecs = removeUnitsFromHNSecs!"seconds"(hnsecs); return FracSec.from!"hnsecs"(cast(int)hnsecs); } }That can be done at library level.I think that that would require a language change of some kind, because it would require that the compiler recognize that the check can go away in -release, which would effectively mean having the try-catch block be optimized away somehow when compiling with -release. Using scope(failure) for that wouldn't change anything, as it would just mean that the compiler was inserting the try-catch statements for you. It would just be slightly less verbose. Maybe the compiler can be changed to optimize out try-catch blocks if the catch block just contains an assertion? That would at least get rid of the overhead of the idiom, even if it didn't make it less verbose. If we can't get the compiler to recognize the idiom and optimize out the unnecessary code, then if we want it to be optimized out, I think that we'll be forced to add something to the language which the compiler _does_ recognize and therefore can compile out when appropriate - unless someone can come up with some great idea for improving nothrow, but that seems a lot like it would be moving towards not needing trusted anymore, because the compiler would figure that out for you. I don't see how you could get away from having to have the programmer tell the compiler that they're sure that the function which can throw won't throw inside the nothrow function, because that requires human judgement just like trusted does, though it should generally be easier for a programmer to be sure that something won't throw than it is to be sure that it's actually safe. - Jonathan M DavisAgain, one problem here (in addition to the blowup in code size and decay of readability) is we're talking about an internal design validation, not meaningful runtime semantics. There should be a way to remove the baggage.4. ...?We now have std.exception.assumeWontThrow, which works reasonably well when you need to wrap a single call as opposed to several, but it has the same problem as enforce in that it uses lazy, which is definite performance hit. So, in most cases, I'd be more inclined to just use a try-catch, and if it's more than one expression, you pretty much need to use try-catch or scope(failure) instead anyway, since you wouldn't want to wrap whole function bodies in a call to assumeWontThrow (assuming that you even could).
Nov 18 2013
On 2013-11-18 09:14, Andrei Alexandrescu wrote:I'm also concerned about generated code size and overall efficiency. It looks like the assert(0) insertions are there simply to validate the design (they only fail if Phobos has an internal error), so there should be some means to remove them in release builds. We don't have such a possibility at the moment.Asserts are supposed to be removed in release builds. There's also the "debug" statement that can be used. Although it won't remove the try-catch. -- /Jacob Carlborg
Nov 18 2013
On Monday, November 18, 2013 10:09:35 Jacob Carlborg wrote:On 2013-11-18 09:14, Andrei Alexandrescu wrote:The aren't if the condition is statically known to be false, as is the case when you use a try-catch block to catch Exception and then assert within the catch block in order to make the function be able to be nothrow.I'm also concerned about generated code size and overall efficiency. It looks like the assert(0) insertions are there simply to validate the design (they only fail if Phobos has an internal error), so there should be some means to remove them in release builds. We don't have such a possibility at the moment.Asserts are supposed to be removed in release builds.There's also the "debug" statement that can be used. Although it won't remove the try-catch.It also won't run unless you compile with -debug, which I expect very, very few people do. - Jonathan M Davis
Nov 18 2013
On 2013-11-18 10:13, Jonathan M Davis wrote:The aren't if the condition is statically known to be false, as is the case when you use a try-catch block to catch Exception and then assert within the catch block in order to make the function be able to be nothrow.Then it supposed to turn it into a HLT instruction, at least on x86. I don't know how much that affects the code size.It also won't run unless you compile with -debug, which I expect very, very few people do.True. -- /Jacob Carlborg
Nov 18 2013
On 2013-11-18 08:28, Jonathan M Davis wrote:That code's like that just because I like to put empty lines before and after if statements and before return statements, as I think that that improves legibility. Short functions like that suffer as a result, because they end up with a larger proportion of the lines being empty than is normal. I'm always a bit torn on that, because I don't like having quite that many empty lines in so few lines, but I also don't like not having space around if statements and return statements. I never feel like I can find a nice, legible balance with short functions like that.I like the empty newlines as well. Otherwise it feels like reading a text without paragraphs.Fine with me. I might still favor the try-catch in cases where you can clearly wrap it around one function call, because then you avoid problems where you've accidentally effectively marked the whole function as trusted-nothrow when you only want to mark one function call that way. But you could do the same thing with scope(failure) and a new scope. The main problem is when you can't really put the calls that need to be trusted-nothrow inside a new scope, in which case, you're forced to mark the whole function (or at least large portions of it) as trusted-nothrow by wrapping it all in a try-catch or scope(failure).In this case the whole function is wrapped on a try-block. In those cases I think a scope(failure) looks nicer.Just do something like this: void trusted_nothrow (alias block) () { scope(failure) assert(0); block(); } trusted_nothrow!({ // code }); Not as pretty. Yet another prefect example for AST macros :) -- /Jacob Carlborg2. Relax the nothrow guarantees. After all, nothrow is opt-in.I'm not quite sure what you're suggesting here. Make it so that nothrow does checking at runtime instead of compile time? That would be moving in the direction of C++ and throw specifiers (or more precisely, noexcept, I suppose). If that's what you're suggesting, I'd be very much against that. I think that the fact D's nothrow is statically checked is a huge advantage over C++'s noexcept. The fact that you have to sometimes use try-catch blocks (or scope(failure) if that works) to make it work is essentially the same as needing trusted to make some stuff safe. I wouldn't want to throw away trusted in favor of making safe more lax either (though that's almost all static checking which can't be done at runtime, unlike with noexcept). Of course, there's no way to verify trusted-nothrow except at runtime like std.datetime is doing with try-catch and assertions, but most code _can_ be checked statically (including the code that calls the functions that use the try-catch-assert idiom to be able to be nothrow), and that's much more pleasant, particularly because it's actually checked by the compiler that way rather than just blowing up on you at runtime. I suppose that if it were considered annoying enough to have to use try-catch blocks or scope(failure), we could add a nothrow equivalent to trusted to mark functions with, though it's already been argued that trusted should be on pieces of a function rather than on the whole function, and it would arguably be better to mark sections of a function as trusted-nothrow rather than the entire thing. try-catch lets us do that already, but it might be nice to be able to do the equivalent of property FracSec fracSec() const nothrow { trusted-nothrow { auto hnsecs = removeUnitsFromHNSecs!"days"(adjTime); if(hnsecs < 0) hnsecs += convert!("hours", "hnsecs")(24); hnsecs = removeUnitsFromHNSecs!"seconds"(hnsecs); return FracSec.from!"hnsecs"(cast(int)hnsecs); } } and have the compiler insert the catch and assertion for you.
Nov 18 2013
On Monday, November 18, 2013 09:59:44 Jacob Carlborg wrote:Just do something like this: void trusted_nothrow (alias block) () { scope(failure) assert(0); block(); } trusted_nothrow!({ // code }); Not as pretty. Yet another prefect example for AST macros :)I don't see much gain over simply putting scope(failure) at the top. You just avoid having to write the assertion yourself. I also expect that it wouldn't work with return statements. - Jonathan M Davis
Nov 18 2013
On 2013-11-18 10:18, Jonathan M Davis wrote:I don't see much gain over simply putting scope(failure) at the top. You just avoid having to write the assertion yourselfYou suggested it, but by adding it to the language instead.I also expect that it wouldn't work with return statements.No. It would just return from the delegate. -- /Jacob Carlborg
Nov 18 2013
On Monday, November 18, 2013 10:24:11 Jacob Carlborg wrote:On 2013-11-18 10:18, Jonathan M Davis wrote:True, but if the language did it, it could deal with it in a manner which compiled out the try-catch in -release mode as well as avoid the ugliness of wrapping the whole function body in a function call. Still, it would probably be better if we could get the compiler to recognize the idiom and compile out the try-catch in that case rather than adding something like that to the language. But I don't know how reasonable that is. - Jonathan m DavisI don't see much gain over simply putting scope(failure) at the top. You just avoid having to write the assertion yourselfYou suggested it, but by adding it to the language instead.
Nov 18 2013
On 11/18/13 12:59 AM, Jacob Carlborg wrote:On 2013-11-18 08:28, Jonathan M Davis wrote:But three newlines for four lines? That's not paragraphs, it's a grocery list. AndreiThat code's like that just because I like to put empty lines before and after if statements and before return statements, as I think that that improves legibility. Short functions like that suffer as a result, because they end up with a larger proportion of the lines being empty than is normal. I'm always a bit torn on that, because I don't like having quite that many empty lines in so few lines, but I also don't like not having space around if statements and return statements. I never feel like I can find a nice, legible balance with short functions like that.I like the empty newlines as well. Otherwise it feels like reading a text without paragraphs.
Nov 18 2013
On Monday, November 18, 2013 07:56:15 Andrei Alexandrescu wrote:On 11/18/13 12:59 AM, Jacob Carlborg wrote:Yeah. It falls apart on some level when there are so few lines. It's just that it's also ugly to mush them all together. So, to me at least, it seems like it's ugly either way, and in the past, I've just stuck with my normal pattern of having the blank lines when that's the case. Now (in part, thanks to your complaints about it), I'm more likely to mush the lines together when putting the blank lines would result in a bunch of one liners. But I don't really feel like there's a good solution when the function is so short. - Jonathan M DavisI like the empty newlines as well. Otherwise it feels like reading a text without paragraphs.But three newlines for four lines? That's not paragraphs, it's a grocery list.
Nov 18 2013
On 11/18/13 12:30 PM, Jonathan M Davis wrote:On Monday, November 18, 2013 07:56:15 Andrei Alexandrescu wrote:Well you must agree at some level that since you don't like short functions with or without newlines inserted, the set of options is considerably reduced. 1. You search for an editor that offers half lines. 2. You shun short functions altogether. 3. You adjust your style. Style is not something immutable. Paradoxically, when I was younger I used to see it as less of a variable than now. Improving and adapting one's style to various contexts is a lifelong and beneficial process. AndreiOn 11/18/13 12:59 AM, Jacob Carlborg wrote:Yeah. It falls apart on some level when there are so few lines. It's just that it's also ugly to mush them all together. So, to me at least, it seems like it's ugly either way, and in the past, I've just stuck with my normal pattern of having the blank lines when that's the case. Now (in part, thanks to your complaints about it), I'm more likely to mush the lines together when putting the blank lines would result in a bunch of one liners. But I don't really feel like there's a good solution when the function is so short.I like the empty newlines as well. Otherwise it feels like reading a text without paragraphs.But three newlines for four lines? That's not paragraphs, it's a grocery list.
Nov 18 2013
On Monday, November 18, 2013 12:44:43 Andrei Alexandrescu wrote:Well you must agree at some level that since you don't like short functions with or without newlines inserted, the set of options is considerably reduced. 1. You search for an editor that offers half lines. 2. You shun short functions altogether. 3. You adjust your style. Style is not something immutable. Paradoxically, when I was younger I used to see it as less of a variable than now. Improving and adapting one's style to various contexts is a lifelong and beneficial process.I agree. My style has adjusted over time. It's just that I don't like any of the options in this case, so I'm divided on how to format the code. But given how others dislike the empty lines, I now lean more towards going with fewer lines in this case. At least then other folks will like the formatting more even if both ways bug may. But maybe it'll grow on me and bug me less. - Jonathan M Davis
Nov 18 2013
On 2013-11-18 16:56, Andrei Alexandrescu wrote:But three newlines for four lines? That's not paragraphs, it's a grocery list.As Jonathan said, this example is quite bad in the empty lines vs code lines ratio. But I general I do put an empty newline before and after each statement. BTW, I don't have any empty newlines in my grocery list :) . It's more of bullet point list. Or I use an app on my phone which handles all that for me. -- /Jacob Carlborg
Nov 18 2013
On 11/18/13 12:53 PM, Jacob Carlborg wrote:On 2013-11-18 16:56, Andrei Alexandrescu wrote:So that would be two empty lines between statements :o). Anyhow, assuming you only meant one, that would be excessive if done for _all_ statements (i.e. everything ending with a semicolon). It would make code look like a doc printed by someone who didn't know how to turn double spacing off. AndreiBut three newlines for four lines? That's not paragraphs, it's a grocery list.As Jonathan said, this example is quite bad in the empty lines vs code lines ratio. But I general I do put an empty newline before and after each statement.
Nov 18 2013
On 2013-11-18 23:33, Andrei Alexandrescu wrote:So that would be two empty lines between statements :o). Anyhow, assuming you only meant one, that would be excessive if done for _all_ statements (i.e. everything ending with a semicolon). It would make code look like a doc printed by someone who didn't know how to turn double spacing off.I don't agree. And not having any empty newlines in the code would be like reading a text without paragraphs, as I've already said. Instead, take a look at some code I've written and you're free to thinking whatever you like, but I like that style. https://github.com/jacob-carlborg/dstep/blob/master/dstep/translator/Translator.d -- /Jacob Carlborg
Nov 18 2013
On Tuesday, November 19, 2013 08:30:56 Jacob Carlborg wrote:On 2013-11-18 23:33, Andrei Alexandrescu wrote:That looks pretty good overall, though there are probably a few places where I'd favor having fewer newlines. e.g. the body of the foreach in translateFunction is formatted similarly to what I'd do by default, but the overall feel is that it has too much vertical space. So, I might be inclined to remove some of the newlines, much as I'd want them by default. The fact that you have braces on the first version block doesn't help either. But the formatting is okay. While I suspect that Andrei would definitely want to remove many of those newlines if he were formatting the code, what I think Andrei was really objecting to in your comments said that you wanted to put a newline after _every_ statement (which you're not actually doing in your code), and then you wanted separate lines between "paragraphs of code," implying that you'd end up with two newlines separating out sections of your functions. And that would be a lot of extraneous newlines. What you actually seem to have is putting a newline around "paragraphs" but not a newline between every statement, which is a lot more reasonable (though if your "paragraphs" are too small, you run into the exact problem that Andrei was commenting on in my code that started this discussion). By the way, you have a lot of useless breaks in your code. A break isn't actually required if there's a return statement or other control statement such as continue at the end of the case statement. Maybe it's a stylistic thing? But in case you didn't know, they're completely unnecessary, and personally, I'd consider them to be clutter. It's your code though, not mine. - Jonathan M DavisSo that would be two empty lines between statements :o). Anyhow, assuming you only meant one, that would be excessive if done for _all_ statements (i.e. everything ending with a semicolon). It would make code look like a doc printed by someone who didn't know how to turn double spacing off.I don't agree. And not having any empty newlines in the code would be like reading a text without paragraphs, as I've already said. Instead, take a look at some code I've written and you're free to thinking whatever you like, but I like that style. https://github.com/jacob-carlborg/dstep/blob/master/dstep/translator/Transla tor.d
Nov 19 2013
On 2013-11-19 11:32, Jonathan M Davis wrote:That looks pretty good overall, though there are probably a few places where I'd favor having fewer newlines. e.g. the body of the foreach in translateFunction is formatted similarly to what I'd do by default, but the overall feel is that it has too much vertical space. So, I might be inclined to remove some of the newlines, much as I'd want them by default. The fact that you have braces on the first version block doesn't help either. But the formatting is okay.That particular version block is from a pull request and doesn't follow the overall formatting. I wasn't so picky about the formatting in this case.While I suspect that Andrei would definitely want to remove many of those newlines if he were formatting the code, what I think Andrei was really objecting to in your comments said that you wanted to put a newline after _every_ statement (which you're not actually doing in your code), and then you wanted separate lines between "paragraphs of code," implying that you'd end up with two newlines separating out sections of your functions. And that would be a lot of extraneous newlines. What you actually seem to have is putting a newline around "paragraphs" but not a newline between every statement, which is a lot more reasonable (though if your "paragraphs" are too small, you run into the exact problem that Andrei was commenting on in my code that started this discussion).Looking at code in Phobos I would suspect Andrei wanting to remove all empty newlines inside functions. No, I don't want two empty newlines next to each other. I guess I didn't phrase my self very clearly. Your explanation of putting a newline around "paragraphs" seems better.By the way, you have a lot of useless breaks in your code. A break isn't actually required if there's a return statement or other control statement such as continue at the end of the case statement. Maybe it's a stylistic thing? But in case you didn't know, they're completely unnecessary, and personally, I'd consider them to be clutter. It's your code though, not mine.Hmm, I don't remember how I ended up with that. I do know that they're not required. Perhaps it was a stylistic choice. I think it groups things nicely, just as braces do. -- /Jacob Carlborg
Nov 19 2013
On 11/18/13 11:30 PM, Jacob Carlborg wrote:On 2013-11-18 23:33, Andrei Alexandrescu wrote:That doesn't have an empty line after each statement. AndreiSo that would be two empty lines between statements :o). Anyhow, assuming you only meant one, that would be excessive if done for _all_ statements (i.e. everything ending with a semicolon). It would make code look like a doc printed by someone who didn't know how to turn double spacing off.I don't agree. And not having any empty newlines in the code would be like reading a text without paragraphs, as I've already said. Instead, take a look at some code I've written and you're free to thinking whatever you like, but I like that style. https://github.com/jacob-carlborg/dstep/blob/master/dstep/translator/Translator.d
Nov 19 2013
On 2013-11-19 16:01, Andrei Alexandrescu wrote:That doesn't have an empty line after each statement.If an expression or another statement follows, then yes. But not if a closing brace follows. I guess I didn't phrase myself very clearly. -- /Jacob Carlborg
Nov 19 2013
On 11/19/13 7:45 AM, Jacob Carlborg wrote:On 2013-11-19 16:01, Andrei Alexandrescu wrote:You're not operating with the correct definitions of "expression" and "statement". AndreiThat doesn't have an empty line after each statement.If an expression or another statement follows, then yes.
Nov 19 2013
On 2013-11-19 16:56, Andrei Alexandrescu wrote:You're not operating with the correct definitions of "expression" and "statement".I would consider this a statement: https://github.com/jacob-carlborg/dstep/blob/master/dstep/translator/Translator.d#L285..L286 And this an expression: https://github.com/jacob-carlborg/dstep/blob/master/dstep/translator/Translator.d#L288 The first would be an if-statement [1] and the second would be an assign expression [2]. Am I wrong? [1] http://dlang.org/statement.html#IfStatement [2] http://dlang.org/expression.html#AssignExpression -- /Jacob Carlborg
Nov 19 2013
On Tuesday, 19 November 2013 at 16:07:17 UTC, Jacob Carlborg wrote:On 2013-11-19 16:56, Andrei Alexandrescu wrote:http://dlang.org/statement.html#ExpressionStatement DavidYou're not operating with the correct definitions of "expression" and "statement".I would consider this a statement: https://github.com/jacob-carlborg/dstep/blob/master/dstep/translator/Translator.d#L285..L286 And this an expression: https://github.com/jacob-carlborg/dstep/blob/master/dstep/translator/Translator.d#L288
Nov 19 2013
On 2013-11-19 18:59, David Nadlinger wrote:http://dlang.org/statement.html#ExpressionStatementOk, I didn't know that. -- /Jacob Carlborg
Nov 19 2013
On 11/19/13 8:07 AM, Jacob Carlborg wrote:On 2013-11-19 16:56, Andrei Alexandrescu wrote:Sure is.You're not operating with the correct definitions of "expression" and "statement".I would consider this a statement: https://github.com/jacob-carlborg/dstep/blob/master/dstep/translator/Translator.d#L285..L286And this an expression: https://github.com/jacob-carlborg/dstep/blob/master/dstep/translator/Translator.d#L288That's not an expression, it's a statement - more precisely an expression statement. (The semicolon makes it so.) By the rules you incorrectly stated, there should be an empty line after it. I'll allow myself a piece of advice - the density of e.g. https://github.com/jacob-carlborg/dstep/blob/master/dstep/translat r/Translator.d#L251 is low enough to make it career limiting. You'd do good to change your style. Andrei
Nov 19 2013
On Tuesday, 19 November 2013 at 18:29:57 UTC, Andrei Alexandrescu wrote:I'll allow myself a piece of advice - the density of e.g. https://github.com/jacob-carlborg/dstep/blob/master/dstep/translat r/Translator.d#L251 is low enough to make it career limiting.Doesn't look *that* sparse to me... I think the 8-space indentation give a false perspective. E.g. this is actually 1 line longer, but looks denser: package string translateFunction (string result, string name, Parameter[] parameters, bool variadic, String context) { context ~= result ~ ' ' ~ name ~ " ("; string[] params; params.reserve(parameters.length); foreach (param ; parameters) { string p; version(D1) { p ~= param.type; } else { if (param.isConst) { p ~= "const(" ~ param.type; ~ ')'; } else { p ~= param.type; } } if (param.name.any) { p ~= " " ~ translateIdentifier(param.name); } params ~= p; } if (variadic) { params ~= "..."; } context ~= params.join(", ") ~ ')'; return context.data; }
Nov 19 2013
On 2013-11-19 20:03, John Colvin wrote:Doesn't look *that* sparse to me... I think the 8-space indentation give a false perspective. E.g. this is actually 1 line longer, but looks denser:I'm using one tab as indentation. It's Github formatting it like that. -- /Jacob Carlborg
Nov 19 2013
On 11/19/2013 12:19 PM, Jacob Carlborg wrote:On 2013-11-19 20:03, John Colvin wrote:I've abandoned using tabs as every text rendering engine interprets them differently.Doesn't look *that* sparse to me... I think the 8-space indentation give a false perspective. E.g. this is actually 1 line longer, but looks denser:I'm using one tab as indentation. It's Github formatting it like that.
Nov 19 2013
Am Tue, 19 Nov 2013 12:27:43 -0800 schrieb Walter Bright <newshound2 digitalmars.com>:I've abandoned using tabs as every text rendering engine interprets them differently.Yes, that's one of the selling points of using tabs, as they all have a tab width setting you can customize. :)
Nov 20 2013
On 2013-11-19 19:29, Andrei Alexandrescu wrote:That's not an expression, it's a statement - more precisely an expression statement. (The semicolon makes it so.) By the rules you incorrectly stated, there should be an empty line after it.I'm sorry for not know every minor detail of the language.I'll allow myself a piece of advice - the density of e.g. https://github.com/jacob-carlborg/dstep/blob/master/dstep/translator/Translator.d#L251 is low enough to make it career limiting. You'd do good to change your style.Are you serious? Anyone caring about that doesn't know what he/she is doing. -- /Jacob Carlborg
Nov 19 2013
On 11/19/13 12:17 PM, Jacob Carlborg wrote:On 2013-11-19 19:29, Andrei Alexandrescu wrote:It's not a minor detail. Besides, people took time to explain this to you, and the right response is to integrate that information, not to demean it.That's not an expression, it's a statement - more precisely an expression statement. (The semicolon makes it so.) By the rules you incorrectly stated, there should be an empty line after it.I'm sorry for not know every minor detail of the language.That's exactly my point. The matter of fact is, in a setting where people are paid to write code, this kind of minor issue would be settled around the first week since hiring. At Facebook for example you'd be submitting a phabricator diff (loosely equivalent to a github pull request) and our linter will point out we use two spaces for indentation instead of tabs, and 80 columns. Then a couple of peers would point out that code is about twice as sparse vertically than it should. You'd fix these issues for good and that would be that. This has happened quite a few times. If, on the other hand, you chose to make a big deal out of it, that would be a cultural mismatch that to my knowledge would be unprecedented. AndreiI'll allow myself a piece of advice - the density of e.g. https://github.com/jacob-carlborg/dstep/blob/master/dstep/translator/Translator.d#L251 is low enough to make it career limiting. You'd do good to change your style.Are you serious? Anyone caring about that doesn't know what he/she is doing.
Nov 19 2013
On Tuesday, 19 November 2013 at 21:21:30 UTC, Andrei Alexandrescu wrote:On 11/19/13 12:17 PM, Jacob Carlborg wrote:Man, I thought these debates died out long ago. All that matters in style is readability and consistency. Looking at that code linked above:: 1. Is the code formatting consistent? Yes 2. Is the code formatting consistent within the repo? Yes 3. Is the code formatting easily read by a programmer? Yes Whether you, I, or anyone else like with the style is irrelevant. Personally I'm not a fan, but that doesn't make the author a bad programmer, nor does it make me right. It simply means: a) Jacob and I prefer different formatting styles and b) I need to adapt if I'm to work on dstep. Now if dstep was written at Facebook then OK, the code would need reformatting because it violates point 2. above. As for Phobos code, insist on tighter style guidelines. No one will care as long as it is consistent and readable. Any programmer worth anything will just adhere to it happily. Those that don't want to are not going to have much to contribute, they're more worried about their personal style than advancing Phobos and D with great code. Cheers.On 2013-11-19 19:29, Andrei Alexandrescu wrote:It's not a minor detail. Besides, people took time to explain this to you, and the right response is to integrate that information, not to demean it.That's not an expression, it's a statement - more precisely an expression statement. (The semicolon makes it so.) By the rules you incorrectly stated, there should be an empty line after it.I'm sorry for not know every minor detail of the language.That's exactly my point. The matter of fact is, in a setting where people are paid to write code, this kind of minor issue would be settled around the first week since hiring. At Facebook for example you'd be submitting a phabricator diff (loosely equivalent to a github pull request) and our linter will point out we use two spaces for indentation instead of tabs, and 80 columns. Then a couple of peers would point out that code is about twice as sparse vertically than it should. You'd fix these issues for good and that would be that. This has happened quite a few times. If, on the other hand, you chose to make a big deal out of it, that would be a cultural mismatch that to my knowledge would be unprecedented. AndreiI'll allow myself a piece of advice - the density of e.g. https://github.com/jacob-carlborg/dstep/blob/master/dstep/translator/Translator.d#L251 is low enough to make it career limiting. You'd do good to change your style.Are you serious? Anyone caring about that doesn't know what he/she is doing.
Nov 19 2013
On 2013-11-20 02:26, growler wrote:Man, I thought these debates died out long ago. All that matters in style is readability and consistency. Looking at that code linked above:: 1. Is the code formatting consistent? Yes 2. Is the code formatting consistent within the repo? Yes 3. Is the code formatting easily read by a programmer? Yes Whether you, I, or anyone else like with the style is irrelevant. Personally I'm not a fan, but that doesn't make the author a bad programmer, nor does it make me right. It simply means: a) Jacob and I prefer different formatting styles and b) I need to adapt if I'm to work on dstep. Now if dstep was written at Facebook then OK, the code would need reformatting because it violates point 2. above. As for Phobos code, insist on tighter style guidelines. No one will care as long as it is consistent and readable. Any programmer worth anything will just adhere to it happily. Those that don't want to are not going to have much to contribute, they're more worried about their personal style than advancing Phobos and D with great code. Cheers.Thank you. -- /Jacob Carlborg
Nov 19 2013
On Wednesday, 20 November 2013 at 01:26:35 UTC, growler wrote:... Cheers.Very true. I sometimes switch between 3 to 4 different code styles within a single day when actively working on some D projects (internal one at work, Phobos style, vibe.d guidelines + one preferred style for random chosen project of the day). Pretty much only issue it causes is necessity to switch vim tab/space profiles ;) I fully expect any reasonable programmer to be able to conform any target code style just by a random call. There are no really good or bad styles, only consistent and inconsistent ones.
Nov 20 2013
On 11/20/13 5:00 AM, Dicebot wrote:On Wednesday, 20 November 2013 at 01:26:35 UTC, growler wrote:Oh there are consistent styles that are worse than other consistent styles of course. Another dimension is there are also styles that are more or less frequent. Andrei... Cheers.Very true. I sometimes switch between 3 to 4 different code styles within a single day when actively working on some D projects (internal one at work, Phobos style, vibe.d guidelines + one preferred style for random chosen project of the day). Pretty much only issue it causes is necessity to switch vim tab/space profiles ;) I fully expect any reasonable programmer to be able to conform any target code style just by a random call. There are no really good or bad styles, only consistent and inconsistent ones.
Nov 20 2013
On 11/20/13 1:14 PM, Andrei Alexandrescu wrote:On 11/20/13 5:00 AM, Dicebot wrote:Of course: the ones you don't like.On Wednesday, 20 November 2013 at 01:26:35 UTC, growler wrote:Oh there are consistent styles that are worse than other consistent styles of course.... Cheers.Very true. I sometimes switch between 3 to 4 different code styles within a single day when actively working on some D projects (internal one at work, Phobos style, vibe.d guidelines + one preferred style for random chosen project of the day). Pretty much only issue it causes is necessity to switch vim tab/space profiles ;) I fully expect any reasonable programmer to be able to conform any target code style just by a random call. There are no really good or bad styles, only consistent and inconsistent ones.
Nov 20 2013
On 11/20/13 9:43 AM, Ary Borenszweig wrote:On 11/20/13 1:14 PM, Andrei Alexandrescu wrote:What I meant is there are consistent styles that are objectively worse. Consistency is necessary but not sufficient. AndreiOn 11/20/13 5:00 AM, Dicebot wrote:Of course: the ones you don't like.On Wednesday, 20 November 2013 at 01:26:35 UTC, growler wrote:Oh there are consistent styles that are worse than other consistent styles of course.... Cheers.Very true. I sometimes switch between 3 to 4 different code styles within a single day when actively working on some D projects (internal one at work, Phobos style, vibe.d guidelines + one preferred style for random chosen project of the day). Pretty much only issue it causes is necessity to switch vim tab/space profiles ;) I fully expect any reasonable programmer to be able to conform any target code style just by a random call. There are no really good or bad styles, only consistent and inconsistent ones.
Nov 20 2013
On Wednesday, 20 November 2013 at 20:06:04 UTC, Andrei Alexandrescu wrote:What I meant is there are consistent styles that are objectively worse. Consistency is necessary but not sufficient.And what I meant is this opinion of yours is wrong. Any consistent style that is liked by at least one programmer that uses it in practice is no worse than any other possible consistent style. There is nothing objective about it, pretty much as there are not that much common about human perception.
Nov 20 2013
On 20.11.2013 21:17, Dicebot wrote:On Wednesday, 20 November 2013 at 20:06:04 UTC, Andrei Alexandrescu wrote:I think you misunderstand Andrei here. There are styles that are consistent that are not liked by *any* programmers. These styles are of course not in use. A style that says you should use seventeen blank lines between functions, and each blank line should have 3 tabs and 2 spaces, alternating, may be consistent, but it's also ugly, and I daresay that is objective (as in, nobody would disagree, I don't believe in perfect objectivity). A less constructed example may be where a non-programmer manager has seen that he cannot understand what his programmers are writing, and decides to formulate some coding style to make it easier for him, but which severely hamstrings actual programmers. (though I guess in this case, one could argue it's subjectively better for him...) -- SimenWhat I meant is there are consistent styles that are objectively worse. Consistency is necessary but not sufficient.And what I meant is this opinion of yours is wrong. Any consistent style that is liked by at least one programmer that uses it in practice is no worse than any other possible consistent style. There is nothing objective about it, pretty much as there are not that much common about human perception.
Nov 20 2013
On 11/20/13 12:17 PM, Dicebot wrote:On Wednesday, 20 November 2013 at 20:06:04 UTC, Andrei Alexandrescu wrote:There are a bunch of styles that are consistent yet used by nobody, because they are objectively worse. So consistency is not the sole requirement. Then you hadn't mentioned "that is liked by at least one programmer" which moves the goalposts. Then one programmer can be one highly unusual human being, so there is strength in numbers. Generally I tend to double-check with all-inclusive characterization "everybody's doing great" and "agree to disagree" and such. Taken to the extreme such a stance does little else than to promote undue relativism. So whenever someone asks me to agree to disagree I go back to first principles and look for rationale that makes one of the points objectively refutable. AndreiWhat I meant is there are consistent styles that are objectively worse. Consistency is necessary but not sufficient.And what I meant is this opinion of yours is wrong. Any consistent style that is liked by at least one programmer that uses it in practice is no worse than any other possible consistent style.
Nov 20 2013
On Wednesday, 20 November 2013 at 20:47:10 UTC, Andrei Alexandrescu wrote: [snip]There are a bunch of styles that are consistent yet used by nobody, because they are objectively worse. So consistency is not the sole requirement.[snip] This is true, one can always be consistently illegible :D http://www.ioccc.org/1986/bright.c For me personally I always find switching to FORTRAN very jarring. It is interesting because FORTRAN itself is very readable, but the style often used and ALLCAPS everywhere make it hard to read at first. However, on the whole I've found consistency often leads to legibility. Cheers.
Nov 20 2013
On Wednesday, 20 November 2013 at 20:47:10 UTC, Andrei Alexandrescu wrote:There are a bunch of styles that are consistent yet used by nobody, because they are objectively worse. So consistency is not the sole requirement. Then you hadn't mentioned "that is liked by at least one programmer" which moves the goalposts. Then one programmer can be one highly unusual human being, so there is strength in numbers.You see, the whole definition of term "objective comparison" implies comparison by traits other than actual usage. The very moment when you refer to something being worse because no one (or no one in sane mind) uses it, comparison moves into area of subjectivity. Not your specific subjectivity but subjectivity as a general evaluation approach. I am not arguing about the fact that some code styles are widely recognized as better ones. I oppose using the term "objectivity" in that context, consider it etymological nitpicking.
Nov 20 2013
On 11/20/13 3:45 PM, Dicebot wrote:On Wednesday, 20 November 2013 at 20:47:10 UTC, Andrei Alexandrescu wrote:No, because the quantity being measured is "worse". AndreiThere are a bunch of styles that are consistent yet used by nobody, because they are objectively worse. So consistency is not the sole requirement. Then you hadn't mentioned "that is liked by at least one programmer" which moves the goalposts. Then one programmer can be one highly unusual human being, so there is strength in numbers.You see, the whole definition of term "objective comparison" implies comparison by traits other than actual usage.
Nov 20 2013
On Wednesday, 20 November 2013 at 23:57:40 UTC, Andrei Alexandrescu wrote:No, because the quantity being measured is "worse". AndreiIf it would have been an inherent well-defined trait of an object itself, it would have been objective comparison. As this quantity is defined purely (and no real measurable traits other than consistency were proposed) by observing subject, it can't be an objective measurement and this objective comparison. Objective comparison is something like comparing average amount of extra symbols per lexer token between two code styles.
Nov 20 2013
On 11/20/13 4:03 PM, Dicebot wrote:On Wednesday, 20 November 2013 at 23:57:40 UTC, Andrei Alexandrescu wrote:In this context "objectively worse" means "all reasonable people would say it's worse". AndreiNo, because the quantity being measured is "worse". AndreiIf it would have been an inherent well-defined trait of an object itself, it would have been objective comparison. As this quantity is defined purely (and no real measurable traits other than consistency were proposed) by observing subject, it can't be an objective measurement and this objective comparison.
Nov 20 2013
On Thursday, 21 November 2013 at 01:45:46 UTC, Andrei Alexandrescu wrote:In this context "objectively worse" means "all reasonable people would say it's worse". AndreiWhich is exactly hidden changing of word meaning to push own opinion which disgusts me. http://www.oxforddictionaries.com/definition/english/objective "not influenced by personal feelings or opinions in considering and representing facts; not dependent on the mind for existence; actual:" "opinion" pretty much annihilates "objectivity" when used in same sentence. If statistical opinion sounds "objective" enough for you, I may propose to start changing laws of Physics by voting. Why would it object if all reasonable people say so?
Nov 20 2013
On 11/20/13 6:02 PM, Dicebot wrote:On Thursday, 21 November 2013 at 01:45:46 UTC, Andrei Alexandrescu wrote:No reason to escalate.In this context "objectively worse" means "all reasonable people would say it's worse". AndreiWhich is exactly hidden changing of word meaning to push own opinion which disgusts me.http://www.oxforddictionaries.com/definition/english/objective "not influenced by personal feelings or opinions in considering and representing facts; not dependent on the mind for existence; actual:"Yah, and it's a fact that certain coding styles would not be used in spite of them being consistent."opinion" pretty much annihilates "objectivity" when used in same sentence.It does, but this is not about opinions.If statistical opinion sounds "objective" enough for you, I may propose to start changing laws of Physics by voting. Why would it object if all reasonable people say so?... and the strawman is also uncalled for. Andrei
Nov 20 2013
On Thursday, 21 November 2013 at 02:02:08 UTC, Dicebot wrote:On Thursday, 21 November 2013 at 01:45:46 UTC, Andrei Alexandrescu wrote:A sample of code from my very first job (needless to say that wasn't software company): if (...) statements1; else statements2; yes, not indentation and it was used pretty consistently, and I'm not kidding! To make things worse, in the code fragment I was debugging `statements1` exceeded screen width, so I have not seen `else`. It was on my very first day, and after discovering this nightmare I spent all the rest of the day searching for code pretty-printer. At that moment I didn't even know how these class of programs called or if they exist at all, but debugging code written like that would be unbearable. Now, Michael, please, go and show me *a single* experienced programmer who wouldn't call this coding style a disaster. DISASTER. I think I understand your stance - adherence to scientific method etc and that's great, but there is a limit to it. (Once again, I'm not exaggerating at all. There were other "nice" things there, like behemoth method 8k lines long, code duplication all over the place, sometimes tens times)In this context "objectively worse" means "all reasonable people would say it's worse". Andrei
Nov 20 2013
On 11/20/2013 5:45 PM, Andrei Alexandrescu wrote:In this context "objectively worse" means "all reasonable people would say it's worse".It could also be objectively measured as: 1. results in more bugs 2. takes people longer to get up to speed with code written that way
Nov 20 2013
On Thursday, 21 November 2013 at 03:15:16 UTC, Walter Bright wrote:On 11/20/2013 5:45 PM, Andrei Alexandrescu wrote:You need to prove first that it results in more bugs because of inherent code style traits, not preferences of target audience. Otherwise you can have a potential code style which is objectively good for one measured group of programmers and objectively bad for other. Which contradicts the definition of "objectivity". There is nothing terrible about being only subjectively worse / better. Code style exists only to help people and it is completely natural that its applicability is evaluated in people context. But that is a different thing with different name. Same with your plane example - as described, one case wasn't objectively worse than another (or probably was but it wasn't checked). But as it is all about pilots (who are very alive subjects), subjective comparison is important enough to make the change and resulted in real practical improvement.In this context "objectively worse" means "all reasonable people would say it's worse".It could also be objectively measured as: 1. results in more bugs 2. takes people longer to get up to speed with code written that way
Nov 21 2013
On 11/21/2013 6:10 AM, Dicebot wrote:You need to prove first that it results in more bugs because of inherent code style traits, not preferences of target audience.You're saying that human factors engineering is purely a matter of personal preference. IT IS NOT. The way the mind works is not random.Same with your plane example - as described, one case wasn't objectively worse than another (or probably was but it wasn't checked).Yes it was checked. People crashed. It was objectively worse. It's not a matter of preference.But as it is all about pilots (who are very alive subjects), subjective comparison is important enough to make the change and resulted in real practical improvement.A real practical improvement is quite objective and measurable - fewer crashes. It's not happenstance that airline travel is far, far safer now. An awful lot of crashes were caused by pilot confusion - and every one is analyzed and designs are changed and tested to eliminate that confusion. The objective results are obvious.
Nov 21 2013
On Wednesday, 20 November 2013 at 23:57:40 UTC, Andrei Alexandrescu wrote:On 11/20/13 3:45 PM, Dicebot wrote:Ho please, we aren't in a schoolyard.On Wednesday, 20 November 2013 at 20:47:10 UTC, Andrei Alexandrescu wrote:No, because the quantity being measured is "worse". AndreiThere are a bunch of styles that are consistent yet used by nobody, because they are objectively worse. So consistency is not the sole requirement. Then you hadn't mentioned "that is liked by at least one programmer" which moves the goalposts. Then one programmer can be one highly unusual human being, so there is strength in numbers.You see, the whole definition of term "objective comparison" implies comparison by traits other than actual usage.
Nov 20 2013
On 11/20/2013 3:45 PM, Dicebot wrote:I oppose using the term "objectivity" in that context, consider it etymological nitpicking.To reiterate my analogy, some cockpit control designs caused more crashes. This is objective fact. It isn't a matter of the pilots "liking" or "disliking" one or the other.
Nov 20 2013
On 11/20/2013 12:17 PM, Dicebot wrote:On Wednesday, 20 November 2013 at 20:06:04 UTC, Andrei Alexandrescu wrote:I'm going to very strongly disagree with this. And I'll use a hoary analogy - airplane cockpit design. It doesn't matter how consistent it is - there are an awful lot of cockpit design features that have caused crashes, and the fix was changing the feature. These misfeatures were not technically faulty nor were they inconsistent. But they were still bad. I'll give one example. Warning horns for things that went wrong were good features. So good, in fact, that designers added more and more horns. Each horn had a different sound signature. The horns were consistent, and worked faultlessly. Unfortunately, the plethora of horns caused pilots to get confused under stress, and they'd react to solve the wrong problem. The solution was to replace the horn's siren with a voice saying what was wrong, such as "fire engine no. 2" and "pull up". This worked BETTER. I see no difference with coding styles. Some are better, some are worse. As Andrei stated, consistency is not sufficient. For example, no indenting at all. Consistent? Yes. Bad? Yes. Case closed!What I meant is there are consistent styles that are objectively worse. Consistency is necessary but not sufficient.And what I meant is this opinion of yours is wrong. Any consistent style that is liked by at least one programmer that uses it in practice is no worse than any other possible consistent style. There is nothing objective about it, pretty much as there are not that much common about human perception.
Nov 20 2013
On Thursday, 21 November 2013 at 03:10:17 UTC, Walter Bright wrote:... The solution was to replace the horn's siren with a voice saying what was wrong, such as "fire engine no. 2" and "pull up". This worked BETTER. I see no difference with coding styles. Some are better, some are worse. As Andrei stated, consistency is not sufficient. For example, no indenting at all. Consistent? Yes. Bad? Yes. Case closed!I strongly agree with Walter on this one.
Nov 21 2013
On Wednesday, November 20, 2013 12:06:04 Andrei Alexandrescu wrote:What I meant is there are consistent styles that are objectively worse. Consistency is necessary but not sufficient.I agree, but you do come across sometimes as seeming to think that some styles are objectively worse when plenty of other folks would disagree with you (e.g. with regards to how much vertical whitespace is reasonable). I think that in most cases, style ends up being very subjective and not objective at all. IIRC, Walter linked to a blog post a few months back about someone talking about what good coding style looked like, and they were talking about the code for Doom 3 and how beautiful it was, and yet when they showed what it looked like, many people thought that the coding style was absolutely horrible. I think that it's quite clear that what people consider to look good in terms of coding style varies quite a bit. So, while some cases are clearly objectively bad (e.g. all of the code on one line), I think that you're going to have a hard time arguing it objectively in most cases - especially when it comes to legibility, since different people find different styles to be more legible, and what's more legible often comes down to what you're used to seeing rather than anything particularly objective. - Jonathan M Davis
Nov 20 2013
On 11/20/13 12:23 PM, Jonathan M Davis wrote:On Wednesday, November 20, 2013 12:06:04 Andrei Alexandrescu wrote:That I agree with. AndreiWhat I meant is there are consistent styles that are objectively worse. Consistency is necessary but not sufficient.I agree, but you do come across sometimes as seeming to think that some styles are objectively worse when plenty of other folks would disagree with you (e.g. with regards to how much vertical whitespace is reasonable). I think that in most cases, style ends up being very subjective and not objective at all.
Nov 20 2013
On 11/20/2013 12:23 PM, Jonathan M Davis wrote:So, while some cases are clearly objectively bad (e.g. all of the code on one line), I think that you're going to have a hard time arguing it objectively in most cases - especially when it comes to legibility, since different people find different styles to be more legible, and what's more legible often comes down to what you're used to seeing rather than anything particularly objective.Legibility can be objective, too. For example, it is a fact that many fonts do not distinguish O and 0, l and 1. It is an objective fact that people have trouble distinguishing them. The JSF C++ coding standard, for example, prohibits using an "l" suffix on integral literals for that reason. It's also an objective fact that people have trouble distinguishing: verylongisanidentifier from: verulongsianidentifier It's not just tomayto-tomahto. Human factors research shows that some designs are objectively better than others.
Nov 20 2013
On Wednesday, November 20, 2013 19:22:10 Walter Bright wrote:On 11/20/2013 12:23 PM, Jonathan M Davis wrote:Definitely, but almost all arguments over coding style seem to be very subjective even when some people try and claim that some of the issues are objective. Most style choices which are objectively bad don't even ever get made precisely because they're objectively bad. There are of course exceptions, but I've rarely seen style arguments that are actually objective, and it's not uncommon to have people with drastically different opinions as to what looks good and who think that it should be obvious to everyone that what they think looks good looks good and that the other style looks horrible. And I've run into plenty of cases where one developer thinks that a particular coding style is much easier to read, which is in complete contrast with what another developer thought was legible (how many parens to use and where being a prime example of that). So, it at least almost always seems like what's considered be a good, legible style is very subjective. - Jonathan M DavisSo, while some cases are clearly objectively bad (e.g. all of the code on one line), I think that you're going to have a hard time arguing it objectively in most cases - especially when it comes to legibility, since different people find different styles to be more legible, and what's more legible often comes down to what you're used to seeing rather than anything particularly objective.Legibility can be objective, too. For example, it is a fact that many fonts do not distinguish O and 0, l and 1. It is an objective fact that people have trouble distinguishing them. The JSF C++ coding standard, for example, prohibits using an "l" suffix on integral literals for that reason. It's also an objective fact that people have trouble distinguishing: verylongisanidentifier from: verulongsianidentifier It's not just tomayto-tomahto. Human factors research shows that some designs are objectively better than others.
Nov 20 2013
On 11/20/2013 10:35 PM, Jonathan M Davis wrote:Definitely, but almost all arguments over coding style seem to be very subjective even when some people try and claim that some of the issues are objective.I've run across actual research on aspects of this now and then, and real measurable statistics show one way is better than another (better meaning produces fewer perceptual mistakes). It's not dissimilar to A/B testing often done on web shopping pages.Most style choices which are objectively bad don't even ever get made precisely because they're objectively bad. There are of course exceptions, but I've rarely seen style arguments that are actually objective, and it's not uncommon to have people with drastically different opinions as to what looks good and who think that it should be obvious to everyone that what they think looks good looks good and that the other style looks horrible.Just because A/B testing isn't done doesn't mean it's subjective.And I've run into plenty of cases where one developer thinks that a particular coding style is much easier to read, which is in complete contrast with what another developer thought was legible (how many parens to use and where being a prime example of that). So, it at least almost always seems like what's considered be a good, legible style is very subjective.I bet if testing was done on it, a clear difference would emerge. Just because two developers argue about it doesn't make it subjective. For example, we can both argue about where a program is expending its time, and we can both be wrong if we actually profile it and see. The way humans perceive information is not random. Human factors engineering is not junk science. There's a reason lower case letters exist, it is not merely personal preference. It's objectively easier to read.
Nov 21 2013
On Thursday, 21 November 2013 at 06:35:35 UTC, Jonathan M Davis wrote:Definitely, but almost all arguments over coding style seem to be very subjective even when some people try and claim that some of the issues are objective. Most style choices which are objectively bad don't even ever get made precisely because they're objectively bad. There are of course exceptions, but I've rarely seen style arguments that are actually objective, and it's not uncommon to have people with drastically different opinions as to what looks good and who think that it should be obvious to everyone that what they think looks good looks good and that the other style looks horrible. And I've run into plenty of cases where one developer thinks that a particular coding style is much easier to read, which is in complete contrast with what another developer thought was legible (how many parens to use and where being a prime example of that). So, it at least almost always seems like what's considered be a good, legible style is very subjective. - Jonathan M DavisIt is because it is really context sensitive. Let me take a practical example. function foo() { return { foo: bar } } What does this JS sample returns ? Answer is : undefined (undefined is a thing in JS, not talking about undefined behavior). It is undefined because an semicolon is implicitly inserted after return. It can take place in much more subtle forms. How does it affect my D style ? It is quite simple, I integrated in JS that I should never ever ever put the brace on the next line. That is the worse idea ever. My work involve to do some JS, so I want to reduce as much as possible the cost of context switching and adopt a style that is consistent. That is why I hate putting brace on the next line. Indeed, in most language you'll find no objective reason why it is good or bad. But in JS you have an objective good reason to do it that way. And, as it reduce the cost of context switching, it make sense to do it in other languages (JS is not going to disappear soon). Obviously, a dev that never do any JS will find this futile. Because he/she do not have to pay the same cost.
Nov 22 2013
On 11/19/2013 04:21 PM, Andrei Alexandrescu wrote:On 11/19/13 12:17 PM, Jacob Carlborg wrote:This is great stuff for every developer to learn, but can someone please create (or start creating) a code formatting tool for D? I guess it's even worth putting some bounties on it, if that helps.On 2013-11-19 19:29, Andrei Alexandrescu wrote:It's not a minor detail. Besides, people took time to explain this to you, and the right response is to integrate that information, not to demean it.That's not an expression, it's a statement - more precisely an expression statement. (The semicolon makes it so.) By the rules you incorrectly stated, there should be an empty line after it.I'm sorry for not know every minor detail of the language.That's exactly my point. The matter of fact is, in a setting where people are paid to write code, this kind of minor issue would be settled around the first week since hiring. At Facebook for example you'd be submitting a phabricator diff (loosely equivalent to a github pull request) and our linter will point out we use two spaces for indentation instead of tabs, and 80 columns. Then a couple of peers would point out that code is about twice as sparse vertically than it should. You'd fix these issues for good and that would be that. This has happened quite a few times. If, on the other hand, you chose to make a big deal out of it, that would be a cultural mismatch that to my knowledge would be unprecedented. AndreiI'll allow myself a piece of advice - the density of e.g. https://github.com/jacob-carlborg/dstep/blob/master/dstep/translator/Translator.d#L251 is low enough to make it career limiting. You'd do good to change your style.Are you serious? Anyone caring about that doesn't know what he/she is doing.
Nov 19 2013
On Wednesday, 20 November 2013 at 01:30:18 UTC, John J wrote:On 11/19/2013 04:21 PM, Andrei Alexandrescu wrote:I find astyle works very well for D. http://astyle.sourceforge.net/On 11/19/13 12:17 PM, Jacob Carlborg wrote:This is great stuff for every developer to learn, but can someone please create (or start creating) a code formatting tool for D? I guess it's even worth putting some bounties on it, if that helps.On 2013-11-19 19:29, Andrei Alexandrescu wrote:It's not a minor detail. Besides, people took time to explain this to you, and the right response is to integrate that information, not to demean it.That's not an expression, it's a statement - more precisely an expression statement. (The semicolon makes it so.) By the rules you incorrectly stated, there should be an empty line after it.I'm sorry for not know every minor detail of the language.That's exactly my point. The matter of fact is, in a setting where people are paid to write code, this kind of minor issue would be settled around the first week since hiring. At Facebook for example you'd be submitting a phabricator diff (loosely equivalent to a github pull request) and our linter will point out we use two spaces for indentation instead of tabs, and 80 columns. Then a couple of peers would point out that code is about twice as sparse vertically than it should. You'd fix these issues for good and that would be that. This has happened quite a few times. If, on the other hand, you chose to make a big deal out of it, that would be a cultural mismatch that to my knowledge would be unprecedented. AndreiI'll allow myself a piece of advice - the density of e.g. https://github.com/jacob-carlborg/dstep/blob/master/dstep/translator/Translator.d#L251 is low enough to make it career limiting. You'd do good to change your style.Are you serious? Anyone caring about that doesn't know what he/she is doing.
Nov 19 2013
On Tue, Nov 19, 2013 at 08:30:18PM -0500, John J wrote:On 11/19/2013 04:21 PM, Andrei Alexandrescu wrote:[...][...]That's exactly my point. The matter of fact is, in a setting where people are paid to write code, this kind of minor issue would be settled around the first week since hiring. At Facebook for example you'd be submitting a phabricator diff (loosely equivalent to a github pull request) and our linter will point out we use two spaces for indentation instead of tabs, and 80 columns. Then a couple of peers would point out that code is about twice as sparse vertically than it should. You'd fix these issues for good and that would be that. This has happened quite a few times. If, on the other hand, you chose to make a big deal out of it, that would be a cultural mismatch that to my knowledge would be unprecedented.This is great stuff for every developer to learn, but can someone please create (or start creating) a code formatting tool for D? I guess it's even worth putting some bounties on it, if that helps.The main holdup is the lack of a ready-made, official D lexer/parser. Nobody wants to manually maintain their own version of the D lexer/parser given how fast the language is still developing (plus, it sucks to have to keep fixing lexing/parsing bugs just because DMD's lexer/parser doesn't quite work the way you thought it would). Once we have this in place, I'm quite confident that a lot of nice D tools would spring up. Pretty-printing is, after all, one of the simplest uses of lexers/parsers. T -- One reason that few people are aware there are programs running the internet is that they never crash in any significant way: the free software underlying the internet is reliable to the point of invisibility. -- Glyn Moody, from the article "Giving it all away"
Nov 19 2013
On 11/19/13 10:39 PM, H. S. Teoh wrote:On Tue, Nov 19, 2013 at 08:30:18PM -0500, John J wrote:One day you should all stop doing what you are doing and focus on bootstrapping the compiler. Then you will have a lexer/parser that's always consistent with the language. And you will have a very big program that will make it harder to introduce bugs in next versions. And it will show you where you need improvements to the language. It's really sad that D is a system's programming language but its compiler is written in another language. The strategy I would use is: 1. Copy the code as-is from C++ to D. 2. Make the smallest changes to make it work, even if it doesn't look like D code. At this point, you will have a working compiler in D and a lexer/parser as libraries, that are always in-sync with the language. Then: 3. Refactor the code to make it more D-ish. Remember: the more you wait for this to happen, the harder it will be. Just... stop, take some time, and do it.On 11/19/2013 04:21 PM, Andrei Alexandrescu wrote:[...][...]That's exactly my point. The matter of fact is, in a setting where people are paid to write code, this kind of minor issue would be settled around the first week since hiring. At Facebook for example you'd be submitting a phabricator diff (loosely equivalent to a github pull request) and our linter will point out we use two spaces for indentation instead of tabs, and 80 columns. Then a couple of peers would point out that code is about twice as sparse vertically than it should. You'd fix these issues for good and that would be that. This has happened quite a few times. If, on the other hand, you chose to make a big deal out of it, that would be a cultural mismatch that to my knowledge would be unprecedented.This is great stuff for every developer to learn, but can someone please create (or start creating) a code formatting tool for D? I guess it's even worth putting some bounties on it, if that helps.The main holdup is the lack of a ready-made, official D lexer/parser. Nobody wants to manually maintain their own version of the D lexer/parser given how fast the language is still developing (plus, it sucks to have to keep fixing lexing/parsing bugs just because DMD's lexer/parser doesn't quite work the way you thought it would). Once we have this in place, I'm quite confident that a lot of nice D tools would spring up. Pretty-printing is, after all, one of the simplest uses of lexers/parsers.
Nov 20 2013
On Wednesday, 20 November 2013 at 14:15:17 UTC, Ary Borenszweig wrote:One day you should all stop doing what you are doing and focus on bootstrapping the compiler.You'll be surprised - https://github.com/yebblies/magicport2 ;) ( https://github.com/D-Programming-Language/dmd/pull/1980 ) It won't fix the lexer/parser issue on its own as current DMD code that does it does not seem to be easily usable as a library. Though it will allow to switch compiler to imaginary std.d.lexer/std.d.parser to guarantee consistency.
Nov 20 2013
On 11/20/13 11:51 AM, Dicebot wrote:On Wednesday, 20 November 2013 at 14:15:17 UTC, Ary Borenszweig wrote:Awesome :-) Yes, now I remember DMD is not built as a library but as a program (lots of global variables).One day you should all stop doing what you are doing and focus on bootstrapping the compiler.You'll be surprised - https://github.com/yebblies/magicport2 ;) ( https://github.com/D-Programming-Language/dmd/pull/1980 ) It won't fix the lexer/parser issue on its own as current DMD code that does it does not seem to be easily usable as a library. Though it will allow to switch compiler to imaginary std.d.lexer/std.d.parser to guarantee consistency.
Nov 20 2013
"Dicebot" <public dicebot.lv> wrote in message news:nagopvjxtbsaagbsqnxp forum.dlang.org...On Wednesday, 20 November 2013 at 14:15:17 UTC, Ary Borenszweig wrote:Compiler-as-a-library comes next.One day you should all stop doing what you are doing and focus on bootstrapping the compiler.You'll be surprised - https://github.com/yebblies/magicport2 ;) ( https://github.com/D-Programming-Language/dmd/pull/1980 ) It won't fix the lexer/parser issue on its own as current DMD code that does it does not seem to be easily usable as a library. Though it will allow to switch compiler to imaginary std.d.lexer/std.d.parser to guarantee consistency.
Nov 20 2013
On 2013-11-19 22:21, Andrei Alexandrescu wrote:It's not a minor detail. Besides, people took time to explain this to you, and the right response is to integrate that information, not to demean it.I think so. I've not magically become a better programmer just because I know this information. I can also tell you that a lot of programmers have no idea what's the difference between an expression and a statement. Many have not even header of those words. The first person that said that an expression ending with a semicolon is a statement was David, and my reply was "Ok, I didn't know that.". What's so demeaning about that?That's exactly my point. The matter of fact is, in a setting where people are paid to write code, this kind of minor issue would be settled around the first week since hiring. At Facebook for example you'd be submitting a phabricator diff (loosely equivalent to a github pull request) and our linter will point out we use two spaces for indentation instead of tabs, and 80 columns. Then a couple of peers would point out that code is about twice as sparse vertically than it should. You'd fix these issues for good and that would be that. This has happened quite a few times. If, on the other hand, you chose to make a big deal out of it, that would be a cultural mismatch that to my knowledge would be unprecedented.Don't you think I can adapt to a particular style that a company or project is using? I can tell you that I would love to have code reviews like you have at Facebook. We have just started using code reviews at my work, it only took me three _years_ to get to that. I can also say that we have lines with over 400 columns, I hate it. But it's there because no one cared enough. I think it would be insulting if someone about to hire me can't see through the formatting of my code and base a decision on that. I would have no interest in working for someone like that. I don't even want to know what other strange things that can be hiding in that company behaving like that. Who said your style is a better one that I should follow. I should just stop this because it doesn't lead anywhere. -- /Jacob Carlborg
Nov 19 2013
On 11/17/13 11:28 PM, Jonathan M Davis wrote:I thought about this some more. So std.datetime accounts for > 1/3 of all try statements in Phobos. (It also holds a number of other odd records - talking from allegations: largest module in source code, requires most memory to build for unittest, most unittest lines per line of working code. It's great that these are being worked on.) That must mean something. The question is what. It may as well mean that the rest of Phobos is sloppy and does not add nothrow in places where it should, which would make the rest of it insert a bunch of try/catch in the same pattern as std.datetime. An argument based on sense and sensibility would suggest that something ought to be done about that idiom in std.datetime. But the argument "the more nothrow the merrier" is also sensible. So I think a closer look is warranted here, on a case basis. If the conclusion is that such inspection doesn't scale, fine, we've learned something. But I think there are more valid learnings to derive from here. I'll walk through a few samples I gathered below, please don't get offended (I know it can feel odd to have one's own code criticized). All in good spirit. 1. Consider: this(in DateTime dateTime, immutable TimeZone tz = null) nothrow { try this(dateTime, FracSec.from!"hnsecs"(0), tz); catch(Exception e) assert(0, "FracSec's constructor threw when it shouldn't have."); } That's because FracSec.from!"hnsecs"(n) may throw for some values of n. To me, this is overkill. Clearly there must be a way to construct a zero time interval without much code being executed at all, let alone checks and exceptions and whatnot. (In particular FracSec.zero looks like the ticket.) Furthermore, having a simpler constructor call a more complicated constructor reminds one of the mathematician who throws away the water in the kettle to regress to an already solved problem. 2. Consider: this(in DateTime dateTime, in FracSec fracSec, immutable TimeZone tz = null) { immutable fracHNSecs = fracSec.hnsecs; enforce(fracHNSecs >= 0, new DateTimeException("A SysTime cannot have negative fractional seconds.")); _timezone = tz is null ? LocalTime() : tz; try { immutable dateDiff = (dateTime.date - Date(1, 1, 1)).total!"hnsecs"; immutable todDiff = (dateTime.timeOfDay - TimeOfDay(0, 0, 0)).total!"hnsecs"; immutable adjustedTime = dateDiff + todDiff + fracHNSecs; immutable standardTime = _timezone.tzToUTC(adjustedTime); this(standardTime, _timezone); } catch(Exception e) { assert(0, "Date, TimeOfDay, or DateTime's constructor threw when " ~ "it shouldn't have."); } } This is not even marked as nothrow (sic!) so the entire try/catch is completely meaningless - probably a rote application of the idiom without minding its intent. The only difference it would make to the user is that a library bug may manifest in slighlty different ways. The code has a smartaleck thing about it: "You'd be surprised to see an exception here. I, too, would be surprised, and I wanted to make sure I tell you about it". The entire try/catch should be removed. 3. Consider: this(in Date date, immutable TimeZone tz = null) nothrow { _timezone = tz is null ? LocalTime() : tz; try { immutable adjustedTime = (date - Date(1, 1, 1)).total!"hnsecs"; immutable standardTime = _timezone.tzToUTC(adjustedTime); this(standardTime, _timezone); } catch(Exception e) assert(0, "Date's constructor through when it shouldn't have."); } Here, again, we have a nothrow constructor call a more general one, which may throw in general. The intent - maximizing internal reuse through forwarding - is noble. The realization - it takes more code to reuse than to just set the blessed variables - is a corruption of the noble goal. 4. Consider: property FracSec fracSec() const nothrow { try { auto hnsecs = removeUnitsFromHNSecs!"days"(adjTime); if(hnsecs < 0) hnsecs += convert!("hours", "hnsecs")(24); hnsecs = removeUnitsFromHNSecs!"seconds"(hnsecs); return FracSec.from!"hnsecs"(cast(int)hnsecs); } catch(Exception e) assert(0, "FracSec.from!\"hnsecs\"() threw."); } This is an interesting one. Going to FracSec.from's documentation, we see that it throws if the input does not fall within (-1, 1) seconds, which means (if I count correctly) hnsec inputs must fall between -10 million and 10 million. But we already know that we're in good shape because we just called removeUnitsFromHNSecs. The problem is the two calls are disconnected. That means the API could be improved here: combine the two functions by defining a way to get the fractionary FracSec from a time value. That will always succeed by definition, so we're in good shape. 5. Consider: tm toTM() const nothrow { try { auto dateTime = cast(DateTime)this; tm timeInfo; timeInfo.tm_sec = dateTime.second; timeInfo.tm_min = dateTime.minute; timeInfo.tm_hour = dateTime.hour; timeInfo.tm_mday = dateTime.day; timeInfo.tm_mon = dateTime.month - 1; timeInfo.tm_year = dateTime.year - 1900; timeInfo.tm_wday = dateTime.dayOfWeek; timeInfo.tm_yday = dateTime.dayOfYear - 1; timeInfo.tm_isdst = _timezone.dstInEffect(_stdTime); version(Posix) { char[] zone = (timeInfo.tm_isdst ? _timezone.dstName : _timezone.stdName).dup; zone ~= "\0"; timeInfo.tm_gmtoff = cast(int)convert!("hnsecs", "seconds")(adjTime - _stdTime); timeInfo.tm_zone = zone.ptr; } return timeInfo; } catch(Exception e) assert(0, "Either DateTime's constructor threw."); } In fact the assertion message is wrong here. There's no constructor that could fail. The problem is with the .dup! That's a bug in .dup for char[] - it may indeed throw, but that's an Error not an exception. (Anyhow, I got curious about tm_zone out of concern for the odd allocation. According to http://www.cs.utah.edu/dept/old/texinfo/glibc-manual-0.02/library_19.html it's a gnu extension and it's a const char*, meaning the user code is not supposed to mess with it. Therefore, my guess is some sort of memoization here would be in order. But then http://www.gsp.com/cgi-bin/man.cgi?topic=mktime suggests it's not const. Then http://www.eecs.harvard.edu/syrah/vino/release-0.40/man/programref/l bc/time/ctime.3.txt says "he tm_zone field of a returned struct tm points to a static array of characters, which will also be overwritten at the next call (and by calls to tzset)." That means a static char[3] would be in order. Anyhow, more investigation is needed here.) 6. Consider: /+ref SysTime+/ void roll(string units)(long value) nothrow if(units == "hours" || units == "minutes" || units == "seconds") { try { auto hnsecs = adjTime; auto days = splitUnitsFromHNSecs!"days"(hnsecs) + 1; if(hnsecs < 0) { hnsecs += convert!("hours", "hnsecs")(24); --days; } immutable hour = splitUnitsFromHNSecs!"hours"(hnsecs); immutable minute = splitUnitsFromHNSecs!"minutes"(hnsecs); immutable second = splitUnitsFromHNSecs!"seconds"(hnsecs); auto dateTime = DateTime(Date(cast(int)days), TimeOfDay(cast(int)hour, cast(int)minute, cast(int)second)); dateTime.roll!units(value); --days; hnsecs += convert!("hours", "hnsecs")(dateTime.hour); hnsecs += convert!("minutes", "hnsecs")(dateTime.minute); hnsecs += convert!("seconds", "hnsecs")(dateTime.second); if(days < 0) { hnsecs -= convert!("hours", "hnsecs")(24); ++days; } immutable newDaysHNSecs = convert!("days", "hnsecs")(days); adjTime = newDaysHNSecs + hnsecs; } catch(Exception e) assert(0, "Either DateTime's constructor or TimeOfDay's constructor threw."); } Here again the error message is mistaken; the only culprit is TimeOfDay's constructor. This prompts a different protest. Note that up until the point liable to throw, the input "value" is not involved at all, meaning that whatever simple manipulation is done there cannot be done without an inefficiency being in the loop. So there's a problem with the API design. 7. Consider: DateTime opCast(T)() const nothrow if(is(Unqual!T == DateTime)) { try { auto hnsecs = adjTime; auto days = splitUnitsFromHNSecs!"days"(hnsecs) + 1; if(hnsecs < 0) { hnsecs += convert!("hours", "hnsecs")(24); --days; } immutable hour = splitUnitsFromHNSecs!"hours"(hnsecs); immutable minute = splitUnitsFromHNSecs!"minutes"(hnsecs); immutable second = getUnitsFromHNSecs!"seconds"(hnsecs); return DateTime(Date(cast(int)days), TimeOfDay(cast(int)hour, cast(int)minute, cast(int)second)); } catch(Exception e) assert(0, "Either DateTime's constructor or TimeOfDay's constructor threw."); } This seems to be another instance of the problem at (6). No matter how one looks at it, one must admit that the API should be designed such that a valid SysTime should convert to the corresponding DateTime without much intervening brouhaha. ============= That 7 issues with as many try/catch instances, i.e. 13% of the total 54. You may think I cherry-picked them; in fact, they are the FIRST 7 instances I found by simply searching the file for 'try'. I have no reason to believe I won't find similar issues with most or all of them. I think std.datetime needs a pass with an eye for this idiom and obviating it wherever possible. Andrei3. Look into API changes that add nothrow, narrower functions to the more general ones that may throw.In some cases (e.g. format) that's probably a good idea, but I don't think that really scales. In some cases, it would be well worth it, whereas in others, it would just be better to use try-catch in the few places where you know the function won't throw, because it would be quite rare to be able to guarantee that it wouldn't throw. It's also not pleasant to have to duplicate functions all over the place.
Nov 18 2013
On 11/18/2013 11:44 AM, Andrei Alexandrescu wrote:1. Consider: this(in DateTime dateTime, immutable TimeZone tz = null) nothrow { try this(dateTime, FracSec.from!"hnsecs"(0), tz); catch(Exception e) assert(0, "FracSec's constructor threw when it shouldn't have."); } That's because FracSec.from!"hnsecs"(n) may throw for some values of n. To me, this is overkill. Clearly there must be a way to construct a zero time interval without much code being executed at all, let alone checks and exceptions and whatnot. (In particular FracSec.zero looks like the ticket.) Furthermore, having a simpler constructor call a more complicated constructor reminds one of the mathematician who throws away the water in the kettle to regress to an already solved problem.I agree. But I'll add that it would be even better to redesign FracSec.from so it never throws at all - have it assert(0) for invalid values being passed to it. Exceptions should not be thrown for invalid arguments. Data validation should be handled separately.
Nov 18 2013
On Monday, November 18, 2013 12:18:31 Walter Bright wrote:I agree. But I'll add that it would be even better to redesign FracSec.from so it never throws at all - have it assert(0) for invalid values being passed to it. Exceptions should not be thrown for invalid arguments. Data validation should be handled separately.I think that that depends. In some cases, it would just mean duplicating work if the API expected the caller to verify the arguments' validity first. A prime example of this would be functions which parse strings. It would be highly inefficient for a function like fromISOExtString to assert instead of enforcing. The caller would have to do the exact work that the function has to do in order to do the check, so it's just wasteful to expect the caller to ensure the validity of the input. Constructors for types like SysTime or DateTime are in a bit more of a grey area in that in most cases what's passed is likely to be valid - e.g. hopefully, when someone gives a date, they have some clue that the date is valid ahead of time. However, there are cases where the caller really has no way of knowing ahead of time whether a date is going to be valid or not - e.g. is February 29th a valid date in year X? A function could be provided to check the validity ahead of time, but that puts a greater burden on the caller and ends up duplicating the checks. It also would have created more code duplication in std.datetime in some places, because the checks would have to be put in more places rather than having them in a centralized location (e.g. Date's constructor can check the validity of the values, and DateTime can just take advantage of that rather than having to duplicate the check). I do acknowledge however that it's a grey area. In general, std.datetime takes a defensive programming approach rather than the DbC approach with the idea that it's often likely that the input is going to be originate from a user or file and that many of the checks have to be done regardless, so asking the programmer to do them just adds overhead. In some cases, it arguably should have used DbC, but there were enough places that it should have been using defensive programming already that it was more consistent to use defensive programming in any of the grey areas. - Jonathan M Davis
Nov 18 2013
On 11/18/2013 1:16 PM, Jonathan M Davis wrote:On Monday, November 18, 2013 12:18:31 Walter Bright wrote:I think this is exactly a case where you want to have data validation separate from operating on that data. Operating on the data should be efficient, and it is not if it must also validate.I agree. But I'll add that it would be even better to redesign FracSec.from so it never throws at all - have it assert(0) for invalid values being passed to it. Exceptions should not be thrown for invalid arguments. Data validation should be handled separately.I think that that depends. In some cases, it would just mean duplicating work if the API expected the caller to verify the arguments' validity first. A prime example of this would be functions which parse strings. It would be highly inefficient for a function like fromISOExtString to assert instead of enforcing. The caller would have to do the exact work that the function has to do in order to do the check, so it's just wasteful to expect the caller to ensure the validity of the input.Constructors for types like SysTime or DateTime are in a bit more of a grey area in that in most cases what's passed is likely to be valid - e.g. hopefully, when someone gives a date, they have some clue that the date is valid ahead of time. However, there are cases where the caller really has no way of knowing ahead of time whether a date is going to be valid or not - e.g. is February 29th a valid date in year X? A function could be provided to check the validity ahead of time, but that puts a greater burden on the caller and ends up duplicating the checks. It also would have created more code duplication in std.datetime in some places, because the checks would have to be put in more places rather than having them in a centralized location (e.g. Date's constructor can check the validity of the values, and DateTime can just take advantage of that rather than having to duplicate the check). I do acknowledge however that it's a grey area.Again, I think data validation must be a separate function. std.datetime is a prime example of why.In general, std.datetime takes a defensive programming approach rather than the DbC approach with the idea that it's often likely that the input is going to be originate from a user or file and that many of the checks have to be done regardless, so asking the programmer to do them just adds overhead. In some cases, it arguably should have used DbC, but there were enough places that it should have been using defensive programming already that it was more consistent to use defensive programming in any of the grey areas.I think the fact that there's a lot of code in std.datetime that asserts if the underlying functions throw demonstrates my point. Input data validation and operating on the data should be separate operations, not combined. Functions that operate on data should assert on bad arguments, not throw.
Nov 18 2013
On Monday, November 18, 2013 13:56:39 Walter Bright wrote:On 11/18/2013 1:16 PM, Jonathan M Davis wrote:But that would just duplicate the validation. You validate by parsing the string, and you extract the necessary data from it by parsing it. Validating the data first would just double the work - on top of the fact that strings are most likely to have come from outside the program rather than having been generated internally and then parsed internally. This is exactly the sort of case where I think that separate validation makes no sense. Separate validation only makes sense when the result is _less_ overhead, not more. Separate validation is of benefit when the caller can avoid the validation in most cases, and the function itself doesn't need to do it, so the validition only occurs when the caller needs it. If the validation has to be done anyway, then there's no point in having separate validation. It just increases overhead. - Jonathan M DavisOn Monday, November 18, 2013 12:18:31 Walter Bright wrote:I think this is exactly a case where you want to have data validation separate from operating on that data. Operating on the data should be efficient, and it is not if it must also validate.I agree. But I'll add that it would be even better to redesign FracSec.from so it never throws at all - have it assert(0) for invalid values being passed to it. Exceptions should not be thrown for invalid arguments. Data validation should be handled separately.I think that that depends. In some cases, it would just mean duplicating work if the API expected the caller to verify the arguments' validity first. A prime example of this would be functions which parse strings. It would be highly inefficient for a function like fromISOExtString to assert instead of enforcing. The caller would have to do the exact work that the function has to do in order to do the check, so it's just wasteful to expect the caller to ensure the validity of the input.
Nov 18 2013
I'm glad we're discussing this. There's an important misunderstanding. On 11/18/2013 2:16 PM, Jonathan M Davis wrote:But that would just duplicate the validation. You validate by parsing the string, and you extract the necessary data from it by parsing it. Validating the data first would just double the work - on top of the fact that strings are most likely to have come from outside the program rather than having been generated internally and then parsed internally. This is exactly the sort of case where I think that separate validation makes no sense. Separate validation only makes sense when the result is _less_ overhead, not more.The point of asserts is that they are supposed to be redundant. (If they were not redundant, then they would trip and you'd have a program bug.) Asserts are there to detect program bugs, not to validate input data. This is also why the optimizer can remove asserts without affecting the meaning of the code.Separate validation is of benefit when the caller can avoid the validation in most cases, and the function itself doesn't need to do it, so the validition only occurs when the caller needs it. If the validation has to be done anyway, then there's no point in having separate validation. It just increases overhead.Consider again that std.datetime is chock full of code that requires the underlying functions to not throw. Anytime there is code of the pattern: try { func(); } catch (Exception e) { assert(0, "func() should not have thrown"); } then func() has the wrong API or is being misused. Again, data validation and bug checking are two very different things and should not be combined. Note that I have screwed this up myself in the std.uni API.
Nov 18 2013
On Monday, November 18, 2013 14:40:51 Walter Bright wrote:I'm glad we're discussing this. There's an important misunderstanding. On 11/18/2013 2:16 PM, Jonathan M Davis wrote:I understand this. The problem is that in some cases, in order to do the check, you have to do all of the work that the function you're trying to protect bad input from has to do anyway - even without it asserting anything. So, having a separate function do the checking would just be extra overhead. For instance, if you had to parse a string in order to get data out of it, the function checking the string's validity would have parse the string out and get all of the data out of it, validating the string's format and the data's validity in the process, whereas the function that does the actual parsing to give you the result (as opposed to checking the input) has to do all of that same parsing and data extraction. Maybe, if another function had already validated the string, it could avoid a few of the checks, but many of them have to be done just to parse the string (e.g. if its format is wrong, you can't even get at the data properly, regardless of whether the data is valid or not). So, you don't save much in the way of checking if you have a validation function, and you add overhead, because the data has to be processed twice. In other cases, validation is as simple as asserting something about the input, in which case, it's simple enough to assert within the function (which would then go away in -release) and to have a validation function do the checking and get no extra overhead, but that's not always the case, and when it's not, it makes no sense to me to use DbC. In such cases, defensive programming makes far more sense. Also, if the data _always_ has to be checked (which isn't always the case in std.datetime), then it makes no sense to separate the validation from the function doing the work. I think that whether DbC or defensive programming is more appropriate comes down primarily to two things: 1. Does the validation need to be part of the function for it to do its job, or does doing the validation require doing what the function is going to do anyway? If so, the defensive programming makes more sense. If not, then DbC makes more sense. 2. Is this function treating its caller as part of the program or as a user? If the caller is being treated as part of the program, then DbC tends to make sense, as its reasonable to require that the caller knows what the function requires and is effectively part of the same code as the function. If the caller is being treated as a user (as is often going to be the case with libraries), then it's generally better to use defensive programming, because it ensures that the function gets and operates on valid input rather than resulting in undefined behavior when the caller gives bad input (and unless the library is compiled without -release or the function is templated, assertions won't do anything to help in a library). Efficiency tends toward lean towards using DbC, whereas user-friendliness leans toward defensive programming. In general, I would use DbC internally to a program and defensive programming in a library. Having validator functions definitely helps with DbC, as it gives the caller a way to validate the input when necessary and avoid the validation when it isn't. But it puts all the onus on the caller and makes it much, much more likely that functions will be misused, and if the function is in a library, then the odds are that if validation is done incorrectly by the caller, it'll never get checked by the callee, and you'll end up with buggy code with undefined behavior. I think that you bring up good points, but I also don't think that the situation is anywhere near as clearcut as you do. - Jonathan M DavisBut that would just duplicate the validation. You validate by parsing the string, and you extract the necessary data from it by parsing it. Validating the data first would just double the work - on top of the fact that strings are most likely to have come from outside the program rather than having been generated internally and then parsed internally. This is exactly the sort of case where I think that separate validation makes no sense. Separate validation only makes sense when the result is _less_ overhead, not more.The point of asserts is that they are supposed to be redundant. (If they were not redundant, then they would trip and you'd have a program bug.) Asserts are there to detect program bugs, not to validate input data. This is also why the optimizer can remove asserts without affecting the meaning of the code.
Nov 18 2013
On 2013-11-18 23:42:13 +0000, "Jonathan M Davis" <jmdavisProg gmx.com> said:I understand this. The problem is that in some cases, in order to do the check, you have to do all of the work that the function you're trying to protect bad input from has to do anyway - even without it asserting anything. So, having a separate function do the checking would just be extra overhead.Very true. I'll just point out now that you could actually have only one function that does one or the other or both depending on template parameters. One template parameter is the output sink, which could be a dummy type that does nothing or an actual type that saves the data somewhere. Another parameter is the error handler, which can be a dummy type that does nothing, or it could assert or throw when an error is found. Let the optimizer remove the do-nothing code paths that will result. Now, I really have no idea but that could be overkill in this situation. -- Michel Fortin michel.fortin michelf.ca http://michelf.ca
Nov 18 2013
On Tue, 19 Nov 2013 03:29:48 -0000, Michel Fortin <michel.fortin michelf.ca> wrote:On 2013-11-18 23:42:13 +0000, "Jonathan M Davis" <jmdavisProg gmx.com> said:Why use templates at all? Lets just keep it simple.. You just write /the/ one internal conversion/check function (for each case) such that it returns a boolean success status, then.. in release mode you call it and throw on false/failure, and in debug mode you call it in and assert on false/failure. R -- Using Opera's revolutionary email client: http://www.opera.com/mail/I understand this. The problem is that in some cases, in order to do the check, you have to do all of the work that the function you're trying to protect bad input from has to do anyway - even without it asserting anything. So, having a separate function do the checking would just be extra overhead.Very true. I'll just point out now that you could actually have only one function that does one or the other or both depending on template parameters. One template parameter is the output sink, which could be a dummy type that does nothing or an actual type that saves the data somewhere. Another parameter is the error handler, which can be a dummy type that does nothing, or it could assert or throw when an error is found. Let the optimizer remove the do-nothing code paths that will result. Now, I really have no idea but that could be overkill in this situation.
Nov 19 2013
On Monday, 18 November 2013 at 22:17:16 UTC, Jonathan M Davis wrote:You validate by parsing the string, and you extract the necessary data from it by parsing it.I feel like you and Walter are both saying the same thing. fromISOString sounds to me like a data validation function. It is one that once validated provides a "validated" type, much like SortedRange. I don't think Walter disagrees with that, but isn't calling it out specifically. Did I get that right?
Nov 20 2013
On Thursday, November 21, 2013 08:20:39 Jesse Phillips wrote:On Monday, 18 November 2013 at 22:17:16 UTC, Jonathan M Davis wrote:As I understand it, when Walter is talking about a validator function, _all_ it does is validate the input. It doesn't operate on the data beyond doing validation - the idea being that you validate that the input has some particular property or properties and then all the code that follows can just assume that rather than checking it again. For instance, you could have a function which validated that a string was valid UTF-8 and then have all code that follows just assume that it was valid UTF-8 without ever checking. The validator function is a separate function from any of the functions that actually do any of the work. So, to do that with fromISOExtString, you'd have something like enforce(isValidISOExtString(str)); //validator function auto data = Date.fromISOExtString(str); //func which actually does the work and fromISOExtString wouldn't do any validation at all. It would assume that the caller had already done the validation - either by calling the validator function - isValidISOExtString - or by knowing that it was valid based on where it came from (e.g. from toISOExtString). In many cases, this makes sense from the standpoint of efficiency, because it allows you to check once and then avoid checking in the rest of the code rather than checking at various points throughout the code and potentially duplicating the checks that way. However, in the case of fromISOExtString (as well as many other parsing functions), you are essentially required to parse the string in order to validate it, meaning that if you had isValidISOExtString, and you called it before fromISOExtString, you'd be doubling the amount of work. fromISOExtString might be able to avoid a few of its checks if it knew that it could assume that the string was already valid, but a large portion of the work would have to be done regardless simply because that's what it takes to parse the date from the string. As such, having a separate validator function doesn't make a lot of sense. Rather, it makes more sense to have the function validate its own input rather than assume that the caller already validated it. But Walter seems to be arguing in favor of having a separate validator function all the time (or very close to all the time) - including in this particular case. So, I don't think that we're saying the same thing at all. - Jonathan M DavisYou validate by parsing the string, and you extract the necessary data from it by parsing it.I feel like you and Walter are both saying the same thing. fromISOString sounds to me like a data validation function. It is one that once validated provides a "validated" type, much like SortedRange. I don't think Walter disagrees with that, but isn't calling it out specifically. Did I get that right?
Nov 20 2013
On Thursday, 21 November 2013 at 07:44:44 UTC, Jonathan M Davis wrote:As I understand it, when Walter is talking about a validator function, _all_ it does is validate the input. It doesn't operate on the data beyond doing validationI doubt Walter first checks that a file is a valid D program before tokenizing. More importantly he probably would not go for such a change. The thing is, ISO String isn't a date, we first need to create a date and in doing so we validate it is valid and never have to check again.
Nov 21 2013
On Thursday, November 21, 2013 16:38:48 Jesse Phillips wrote:On Thursday, 21 November 2013 at 07:44:44 UTC, Jonathan M Davis wrote:Of course not, which makes his argument all the odder. Maybe he misunderstood what fromISOExtString does when responding to my argument that it made no sense to use a separate validator function for it.As I understand it, when Walter is talking about a validator function, _all_ it does is validate the input. It doesn't operate on the data beyond doing validationI doubt Walter first checks that a file is a valid D program before tokenizing. More importantly he probably would not go for such a change.The thing is, ISO String isn't a date, we first need to create a date and in doing so we validate it is valid and never have to check again.Agreed. But Walter is specifically arguing for doing validation in separate functions from those that do the work, which would mean having something like isValidateISOExtString which could be called fromISOExtString and then requiring that the string passed to fromISOExtString be valid or you get undefined behavior (in release at least, where there would be no assertions) as opposed to doing the checking in fromISOExtString. It's not necessarily a bad idea in the general case, but in this particular case, it makes no sense IMHO. However, when I argued that, for some reason Walter seemed to think that it was a perfect example of a case where there should be a separate validator function. So, maybe there's a miscommunicaton with regards to what fromISOExtString does. - Jonathan M Davis
Nov 21 2013
On 11/18/2013 11:44 AM, Andrei Alexandrescu wrote:Here, again, we have a nothrow constructor call a more general one, which may throw in general. The intent - maximizing internal reuse through forwarding - is noble. The realization - it takes more code to reuse than to just set the blessed variables - is a corruption of the noble goal.I also have a design issue with having throwing constructors at all. I know D allows them, but I consider it a bad practice.
Nov 18 2013
On 2013-11-18 20:44, Andrei Alexandrescu wrote:I thought about this some more. So std.datetime accounts for > 1/3 of all try statements in Phobos. (It also holds a number of other odd records - talking from allegations: largest module in source code, requires most memory to build for unittest, most unittest lines per line of working code. It's great that these are being worked on.) That must mean something. The question is what. It may as well mean that the rest of Phobos is sloppy and does not add nothrow in places where it should, which would make the rest of it insert a bunch of try/catch in the same pattern as std.datetime. An argument based on sense and sensibility would suggest that something ought to be done about that idiom in std.datetime. But the argument "the more nothrow the merrier" is also sensible.I really don't understand the point in having some functions thrown and others assert. -- /Jacob Carlborg
Nov 18 2013
On 11/18/2013 1:02 PM, Jacob Carlborg wrote:I really don't understand the point in having some functions thrown and others assert.That's a red flag that there is a faulty design somewhere, in this case I think it's not having a separation between input data validation and program bug detection.
Nov 18 2013
On 11/18/13 2:04 PM, Walter Bright wrote:On 11/18/2013 1:02 PM, Jacob Carlborg wrote:The question is what's "program" and what's "input". Consider: int a = fun(); std.gun(a); There are two possible takes on this: 1. The standard library is considered part of the user's program, and the whole thing is a unit. In that case, passing the wrong int to std.gun is an PROGRAM error and 100% blame goes to the programmer who wrote the caller code. In that case, assert/assert(0)/contracts are the appropriate constructs to be used inside std.gun. This is the approach taken by the C standard library, which is free to do whatever it wants (including crashing the program) upon calls such as strlen(NULL) etc. 2. The standard library is a separate entity from the PROGRAM, and as far as it cares, any data from the user is INPUT. So the standard library with SCRUB the input, in which case enforce() and throwing exceptions are appropriate. extent the newer parts of C++'s standard library. To claim that one approach is exactly right and the other is exactly wrong would miss important insights. AndreiI really don't understand the point in having some functions thrown and others assert.That's a red flag that there is a faulty design somewhere, in this case I think it's not having a separation between input data validation and program bug detection.
Nov 18 2013
On 11/18/2013 2:45 PM, Andrei Alexandrescu wrote:There are two possible takes on this: 1. The standard library is considered part of the user's program, and the whole thing is a unit. In that case, passing the wrong int to std.gun is an PROGRAM error and 100% blame goes to the programmer who wrote the caller code. In that case, assert/assert(0)/contracts are the appropriate constructs to be used inside std.gun. This is the approach taken by the C standard library, which is free to do whatever it wants (including crashing the program) upon calls such as strlen(NULL) etc. 2. The standard library is a separate entity from the PROGRAM, and as far as it cares, any data from the user is INPUT. So the standard library with SCRUB the input, in which case enforce() and throwing exceptions are appropriate. the newer parts of C++'s standard library. To claim that one approach is exactly right and the other is exactly wrong would miss important insights.Or: 3. Input validation and data processing should have separate functions in the library. (The Windows API is a special case - it must regard all input as untrusted, unvalidated input, and it must protect Windows itself from malicious input. This is not true of Phobos.)
Nov 18 2013
On 11/18/2013 11:45 PM, Andrei Alexandrescu wrote:On 11/18/13 2:04 PM, Walter Bright wrote:1. Has the shortcoming that you may actually get assertion failures. 2. is not wrong but aesthetically unpleasing as it essentially widens the interface to allow all possible inputs and extends it with behaviour that is useless in order to avoid the situation that the caller does not satisfy the interface. There is also: 3. Require both caller and callee to construct a compile-time checkable proof that they satisfy their part of the interface. This is the approach taken by dependently typed programming languages, and program verifiers. This subsumes 1. and 2. and is a solution to the original problem since in such a setting it can be manually proven that a function call will not throw.On 11/18/2013 1:02 PM, Jacob Carlborg wrote:The question is what's "program" and what's "input". Consider: int a = fun(); std.gun(a); There are two possible takes on this: 1. The standard library is considered part of the user's program, and the whole thing is a unit. In that case, passing the wrong int to std.gun is an PROGRAM error and 100% blame goes to the programmer who wrote the caller code. In that case, assert/assert(0)/contracts are the appropriate constructs to be used inside std.gun. This is the approach taken by the C standard library, which is free to do whatever it wants (including crashing the program) upon calls such as strlen(NULL) etc. 2. The standard library is a separate entity from the PROGRAM, and as far as it cares, any data from the user is INPUT. So the standard library with SCRUB the input, in which case enforce() and throwing exceptions are appropriate. extent the newer parts of C++'s standard library. To claim that one approach is exactly right and the other is exactly wrong would miss important insights. ...I really don't understand the point in having some functions thrown and others assert.That's a red flag that there is a faulty design somewhere, in this case I think it's not having a separation between input data validation and program bug detection.
Nov 18 2013
On Monday, November 18, 2013 14:45:54 Andrei Alexandrescu wrote:On 11/18/13 2:04 PM, Walter Bright wrote:+1 - Jonathan M DavisOn 11/18/2013 1:02 PM, Jacob Carlborg wrote:The question is what's "program" and what's "input". Consider: int a = fun(); std.gun(a); There are two possible takes on this: 1. The standard library is considered part of the user's program, and the whole thing is a unit. In that case, passing the wrong int to std.gun is an PROGRAM error and 100% blame goes to the programmer who wrote the caller code. In that case, assert/assert(0)/contracts are the appropriate constructs to be used inside std.gun. This is the approach taken by the C standard library, which is free to do whatever it wants (including crashing the program) upon calls such as strlen(NULL) etc. 2. The standard library is a separate entity from the PROGRAM, and as far as it cares, any data from the user is INPUT. So the standard library with SCRUB the input, in which case enforce() and throwing exceptions are appropriate. extent the newer parts of C++'s standard library. To claim that one approach is exactly right and the other is exactly wrong would miss important insights.I really don't understand the point in having some functions thrown and others assert.That's a red flag that there is a faulty design somewhere, in this case I think it's not having a separation between input data validation and program bug detection.
Nov 18 2013
On 2013-11-18 23:45, Andrei Alexandrescu wrote:The question is what's "program" and what's "input". Consider: int a = fun(); std.gun(a); There are two possible takes on this: 1. The standard library is considered part of the user's program, and the whole thing is a unit. In that case, passing the wrong int to std.gun is an PROGRAM error and 100% blame goes to the programmer who wrote the caller code. In that case, assert/assert(0)/contracts are the appropriate constructs to be used inside std.gun. This is the approach taken by the C standard library, which is free to do whatever it wants (including crashing the program) upon calls such as strlen(NULL) etc.In D, we can start by saying that all private and package functions should use asserts.2. The standard library is a separate entity from the PROGRAM, and as far as it cares, any data from the user is INPUT. So the standard library with SCRUB the input, in which case enforce() and throwing exceptions are appropriate. extent the newer parts of C++'s standard library. To claim that one approach is exactly right and the other is exactly wrong would miss important insights.I agree, it's not easy to just pick one. Say you have a library that queries a database. Is it the user's responsibility to validate any input that it doesn't contain SQL injections. Or should the library do that? I would say that it would be quite cumbersome and verbose to for the user to constantly validate the input. If the library didn't validate the user would most likely create a wrapper function that does this. Most users would want to have a function like this and implement it. When we come to this, one can ask, isn't this the job of the library? If every user needs it, shouldn't it be put in the library? -- /Jacob Carlborg
Nov 18 2013
"Jacob Carlborg" wrote:I would say that it would be quite cumbersome and verbose to for the user to constantly validate the input.If the "user level" validation fails, shall we throw or assert or...? -- Jouko --- avast! Antivirus k=C3=A4ynniss=C3=A4, joten t=C3=A4ss=C3=A4 s=C3=A4hk=C3=B6= postiviestiss=C3=A4 ei ole viruksia tai haittaohjelmia. http://www.avast.com
Nov 19 2013
On 2013-11-19 18:11, Jouko Koski wrote:If the "user level" validation fails, shall we throw or assert or...?Throw. -- /Jacob Carlborg
Nov 19 2013
"Jacob Carlborg" wrote:On 2013-11-19 18:11, Jouko Koski wrote:Ok. What if the "user" is writing a library code which calls other system utilities? If the validation fails, is the answer now assert? I don't have a definite answer. However, I tend to follow the practice that all public API should do validation and throw on failures. I use assert for catching my own mistakes in more or less internal stuff. Usually I don't find separate validation API functions very practical. One can forget or ignore to do validation, just like one can fail to check errno after a call. Of course, validation is necessary and exceptions can assert (sic!) this. It might be more of a personal preference, but often I catch exceptions only for error reporting purposes. Exception is an indication of an unexpected condition in the program. The solution is to fix the program to be prepared for the condition, not trying to fix the condition afterwards as the exception has already fired. Not all APIs support this approach. -- JoukoIf the "user level" validation fails, shall we throw or assert or...?Throw.
Nov 19 2013
On 2013-11-20 08:22, Jouko Koski wrote:Ok. What if the "user" is writing a library code which calls other system utilities? If the validation fails, is the answer now assert? I don't have a definite answer. However, I tend to follow the practice that all public API should do validation and throw on failures. I use assert for catching my own mistakes in more or less internal stuff.I neither have a definite answer. It depends on what that particular function is doing.Usually I don't find separate validation API functions very practical. One can forget or ignore to do validation, just like one can fail to check errno after a call. Of course, validation is necessary and exceptions can assert (sic!) this. It might be more of a personal preference, but often I catch exceptions only for error reporting purposes. Exception is an indication of an unexpected condition in the program. The solution is to fix the program to be prepared for the condition, not trying to fix the condition afterwards as the exception has already fired. Not all APIs support this approach.I all depends on what API's and what the program is doing. Trying to read a file which you don't have permission to read, or doesn't exist would probably throw an exception. But that is not an error by the programmer. It could be perfectly fine to catch that exception and ask the user of the program to try a different file. -- /Jacob Carlborg
Nov 19 2013
19-Nov-2013 02:45, Andrei Alexandrescu пишет:On 11/18/13 2:04 PM, Walter Bright wrote:3. The standard library handles all input, the wrong input is eliminated as a class by providing meaningful output on error. So called soft-errors. An example would be returning a 0xFFFD when decoding a broken UTF-8 codepoint. Arguably bad examples include fopen/malloc returning 0. All three cases can and should be judiciously mixed and matched. It's also interesting to see that some primitives are better be designed to work with untrusted input see utf.decode. While others are designed not. What I mean by _designed_ is that encountering wrong inputs in a correct program is unavoidable and to be expected. Also (2) and (3) can be constructed on top of (1) by widening the interface. Compare hypothetical: Date parseDate(string str); vs: Date date(Year year, Month month, Day day); The first better be designed to scrub input, while the second is designed to accept verified values (hence Year, Month and not naked ints). -- Dmitry OlshanskyOn 11/18/2013 1:02 PM, Jacob Carlborg wrote:The question is what's "program" and what's "input". Consider: int a = fun(); std.gun(a); There are two possible takes on this: 1. The standard library is considered part of the user's program, and the whole thing is a unit. In that case, passing the wrong int to std.gun is an PROGRAM error and 100% blame goes to the programmer who wrote the caller code. In that case, assert/assert(0)/contracts are the appropriate constructs to be used inside std.gun. This is the approach taken by the C standard library, which is free to do whatever it wants (including crashing the program) upon calls such as strlen(NULL) etc. 2. The standard library is a separate entity from the PROGRAM, and as far as it cares, any data from the user is INPUT. So the standard library with SCRUB the input, in which case enforce() and throwing exceptions are appropriate.I really don't understand the point in having some functions thrown and others assert.That's a red flag that there is a faulty design somewhere, in this case I think it's not having a separation between input data validation and program bug detection.
Nov 19 2013
On Tuesday, November 19, 2013 16:48:30 Dmitry Olshansky wrote:It's also interesting to see that some primitives are better be designed to work with untrusted input see utf.decode. While others are designed not. What I mean by _designed_ is that encountering wrong inputs in a correct program is unavoidable and to be expected. Also (2) and (3) can be constructed on top of (1) by widening the interface. Compare hypothetical: Date parseDate(string str); vs: Date date(Year year, Month month, Day day); The first better be designed to scrub input, while the second is designed to accept verified values (hence Year, Month and not naked ints).True, though there can still be invalid days depending on the month and year, so it's not the case that there can't be bad input even if you no casting to Year, Month, or Day was involved. It's one that I debated on and end up going with exceptions, because it was consistent with everything else in std.datetime to do so, but it could also be argued that asserting would have been better in this case. In contrast, as you indicated, parsing a string for a date is clearly a case where exceptions are better. - Jonathan M Davis
Nov 19 2013
On Monday, November 18, 2013 11:44:46 Andrei Alexandrescu wrote:That 7 issues with as many try/catch instances, i.e. 13% of the total 54. You may think I cherry-picked them; in fact, they are the FIRST 7 instances I found by simply searching the file for 'try'. I have no reason to believe I won't find similar issues with most or all of them. I think std.datetime needs a pass with an eye for this idiom and obviating it wherever possible.Clearly, I need to take a look at this. I don't think that I ever made an attempt to avoid the try-catch-assert(0) idiom. I used exceptions where they made sense and then used try-catch-assert(0) where it was required in order to mark functions as nothrow when I knew that they logically could/should be nothrow. On the whole, I think that the implementaton of std.datetime is good, but my primary focus was always on the API, and it was a lot of code for people to review, so it's not entirely surprising if some things could/should be improved. There were a _lot_ of improvements thanks to the review process, but that doesn't mean that everything was caught. It's also the case that we've all learned quite a bit in the years since, and the compiler and libraries have improved as well, changing what does and doesn't work in some cases (e.g. any function with format is currently impure in std.datetime, but it now may be able to be pure). So, it should now be possible to improve some of std.datetime's implementation beyond what we could do when it was first introduced. Once I've finished splitting std.datetime (which is mostly done, but I've been too busy lately to finish quite yet), I'll look into reducing the number of instances try-catch-assert(0) in std.datetime as well as looking for any other implementation improvements which can be made. And that's one thing that the plethora of unit tests makes easier, because the risk of breaking stuff and not catching it is fairly low (in fact, Martin Nowak made improvements to a function in core.time recently and expressed that he was glad for how much easier it made it to catch mistakes in his refactor because of how thorough the tests were). - Jonathan M Davis
Nov 18 2013
"Andrei Alexandrescu" <SeeWebsiteForEmail erdani.org> wrote in message news:l6ccae$2cmt$1 digitalmars.com...1. Fix scope(failure) and then use it.scope(failure) picks up Errors as well as Exceptions
Nov 18 2013
On Monday, November 18, 2013 20:56:47 Daniel Murphy wrote:"Andrei Alexandrescu" <SeeWebsiteForEmail erdani.org> wrote in message news:l6ccae$2cmt$1 digitalmars.com...Ouch. That's true, which more or less kills that idea - though according to Walter, it's not supposed to be guaranteed to do so, and he'd probably prefer that it never did. But that's a whole debate in and of itself, and we've had it before. - Jonathan M Davis1. Fix scope(failure) and then use it.scope(failure) picks up Errors as well as Exceptions
Nov 18 2013
"Jonathan M Davis" <jmdavisProg gmx.com> wrote in message news:mailman.59.1384769025.2552.digitalmars-d puremagic.com...On Monday, November 18, 2013 20:56:47 Daniel Murphy wrote:Yeah. On the other hand, if we decide assert(0) means 'assume unreachable' we can optimize out the try-catch in release mode, among other things. try { s } catch { assert(0); } -> s if (e) assert(0); else s; -> e; s; etc"Andrei Alexandrescu" <SeeWebsiteForEmail erdani.org> wrote in message news:l6ccae$2cmt$1 digitalmars.com...Ouch. That's true, which more or less kills that idea - though according to Walter, it's not supposed to be guaranteed to do so, and he'd probably prefer that it never did. But that's a whole debate in and of itself, and we've had it before. - Jonathan M Davis1. Fix scope(failure) and then use it.scope(failure) picks up Errors as well as Exceptions
Nov 18 2013
On Monday, November 18, 2013 21:16:20 Daniel Murphy wrote:Yeah. On the other hand, if we decide assert(0) means 'assume unreachable' we can optimize out the try-catch in release mode, among other things. try { s } catch { assert(0); } -> s if (e) assert(0); else s; -> e; s; etcI was hoping that we could do something like that, since that would fix this problem as far as optimizations go. It leaves the slight verbosity of the try- catch, which is a bit annoying, but I wouldn't consider it all that big a deal, as nice as it would be to make it less verbose. - Jonathan M Davis
Nov 18 2013
On 2013-11-18 11:16, Daniel Murphy wrote:Yeah. On the other hand, if we decide assert(0) means 'assume unreachable' we can optimize out the try-catch in release mode, among other things. try { s } catch { assert(0); } -> s if (e) assert(0); else s; -> e; s;"assume" isn't the same as the compile will do this. Currently the spec says "Either AssertError is thrown at runtime if it is reachable, or the execution is halted". To me that means an implementation is free to throw an AssertError. -- /Jacob Carlborg
Nov 18 2013
On Monday, November 18, 2013 11:53:50 Jacob Carlborg wrote:On 2013-11-18 11:16, Daniel Murphy wrote:assert(0) is intended specifically for use in cases where a line is supposed to be unreachable, and it wouldn't make any sense to use it in any other case, because assertion failures are intended to kill the program, and assert(0) always fails. It happens that it throws an AssertError in non-release mode in order to give you better debug information on failure, and it's a HLT instruction in release, but in either case, it's intended to be unreachable code. The spec really should be updated to make it clear that when assertions are compiled in, assert(0) throws an AssertError and that when assertions are supposed to be compiled out, it becomes a HLT instruction. And if need be, we can update the spec to require that try-catches be compiled out when assertions are compiled out, and the catch's body only contains an assert(0). - Jonathan M DavisYeah. On the other hand, if we decide assert(0) means 'assume unreachable' we can optimize out the try-catch in release mode, among other things. try { s } catch { assert(0); } -> s if (e) assert(0); else s; -> e; s;"assume" isn't the same as the compile will do this. Currently the spec says "Either AssertError is thrown at runtime if it is reachable, or the execution is halted". To me that means an implementation is free to throw an AssertError.
Nov 18 2013
On 2013-11-18 12:03, Jonathan M Davis wrote:assert(0) is intended specifically for use in cases where a line is supposed to be unreachable, and it wouldn't make any sense to use it in any other case, because assertion failures are intended to kill the program, and assert(0) always fails. It happens that it throws an AssertError in non-release mode in order to give you better debug information on failure, and it's a HLT instruction in release, but in either case, it's intended to be unreachable code. The spec really should be updated to make it clear that when assertions are compiled in, assert(0) throws an AssertError and that when assertions are supposed to be compiled out, it becomes a HLT instruction. And if need be, we can update the spec to require that try-catches be compiled out when assertions are compiled out, and the catch's body only contains an assert(0).Does all architectures support the HLT instruction or equivalent? The spec explicitly says HLT is used on x86. -- /Jacob Carlborg
Nov 18 2013
On Monday, November 18, 2013 13:23:23 Jacob Carlborg wrote:On 2013-11-18 12:03, Jonathan M Davis wrote:I don't know. My knowledge of stuff at that level on x86 is already poor. I have pretty much zero knowledge about that sort of thing on other architectures. The extent of my knowledge in that area tends to be restricted to big endian vs little endian. The only reason that I even know that HLT exists is thanks to this feature in D. If it's not possible to require HLT or an equivalent, then it wouldn't make sense to put that in the spec, which would be unfortunate, but ideally that would be required. However, even if it can't be, that doesn't change the fact that assert(0) is intended to indicate unreachable code and doesn't make sense for anything else (as I recall, the compiler itself inserts it for that purpose at the end of functions in some circumstances to catch missing return statements). So, I see no problem with the compiler making optimizations in release based on the assumption that assert(0) should never be reached. - Jonathan M Davisassert(0) is intended specifically for use in cases where a line is supposed to be unreachable, and it wouldn't make any sense to use it in any other case, because assertion failures are intended to kill the program, and assert(0) always fails. It happens that it throws an AssertError in non-release mode in order to give you better debug information on failure, and it's a HLT instruction in release, but in either case, it's intended to be unreachable code. The spec really should be updated to make it clear that when assertions are compiled in, assert(0) throws an AssertError and that when assertions are supposed to be compiled out, it becomes a HLT instruction. And if need be, we can update the spec to require that try-catches be compiled out when assertions are compiled out, and the catch's body only contains an assert(0).Does all architectures support the HLT instruction or equivalent? The spec explicitly says HLT is used on x86.
Nov 18 2013
On 11/18/2013 4:23 AM, Jacob Carlborg wrote:Does all architectures support the HLT instruction or equivalent? The spec explicitly says HLT is used on x86.Most architectures I've used supported a HLT, and there's always something the back end could construct if it had to, even: ... call halt ...
Nov 18 2013
On 2013-11-18 21:49, Walter Bright wrote:Most architectures I've used supported a HLT, and there's always something the back end could construct if it had to, even: ... call halt ...In that case, would we want it to be legal to throw AssertError instead of using the halt instructions? Because that's what the docs says. -- /Jacob Carlborg
Nov 18 2013
On 11/18/2013 1:04 PM, Jacob Carlborg wrote:On 2013-11-18 21:49, Walter Bright wrote:Sure.Most architectures I've used supported a HLT, and there's always something the back end could construct if it had to, even: ... call halt ...In that case, would we want it to be legal to throw AssertError instead of using the halt instructions? Because that's what the docs says.
Nov 18 2013
On Monday, November 18, 2013 22:04:59 Jacob Carlborg wrote:In that case, would we want it to be legal to throw AssertError instead of using the halt instructions? Because that's what the docs says.For when you're not in release mode. You want the more informative AssertError in that case. HLT is just for when assertions get compiled out. - Jonathan M Davis
Nov 18 2013
On 2013-11-18 23:21, Jonathan M Davis wrote:For when you're not in release mode. You want the more informative AssertError in that case. HLT is just for when assertions get compiled out.I'm talking about release mode. -- /Jacob Carlborg
Nov 18 2013
On Monday, 18 November 2013 at 20:49:48 UTC, Walter Bright wrote:Most architectures I've used supported a HLT, and there's always something the back end could construct if it had to, even: ... call halt ...I am pretty sure on my x86_64 Linux `assert(0)` results in termination via SIGILL (Illegal Instruction). Well, it is still a hard crash so no big deal but probably in not an expected way.
Nov 19 2013
On 11/18/2013 2:16 AM, Daniel Murphy wrote:Yeah. On the other hand, if we decide assert(0) means 'assume unreachable' we can optimize out the try-catch in release mode, among other things. try { s } catch { assert(0); } -> s if (e) assert(0); else s; -> e; s;I seriously object to this, as assert(0) is there for when you really do want an assert in release builds. I.e.: assert(!e); // removed in release builds if (e) assert(0); // stays in release builds It's a valid D idiom, not a bug or oversight.
Nov 18 2013
On Monday, November 18, 2013 12:47:32 Walter Bright wrote:On 11/18/2013 2:16 AM, Daniel Murphy wrote:Then what would you suggest for dealing with cases where a nothrow function calls a function which can throw in general but can't throw with the given arguments? Completely separate from the issue of whether std.datetime should need to be calling a throwing function from a nothrow function as often as it does, it's still an issue which must be dealt with in general. At present, any nothrow function which wants to call a throwing function needs to use a try- catch block to wrap the throwing function, and it makes good sense to use assert(0) to catch the case where the caller screwed up and the function can indeed throw. However, given that that throwing function should never throw, it's desirable to be able to optimize out the try-catch-assert(0). How would we do that without taking advantage of the fact that assert(0) indicates that that line of code should be unreachable? If this were safe, we would use trusted, but this is nothrow, and there is no trusted-nothrow. - Jonathan M DavisYeah. On the other hand, if we decide assert(0) means 'assume unreachable' we can optimize out the try-catch in release mode, among other things. try { s } catch { assert(0); } -> s if (e) assert(0); else s; -> e; s;I seriously object to this, as assert(0) is there for when you really do want an assert in release builds. I.e.: assert(!e); // removed in release builds if (e) assert(0); // stays in release builds It's a valid D idiom, not a bug or oversight.
Nov 18 2013
On 11/18/2013 1:23 PM, Jonathan M Davis wrote:Then what would you suggest for dealing with cases where a nothrow function calls a function which can throw in general but can't throw with the given arguments? Completely separate from the issue of whether std.datetime should need to be calling a throwing function from a nothrow function as often as it does, it's still an issue which must be dealt with in general. At present, any nothrow function which wants to call a throwing function needs to use a try- catch block to wrap the throwing function, and it makes good sense to use assert(0) to catch the case where the caller screwed up and the function can indeed throw. However, given that that throwing function should never throw, it's desirable to be able to optimize out the try-catch-assert(0). How would we do that without taking advantage of the fact that assert(0) indicates that that line of code should be unreachable?I believe this is solving the problem in the wrong place. The function being called should not be throwing, and should be defined as not throwing. See my other posts in this thread on the topic.
Nov 18 2013
On Monday, November 18, 2013 13:50:42 Walter Bright wrote:On 11/18/2013 1:23 PM, Jonathan M Davis wrote:Sometimes that's true, but if you're asserting that it's not going to be the case that folks are going to need to calling functions which can throw but won't throw from inside nothrow functions, I think that your dead wrong. If nothing else, you don't always have control over what is and isn't going to throw. Maybe it's uncommon enough that it's not worth trying to optimize out the try-catch blocks, but I think that asserting that you won't ever need to call a throwing function from a nothrow function when you know it won't throw is like asserting that trusted isn't needed. - Jonathan M DavisThen what would you suggest for dealing with cases where a nothrow function calls a function which can throw in general but can't throw with the given arguments? Completely separate from the issue of whether std.datetime should need to be calling a throwing function from a nothrow function as often as it does, it's still an issue which must be dealt with in general. At present, any nothrow function which wants to call a throwing function needs to use a try- catch block to wrap the throwing function, and it makes good sense to use assert(0) to catch the case where the caller screwed up and the function can indeed throw. However, given that that throwing function should never throw, it's desirable to be able to optimize out the try-catch-assert(0). How would we do that without taking advantage of the fact that assert(0) indicates that that line of code should be unreachable?I believe this is solving the problem in the wrong place. The function being called should not be throwing, and should be defined as not throwing. See my other posts in this thread on the topic.
Nov 18 2013
On 11/18/2013 2:16 PM, Jonathan M Davis wrote:Sometimes that's true, but if you're asserting that it's not going to be the case that folks are going to need to calling functions which can throw but won't throw from inside nothrow functions, I think that your dead wrong. If nothing else, you don't always have control over what is and isn't going to throw. Maybe it's uncommon enough that it's not worth trying to optimize out the try-catch blocks, but I think that asserting that you won't ever need to call a throwing function from a nothrow function when you know it won't throw is like asserting that trusted isn't needed.What I'm saying is it is bad API design to conflate data processing with input data validation. You may not always have control over the API design and may have to live with bad API design, but in std.datetime's case we do have control and we can do the right thing. Joel Spolsky wrote a nice column http://www.joelonsoftware.com/articles/Wrong.html about this years ago, about conceptually separating input data validation operations from data crunching operations. This distinction is baked into D's design: exceptions - input data validation asserts - crunching on data that has been validated Code like this: try { func(); } catch (Exception e) { assert(0, "func() should not have thrown"); } clearly shows a design failure, either in func()'s design or how func() is used.
Nov 18 2013
On 11/18/13 1:50 PM, Walter Bright wrote:I believe this is solving the problem in the wrong place. The function being called should not be throwing, and should be defined as not throwing. See my other posts in this thread on the topic.I think a stance of unceremoniously halting the program upon passing the wrong parameters to a standard library function is a bit excessive. Andrei
Nov 18 2013
On 11/18/2013 2:37 PM, Andrei Alexandrescu wrote:On 11/18/13 1:50 PM, Walter Bright wrote:Dang, I gotta argue this with you too? :-) Forget the philosophical argument for the moment and let's try a pragmatic one - code that doesn't check its arguments and doesn't throw is going to inline better, run faster, and be smaller. To have a high performance language, we have to have this. This is why we have a -release switch: so we can run with or without checking the arguments for bugs. Input data validation is where exceptions come in. Those are relatively rare compared with processing validated data, and so separating out the slow data validation path from the validated high speed path makes pragmatic sense. Rejecting this approach throws (!) out the whole concept of contract programming.I believe this is solving the problem in the wrong place. The function being called should not be throwing, and should be defined as not throwing. See my other posts in this thread on the topic.I think a stance of unceremoniously halting the program upon passing the wrong parameters to a standard library function is a bit excessive.
Nov 18 2013
On 11/18/13 2:49 PM, Walter Bright wrote:On 11/18/2013 2:37 PM, Andrei Alexandrescu wrote:Yes. I agree with your fundamental point but it is missing important nuances. I destroyed the misunderstanding in http://goo.gl/3xD5Aj. AndreiOn 11/18/13 1:50 PM, Walter Bright wrote:Dang, I gotta argue this with you too? :-)I believe this is solving the problem in the wrong place. The function being called should not be throwing, and should be defined as not throwing. See my other posts in this thread on the topic.I think a stance of unceremoniously halting the program upon passing the wrong parameters to a standard library function is a bit excessive.
Nov 18 2013
On 11/18/2013 2:52 PM, Andrei Alexandrescu wrote:Yes. I agree with your fundamental point but it is missing important nuances. I destroyed the misunderstanding in http://goo.gl/3xD5Aj.And I destroyed your destruction! Anyhow, can you use real links rather than goo ones?
Nov 18 2013
19-Nov-2013 02:49, Walter Bright пишет:On 11/18/2013 2:37 PM, Andrei Alexandrescu wrote:There is a whole class of functions where validation is ~ the same amount of work as scrubbing the input. Do I need to provide an example of parser? :) -- Dmitry OlshanskyOn 11/18/13 1:50 PM, Walter Bright wrote:Dang, I gotta argue this with you too? :-) Forget the philosophical argument for the moment and let's try a pragmatic one - code that doesn't check its arguments and doesn't throw is going to inline better, run faster, and be smaller. To have a high performance language, we have to have this. This is why we have a -release switch: so we can run with or without checking the arguments for bugs. Input data validation is where exceptions come in. Those are relatively rare compared with processing validated data, and so separating out the slow data validation path from the validated high speed path makes pragmatic sense. Rejecting this approach throws (!) out the whole concept of contract programming.I believe this is solving the problem in the wrong place. The function being called should not be throwing, and should be defined as not throwing. See my other posts in this thread on the topic.I think a stance of unceremoniously halting the program upon passing the wrong parameters to a standard library function is a bit excessive.
Nov 19 2013
On 11/18/13 1:56 AM, Daniel Murphy wrote:"Andrei Alexandrescu" <SeeWebsiteForEmail erdani.org> wrote in message news:l6ccae$2cmt$1 digitalmars.com...That's fine. What the compiler should do for a nothrow function goes as follows. Consider: void fun() nothrow { statementsA scope(failure) statement statementsB } First off, statementsA must not throw. Then, if statementsB may throw, then look at what statement throws. If statement always throws an Error, then accept the code. This is because the scope(failure) effectively translates whatever exception into an Error, and it's legal for a nothrow function to throw Error. Currently it looks like the presence of scope(failure) simply makes the function seem legit, no matter what. void fun() nothrow { scope(failure) {} throw new Exception("so sue me"); } Andrei1. Fix scope(failure) and then use it.scope(failure) picks up Errors as well as Exceptions
Nov 18 2013
"Andrei Alexandrescu" <SeeWebsiteForEmail erdani.org> wrote in message news:l6d9ki$ok6$1 digitalmars.com...First off, statementsA must not throw. Then, if statementsB may throw, then look at what statement throws. If statement always throws an Error, then accept the code. This is because the scope(failure) effectively translates whatever exception into an Error, and it's legal for a nothrow function to throw Error.Yes.Currently it looks like the presence of scope(failure) simply makes the function seem legit, no matter what. void fun() nothrow { scope(failure) {} throw new Exception("so sue me"); }Yes, obviously a bug. The compiler is missing the rethrow. My point was only that try-catch(Exception) and scope(failure) do not have the same semantics.
Nov 18 2013
Am Tue, 19 Nov 2013 04:00:56 +1100 schrieb "Daniel Murphy" <yebblies nospamgmail.com>:"Andrei Alexandrescu" <SeeWebsiteForEmail erdani.org> wrote in message news:l6d9ki$ok6$1 digitalmars.com...That's severe. It's not even a rethrow, but an independent throw statement sitting squarely in a nothrow function. -- MarcoCurrently it looks like the presence of scope(failure) simply makes the function seem legit, no matter what. void fun() nothrow { scope(failure) {} throw new Exception("so sue me"); }Yes, obviously a bug. The compiler is missing the rethrow.
Nov 18 2013
On 11/18/13 9:00 AM, Daniel Murphy wrote:https://d.puremagic.com/issues/show_bug.cgi?id=11542void fun() nothrow { scope(failure) {} throw new Exception("so sue me"); }Yes, obviously a bug. The compiler is missing the rethrow.My point was only that try-catch(Exception) and scope(failure) do not have the same semantics.Now I got it, thanks. Far as I can tell in this case it doesn't make a big difference, is that correct? a) with try/catch: Exceptions are transformed in AssertError, Errors just go through b) with scope(failure): everything is transformed in AssertError. There is a difference, but both behaviors would be acceptable. Andrei
Nov 18 2013
As I understand, nothrow is used for minor optimization of exception handling, which doesn't help much if you hide mount of throwing code up the stack.
Nov 18 2013
On 11/17/2013 10:32 PM, Andrei Alexandrescu wrote:4. ...?Revisit why removeUnitsFromHNSecs, etc., are throwing. Since the caller is guaranteeing they don't throw, I suspect the runtime data validation is itself invalid and the throws should be replaced with asserts.
Nov 18 2013
On 2013-11-18 06:32:46 +0000, Andrei Alexandrescu said:1. Fix scope(failure) and then use it. AndreiHuh? Scope failure has no purpose here. It does not CATCH the exception and prevent it from bubbling up the call chain. Try/catch does do this. -Shammah
Nov 21 2013
On Friday, 22 November 2013 at 01:49:11 UTC, Shammah Chancellor wrote:On 2013-11-18 06:32:46 +0000, Andrei Alexandrescu said:It does if you return from the scope(failure) block. The problem is you cannot mark the function as "nothrow" For example: --- void throwingFunction() { throw new Exception("damn!"); } void someFunc() // nothrow { scope(failure) { writeln("Failed in someFunc()"); return; } throwingFunction(); } void main() { try { someFunc(); writeln("Yay, someFunc() is nothrow"); } catch(Exception e) { writeln("An exception in main!"); } } Output: Failed in someFunc() Yay, someFunc() is nothrow. --- But you cannot mark someFunc() as nothrow.1. Fix scope(failure) and then use it. AndreiHuh? Scope failure has no purpose here. It does not CATCH the exception and prevent it from bubbling up the call chain. Try/catch does do this. -Shammah
Nov 21 2013
On 11/21/13 6:07 PM, growler wrote:On Friday, 22 November 2013 at 01:49:11 UTC, Shammah Chancellor wrote:I was thinking that scope(failure) could throw an Error. AndreiOn 2013-11-18 06:32:46 +0000, Andrei Alexandrescu said:It does if you return from the scope(failure) block. The problem is you cannot mark the function as "nothrow"1. Fix scope(failure) and then use it. AndreiHuh? Scope failure has no purpose here. It does not CATCH the exception and prevent it from bubbling up the call chain. Try/catch does do this. -Shammah
Nov 21 2013
On Friday, 22 November 2013 at 02:17:18 UTC, Andrei Alexandrescu wrote:On 11/21/13 6:07 PM, growler wrote:Yes, throwing an error makes more sensee than just return;On Friday, 22 November 2013 at 01:49:11 UTC, Shammah Chancellor wrote:I was thinking that scope(failure) could throw an Error. AndreiOn 2013-11-18 06:32:46 +0000, Andrei Alexandrescu said:It does if you return from the scope(failure) block. The problem is you cannot mark the function as "nothrow"1. Fix scope(failure) and then use it. AndreiHuh? Scope failure has no purpose here. It does not CATCH the exception and prevent it from bubbling up the call chain. Try/catch does do this. -Shammah
Nov 21 2013
On 2013-11-22 02:07:36 +0000, growler said:On Friday, 22 November 2013 at 01:49:11 UTC, Shammah Chancellor wrote:What!? That shouldn't even be legal code! See below for why: void throwingFunction() { throw new Exception("damn!"); } void someFunc() // nothrow { scope(failure) { writeln("What?");} <-- NEVER EXECUTED?! scope(failure) { writeln("Failed in someFunc()"); return; } throwingFunction(); } void main() { try { someFunc(); writeln("Yay, someFunc() is nothrow"); } catch(Exception e) { writeln("An exception in main!"); } }On 2013-11-18 06:32:46 +0000, Andrei Alexandrescu said:It does if you return from the scope(failure) block. The problem is you cannot mark the function as "nothrow" For example: --- void throwingFunction() { throw new Exception("damn!"); } void someFunc() // nothrow { scope(failure) { writeln("Failed in someFunc()"); return; } throwingFunction(); } void main() { try { someFunc(); writeln("Yay, someFunc() is nothrow"); } catch(Exception e) { writeln("An exception in main!"); } } Output: Failed in someFunc() Yay, someFunc() is nothrow. --- But you cannot mark someFunc() as nothrow.1. Fix scope(failure) and then use it. AndreiHuh? Scope failure has no purpose here. It does not CATCH the exception and prevent it from bubbling up the call chain. Try/catch does do this. -Shammah
Nov 21 2013
On Friday, 22 November 2013 at 02:35:40 UTC, Shammah Chancellor wrote: ...... Maybe that's why it is considered broken? The first point was to 'fix' scope(failure) and then use it. However, I don't know I haven't been following the discussion closely enough...What!? That shouldn't even be legal code! See below for why:
Nov 21 2013
On 2013-11-22 02:51:11 +0000, growler said:Maybe that's why it is considered broken? The first point was to 'fix' scope(failure) and then use it. However, I don't know I haven't been following the discussion closely enough...I think fixing it means disallowing return and assert(0) here. Since these things generate unreachable code. If it's fixed in the way I think it should be, it still wouldn't be useful here. The proposed fix was to make it so scope(failure) assert(0); Is detected as preventing the exception, so functions could be marked nothrow. I don't think that's a good idea for the above reason. I'd rather see Walter's proposed fix (http://forum.dlang.org/thread/l6ccae$2cmt$1 digitalmars.com?page=3#post-l6ds2b:241ii3:24 :40digitalmars.com) of std.datetime, and my proposed fix for scope(failure). -Shammah
Nov 22 2013
On Friday, 22 November 2013 at 02:35:40 UTC, Shammah Chancellor wrote:void someFunc() // nothrow { scope(failure) { writeln("What?");} <-- NEVER EXECUTED?! scope(failure) { writeln("Failed in someFunc()"); return; } throwingFunction(); }That does exactly as expected: void someFunc() { try { try { throwingFunction(); } catch { writeln("Failed in someFunc()"); return; } } catch { writeln("What?"); } }
Nov 21 2013
On 2013-11-22 03:03:15 +0000, Jesse Phillips said:On Friday, 22 November 2013 at 02:35:40 UTC, Shammah Chancellor wrote:No. scope(failure) is supposed to re-throw the exception. Remember that the original proposal for scope(failure), etc. were using a stack of delegates. The rewriting to try/catch is new and supposed to be functionally equivalent. Return's from scope statements should be disallowed now.void someFunc() // nothrow { scope(failure) { writeln("What?");} <-- NEVER EXECUTED?! scope(failure) { writeln("Failed in someFunc()"); return; } throwingFunction(); }That does exactly as expected: void someFunc() { try { try { throwingFunction(); } catch { writeln("Failed in someFunc()"); return; } } catch { writeln("What?"); } }
Nov 22 2013
On Friday, 22 November 2013 at 21:48:14 UTC, Shammah Chancellor wrote: [snip]No. scope(failure) is supposed to re-throw the exception.Where does it say that? http://dlang.org/statement.html#ScopeGuardStatement "... scope(failure) executes NonEmptyOrScopeBlockStatement when the scope exits due to exception unwinding. ..." [snip]Return's from scope statements should be disallowed now.I disagree. You cannot return or throw from a scope(exit) or scope(success) for obvious reasons, but how a program behaves when a scope fails should left to the programmer. I might be at a DLL or C/C++ boundary and any exceptions (Throwable or otherwise) I do not want to escape out of the current scope. I know I could use try-catch, but I find scope is just much easier to read. Cheers G.
Nov 22 2013
On 11/22/13 5:37 PM, growler wrote:On Friday, 22 November 2013 at 21:48:14 UTC, Shammah Chancellor wrote: [snip]It's implied. The scope is already being exited by means of an exception. If scope(failure) changed anything, _that_ would have been documented. Anyhow we can be clearer in the docs. AndreiNo. scope(failure) is supposed to re-throw the exception.Where does it say that? http://dlang.org/statement.html#ScopeGuardStatement "... scope(failure) executes NonEmptyOrScopeBlockStatement when the scope exits due to exception unwinding. ..."
Nov 22 2013
On Saturday, 23 November 2013 at 01:48:28 UTC, Andrei Alexandrescu wrote:On 11/22/13 5:37 PM, growler wrote:I stand corrected, cheers.On Friday, 22 November 2013 at 21:48:14 UTC, Shammah Chancellor wrote: [snip]It's implied. The scope is already being exited by means of an exception. If scope(failure) changed anything, _that_ would have been documented. Anyhow we can be clearer in the docs. AndreiNo. scope(failure) is supposed to re-throw the exception.Where does it say that? http://dlang.org/statement.html#ScopeGuardStatement "... scope(failure) executes NonEmptyOrScopeBlockStatement when the scope exits due to exception unwinding. ..."
Nov 22 2013
On Friday, 22 November 2013 at 01:49:11 UTC, Shammah Chancellor wrote:On 2013-11-18 06:32:46 +0000, Andrei Alexandrescu said:If the code is: scope(failure) assert(0); Then it is a statement that the function doesn't throw. So there isn't a need to catch the exception, it is merely a way to state to the compiler "I've verified I know what I'm talking about and this function really doesn't ever throw." Right now just having scope(failure) in the body is making this statement, and that is wrong.1. Fix scope(failure) and then use it. AndreiHuh? Scope failure has no purpose here. It does not CATCH the exception and prevent it from bubbling up the call chain. Try/catch does do this. -Shammah
Nov 21 2013
On 2013-11-22 02:13:42 +0000, Jesse Phillips said:On Friday, 22 November 2013 at 01:49:11 UTC, Shammah Chancellor wrote:Scope failure blocks should not be able to raise errors, or return except "out the end".On 2013-11-18 06:32:46 +0000, Andrei Alexandrescu said:If the code is: scope(failure) assert(0); Then it is a statement that the function doesn't throw. So there isn't a need to catch the exception, it is merely a way to state to the compiler "I've verified I know what I'm talking about and this function really doesn't ever throw." Right now just having scope(failure) in the body is making this statement, and that is wrong.1. Fix scope(failure) and then use it. AndreiHuh? Scope failure has no purpose here. It does not CATCH the exception and prevent it from bubbling up the call chain. Try/catch does do this. -Shammah
Nov 22 2013