digitalmars.D - Why exceptions for error handling is so important
- Walter Bright (22/22) Jan 11 2015 Over the last few days, I have been getting weird errors from various pr...
- Walter Bright (3/9) Jan 11 2015 BTW, the error code I got from Windows Backup is 0x80070002. Backup's su...
- Robert burner Schadek (2/2) Jan 11 2015 to not let ranges succumb to such a problem I wrote:
- Dicebot (4/4) Jan 11 2015 What is your opinion of approach advertised by various functional
- ketmar via Digitalmars-d (9/13) Jan 11 2015 from my POV it trashes logic with error checking. hey, i don't care if
- thedeemon (8/19) Jan 11 2015 This is where monads and applicatives shine. You can describe the
- Andrej Mitrovic via Digitalmars-d (4/8) Jan 11 2015 Or a @noignore attribute. Just having that alone could easily catch
- Andrej Mitrovic via Digitalmars-d (2/5) Jan 11 2015 Or perhaps to avoid keyword/attribute bloat, a -noignore switch.
- ketmar via Digitalmars-d (7/16) Jan 11 2015 `if (myfunc()) {}` ;-) it's still easier than
- Walter Bright (12/15) Jan 11 2015 It's a great question. I have a lot of experience with error codes, and ...
- =?UTF-8?Q?Tobias=20M=C3=BCller?= (8/30) Jan 11 2015 - Error codes are automatically ignored
- Walter Bright (2/9) Jan 11 2015 I don't think this is an answer to my point.
- =?UTF-8?Q?Tobias=20M=C3=BCller?= (8/21) Jan 12 2015 I thought that your question was a rhetorical one and I agree to a certa...
- weaselcat (4/45) Jan 11 2015 There's nothing stopping you from just throwing errors out in
- =?UTF-8?Q?Tobias=20M=C3=BCller?= (5/18) Jan 12 2015 But you have to do so explicitly. You cannot just remove every possibili...
- Tobias Pankrath (3/16) Jan 11 2015 Here is one approach to it:
- Walter Bright (7/11) Jan 12 2015 I don't fully understand it, but it appears to require that each compone...
- bearophile (5/7) Jan 12 2015 Apparently if the language syntax is designed for that style of
- "Ola Fosheim =?UTF-8?B?R3LDuHN0YWQi?= (2/8) Jan 12 2015 Yes, dataflow.
- Paulo Pinto (6/22) Jan 12 2015 Not really, after you have the library that can handle all code
- Tobias Pankrath (6/21) Jan 12 2015 As far as I understand is, it requires each component to settle
- Walter Bright (2/8) Jan 12 2015 Or we could just use exceptions, which require none of that.
- Tobias Pankrath (10/23) Jan 12 2015 Well, yes. I don't think though, that either exception or
- Dicebot (9/22) Jan 12 2015 .. and have many other issues instead :)
- Walter Bright (9/16) Jan 12 2015 For me the primary advantage of EH is put the code to deal with the erro...
- "Ola Fosheim =?UTF-8?B?R3LDuHN0YWQi?= (2/5) Jan 12 2015 Does this mean that D will get fast EH?
- Adam D. Ruppe (3/4) Jan 12 2015 It is fast already...
- "Ola Fosheim =?UTF-8?B?R3LDuHN0YWQi?= (4/8) Jan 12 2015 What makes you say that? Doesn't D still use the standard
- Matthias Bentrup (8/18) Jan 12 2015 The advantage of return code / union type error handling is that
- "Ola Fosheim =?UTF-8?B?R3LDuHN0YWQi?= (3/5) Jan 12 2015 It is in a register...
- Adam D. Ruppe (9/12) Jan 12 2015 try/throw/catch is like 50x slower than doing nothing except
- "Ola Fosheim =?UTF-8?B?R3LDuHN0YWQi?= (14/26) Jan 12 2015 Right, I kinda think that C++ EH is awful outside the context of
- H. S. Teoh via Digitalmars-d (15/24) Jan 12 2015 It's a lot better now, but D exceptions used to be orders of magnitude
- =?UTF-8?Q?Tobias=20M=C3=BCller?= (7/15) Jan 12 2015 But what's exceptional for you is normal for me.
- H. S. Teoh via Digitalmars-d (12/29) Jan 12 2015 And what exactly should operator[] return if a key wasn't found?
- "Ola Fosheim =?UTF-8?B?R3LDuHN0YWQi?= (31/42) Jan 12 2015 Yes, and then think about the original foundation for exceptions.
- =?UTF-8?Q?Tobias=20M=C3=BCller?= (11/27) Jan 12 2015 Just stick with one.
- Russel Winder via Digitalmars-d (10/13) Jan 13 2015 Callee always has the responsibility, it is just a question of whether
- Russel Winder via Digitalmars-d (12/15) Jan 12 2015 […]
- Andrei Alexandrescu (3/12) Jan 12 2015 How can you like undefined? That's a terrible solution again underlining...
- Russel Winder via Digitalmars-d (11/28) Jan 13 2015 They may be fiddling with type systems, but I was wrong about
- Paulo Pinto (6/25) Jan 13 2015 They posted recently a blog post about error handling in Go.
- Atila Neves (3/7) Jan 14 2015 That post, to me, only reinforces how much better using
- Paulo Pinto (4/13) Jan 14 2015 Agree, on the other hand is another example of the mindset.
- Jeremy Powers via Digitalmars-d (4/18) Jan 14 2015 It is interesting to me that as people come up with ways to make error-c...
- H. S. Teoh via Digitalmars-d (5/29) Jan 14 2015 +1.
- ketmar via Digitalmars-d (4/7) Jan 14 2015 great article! it clearly shows how NOT to do error handling and why
- Tobias Pankrath (4/17) Jan 12 2015 That's basically the same as a pointer to the value. If the
- Walter Bright (4/7) Jan 12 2015 That's just putting the responsibility of array bounds checking on the c...
- Russel Winder via Digitalmars-d (16/28) Jan 13 2015 Why not?
- Walter Bright (9/31) Jan 13 2015 My point was more to the issue of how is array bounds checking done in t...
- Walter Bright (7/11) Jan 12 2015 It's not a subjective truth, it's an objective fact.
- Russel Winder via Digitalmars-d (9/18) Jan 13 2015 Python.
- "Tobias =?UTF-8?B?TcO8bGxlciI=?= <troplin bluewin.ch> (7/20) Jan 15 2015 But what this means that exceptions should only ever be used for
- H. S. Teoh via Digitalmars-d (27/48) Jan 15 2015 No, that's wrong. Programming errors are caught by assertions, not
- Tobias M (22/29) Jan 15 2015 But for almost every environmental error, there's a use case
- "Ola Fosheim =?UTF-8?B?R3LDuHN0YWQi?= (25/33) Jan 15 2015 Exactly. Just think about validation of user input. Is it a
- deadalnix (14/31) Jan 15 2015 auto create_unique_file() {
- "Marc =?UTF-8?B?U2Now7x0eiI=?= <schuetzm gmx.net> (8/21) Jan 16 2015 IMO, this example just shows that it's a bad idea to follow the
- Tobias Pankrath (8/15) Jan 16 2015 1. Exception are too slow
- Adam D. Ruppe (5/8) Jan 12 2015 Yea, it was (I did it myself for posix, someone else did it on
- deadalnix (4/12) Jan 12 2015 Can you elaborate on the technical details of that stunt ? When
- "Ola Fosheim =?UTF-8?B?R3LDuHN0YWQi?= (14/19) Jan 12 2015 I'm sorry, but this is just a crappy excuse invented to defend
- ketmar via Digitalmars-d (3/4) Jan 12 2015 (banging his head against a wall) NO. THIS NEVER HAS ANY SENSE.
- "Ola Fosheim =?UTF-8?B?R3LDuHN0YWQi?= (4/8) Jan 12 2015 Sure it has. It is a state machine. You cannot not return state.
- Walter Bright (8/12) Jan 12 2015 I've never encountered any IEEE FP code that ever, and I mean ever, chec...
- "Ola Fosheim =?UTF-8?B?R3LDuHN0YWQi?= (12/15) Jan 12 2015 I am bringing up exception-handling as what it is. Handling an
- deadalnix (7/18) Jan 12 2015 No, Exception are a bail out mechanism. It is the, I have no idea
- "Ola Fosheim =?UTF-8?B?R3LDuHN0YWQi?= (21/26) Jan 12 2015 But exceptions are control flow. There is no such thing as
- "Marc =?UTF-8?B?U2Now7x0eiI=?= <schuetzm gmx.net> (4/32) Jan 13 2015 I usually don't do this, but I really need to post a "+1" here:
- deadalnix (2/40) Jan 13 2015 Too bad you chose to do this on a bad strawmman.
- "Marc =?UTF-8?B?U2Now7x0eiI=?= <schuetzm gmx.net> (10/52) Jan 14 2015 Your claims:
- deadalnix (20/29) Jan 14 2015 Being precise is important.
- "Ola Fosheim =?UTF-8?B?R3LDuHN0YWQi?= (9/14) Jan 14 2015 No, "201" means success, resource created, and "409" means that
- "Marc =?UTF-8?B?U2Now7x0eiI=?= <schuetzm gmx.net> (22/55) Jan 15 2015 Indeed I was assuming that the code handling the HTTP request
- "Ola Fosheim =?UTF-8?B?R3LDuHN0YWQi?= (18/25) Jan 15 2015 FWIW, although the framework I use provides exceptions for
- Dmitry Olshansky (13/35) Jan 13 2015 Actually I agree - exceptions are mechanism. Stressing _exceptional_ as...
- Russel Winder via Digitalmars-d (10/15) Jan 12 2015 […]
- Walter Bright (2/3) Jan 12 2015 Nobody would be in this forum if we preferred Python :-)
- Russel Winder via Digitalmars-d (13/18) Jan 13 2015 Why does one have to "prefer" a language to the exclusion of all
- John Colvin (7/20) Jan 13 2015 I've just seen that PyD has moved to github[1] and has had a
- Russel Winder via Digitalmars-d (20/26) Jan 13 2015 The standard Go development team response to the "we must have dynamic
- Paulo Pinto (7/25) Jan 13 2015 Which doesn't scale in software that requires plugins.
- Russel Winder via Digitalmars-d (14/17) Jan 13 2015 Probably true.
- "Ola Fosheim =?UTF-8?B?R3LDuHN0YWQi?= (10/22) Jan 13 2015 I have found that Python quite nicely can replace perl, php,
- Paulo Pinto (8/25) Jan 13 2015 As long as it is only used for scripting nothing.
- "Ola Fosheim =?UTF-8?B?R3LDuHN0YWQi?= (19/23) Jan 13 2015 But you don't need everything in an application to be fast, so
- Paulo Pinto (12/36) Jan 13 2015 Sure, but JavaScript enjoys some of the best JIT compilers for
- "Ulrich =?UTF-8?B?S8O8dHRsZXIi?= <kuettler gmail.com> (9/19) Jan 13 2015 You are doing Python a disservice here. Their use of exceptions
- Russel Winder via Digitalmars-d (23/28) Jan 13 2015 I think you miss my point. The thread appeared to be assuming the use of
- deadalnix (3/13) Jan 12 2015 LDC, GDC and SDC do all use this.
- John Colvin (4/17) Jan 12 2015 Interesting little rant about exceptions (and more), from the
- deadalnix (5/23) Jan 12 2015 Exception in C++ is different. It is full of pitfalls and
- H. S. Teoh via Digitalmars-d (6/13) Jan 12 2015 I used to throw char* in C++. 'Nuff said. :-)
- Paulo Pinto (11/36) Jan 12 2015 Back when C++ got exceptions I never understood why so much paper
- "Ola Fosheim =?UTF-8?B?R3LDuHN0YWQi?= (10/13) Jan 12 2015 Yeah, C++ exceptions were originally so troubled that no sane
- bearophile (9/12) Jan 12 2015 I don't agree. The basic ideas of STL by Alexander Stepanov are
- "Ola Fosheim =?UTF-8?B?R3LDuHN0YWQi?= (17/23) Jan 12 2015 I've tried to like STL for 17 years, and whenever speed and clear
- bearophile (13/19) Jan 13 2015 Take a look at the ideas of "C++ seasoning"
- "Ola Fosheim =?UTF-8?B?R3LDuHN0YWQi?= (32/45) Jan 13 2015 Yes... you can do that. For little gain since C++'s support for
- Paulo Pinto (7/19) Jan 13 2015 Lets not forget Alexander Stepanov created STL for Ada
- Martin Nowak (11/23) Jan 12 2015 The general solution in functional programming is error chaining.
- Walter Bright (6/14) Jan 12 2015 Yes, it still appears to be just a wrapper around returning two values, ...
- Dicebot (3/7) Jan 12 2015 In server applications there is no such thing as a rare case
- Walter Bright (2/4) Jan 12 2015 Don't use exceptions for normal operations.
- Dicebot (3/8) Jan 12 2015 Which is equivalent to "don't use exceptions on servers" :) Yes,
- Martin Nowak (5/8) Jan 12 2015 I think error handling chains like Maybe!(Result) or
- bearophile (10/16) Jan 12 2015 I suggest to start inverting your point of view: try to look why
- Andrei Alexandrescu (2/13) Jan 12 2015 I can't believe I agree with everything bearophile just said :o). -- And...
- Martin Nowak (5/7) Jan 12 2015 But we knew that already.
- Martin Nowak (11/31) Jan 12 2015 Yes, you wrap the return values, but it doesn't require to deal
- Walter Bright (3/11) Jan 12 2015 Yes, it does. Returning an int in EAX now becomes returning a pair [EAX,...
- deadalnix (4/6) Jan 12 2015 It is not that big of a deal, EDX is a trash register anyway if
- H. S. Teoh via Digitalmars-d (7/14) Jan 12 2015 On the contrary, it's an extremely big deal. Registers are a rare
- deadalnix (5/23) Jan 12 2015 These are trash register. Meaning the callee can put whatever in
- Walter Bright (7/10) Jan 12 2015 1. the register must be assigned a value - that has a cost
- "Ola Fosheim =?UTF-8?B?R3LDuHN0YWQi?= (10/21) Jan 13 2015 xor on Haswell has a reciprocal throughput of 0.25, meaning
- "Ola Fosheim =?UTF-8?B?R3LDuHN0YWQi?= (12/12) Jan 13 2015 Of course the best solution might be to just implement reasonably
- deadalnix (9/17) Jan 13 2015 Exception can bubble from one thread to another.
- "Ola Fosheim =?UTF-8?B?R3LDuHN0YWQi?= (8/24) Jan 13 2015 How? If you are thinking coroutines, then the exception can be
- deadalnix (11/39) Jan 13 2015 http://dlang.org/phobos/core_thread.html#.Thread.join
- "Ola Fosheim =?UTF-8?B?R3LDuHN0YWQi?= (25/42) Jan 13 2015 I don't see the problem. I'm suggesting value semantics, it can
- deadalnix (16/35) Jan 13 2015 Then you can't catch by super class. This is not going to fly.
- "Ola Fosheim =?UTF-8?B?R3LDuHN0YWQi?= (16/42) Jan 13 2015 I said value. Use bitmasks. Class hierarchies don't work very
- "Ola Fosheim =?UTF-8?B?R3LDuHN0YWQi?= (7/7) Jan 14 2015 Just to be clear on this. Sandy Bridge and later have 16 x 256
- deadalnix (17/50) Jan 14 2015 Once again, no specifics.
- "Ola Fosheim =?UTF-8?B?R3LDuHN0YWQi?= (18/38) Jan 14 2015 Then you should also be able to explain where you got lost.
- "Marc =?UTF-8?B?U2Now7x0eiI=?= <schuetzm gmx.net> (4/9) Jan 14 2015 Without addressing anything else: You want moving here, not
- "Ola Fosheim =?UTF-8?B?R3LDuHN0YWQi?= (4/6) Jan 14 2015 How can you move a value, it has no identity?
- "Marc =?UTF-8?B?U2Now7x0eiI=?= <schuetzm gmx.net> (7/12) Jan 15 2015 When you say "copy", I expect that to mean "do a bitwise copy and
- "Ola Fosheim =?UTF-8?B?R3LDuHN0YWQi?= (13/18) Jan 15 2015 Ah, yes, I was not thinking current D compiler internals, just
- Walter Bright (5/9) Jan 12 2015 Returning a slice, for example, already consumes EAX and EDX. Adding an ...
- deadalnix (3/20) Jan 12 2015 So we agree :)
- "Marc =?UTF-8?B?U2Now7x0eiI=?= <schuetzm gmx.net> (8/21) Jan 13 2015 There are techniques to mitigate that. For example, Rust is smart
- Walter Bright (3/20) Jan 13 2015 Such mitigation techniques pretty much confirms there's an efficiency co...
- Martin Nowak (3/7) Jan 12 2015 If you start to discuss register allocation than the actual cost
- "Tobias =?UTF-8?B?TcO8bGxlciI=?= <troplin bluewin.ch> (21/26) Jan 15 2015 There's another reason why this is not that bad (under the
- deadalnix (2/4) Jan 15 2015 BWAHAHAHAHAHAHAHAHAHAHAHAHAHAHAHAHAHAHAHAHAHAHAHAHA !
- "Ola Fosheim =?UTF-8?B?R3LDuHN0YWQi?= (3/8) Jan 15 2015 'tis true, unfortunately implementations don't match the spec...
- ketmar via Digitalmars-d (5/17) Jan 16 2015 so human must do all machine wants from him, until that machine is
- deadalnix (15/19) Jan 12 2015 Rust has an approach very similar to exception, but they do not
- "Ola Fosheim =?UTF-8?B?R3LDuHN0YWQi?= (4/14) Jan 12 2015 It is difficult to introduce exceptions without causing problems
- Dicebot (5/24) Jan 12 2015 Exceptions in Rust are more like Errors in D (but they terminate
- "Marc =?UTF-8?B?U2Now7x0eiI=?= <schuetzm gmx.net> (4/14) Jan 13 2015 I think it does unwind (i.e. call destructors), but the exception
- Iain Buclaw via Digitalmars-d (5/18) Jan 12 2015 Up to this point, I you reminded me of a talk by Joe Armstrong
- Walter Bright (9/12) Jan 12 2015 I've found another problem with Windows Moviemaker, it can read, but can...
- Andrej Mitrovic via Digitalmars-d (4/7) Jan 12 2015 If you haven't read this I highly recommend it, just for the
- Walter Bright (4/7) Jan 12 2015 Epic! But we all have such issues. And it ain't just software, either, e...
- H. S. Teoh via Digitalmars-d (89/97) Jan 12 2015 While I agree with the general sentiment, I think the current convention
- Jacob Carlborg (51/135) Jan 13 2015 I prefer a semantically driven hierarchy. I agree with you that the user...
- Walter Bright (6/8) Jan 13 2015 Interesting that this article just appeared:
- Meta (3/12) Jan 13 2015 But checked exceptions are definitely not.
- deadalnix (3/20) Jan 13 2015 http://www.artima.com/intv/handcuffs.html
- Jeremy Powers via Digitalmars-d (16/18) Jan 13 2015 False.
- deadalnix (8/19) Jan 13 2015 Full stop. I made that mistake myself various time, so I can talk
- Jeremy Powers via Digitalmars-d (10/31) Jan 14 2015 You are completely right. Let me rephrase:
Over the last few days, I have been getting weird errors from various programs I run on Windows 7. Programs would just fail, or produce corrupt output (I'm looking at you, Windows Moviemaker). I have a 128Gb SSD drive for my system drive, for speed, and a 4T secondary spinning drive for storage. I knew I was getting nearly out of space on the SSD drive, and had a 256Gb SSD drive ready to swap in. To migrate the system to the new disk, the idea was to use Windows Backup/Restore of the image. Unfortunately, Windows Backup would get a ways through the process, then fail with no message beyond an error code. Googling the error code produced a vast number of results, and various schemes for fixing it, some official, some not. None of them were applicable. Many involved loading new drivers, editting the system registry, and all sort of unappealing "solutions". Then I thought, hmm, maybe it's running out of space for temporary files. I deleted a few gigs, tried again, and it worked fine! I transferred to the new disk, had lots of free gigs, and suddenly the various programs that were experiencing mysterious failures all started working properly. What I'm pretty sure is happening is those programs use error codes for error reporting, and then don't check the error codes. This is common practice for C code. I'm a little surprised that with Windows' long history, it still has problems detecting when it runs out of disk space. However, if exceptions are thrown for errors instead, the programmer has to deliberately add code if he wishes to ignore the error.
Jan 11 2015
On 1/11/2015 2:48 AM, Walter Bright wrote:To migrate the system to the new disk, the idea was to use Windows Backup/Restore of the image. Unfortunately, Windows Backup would get a ways through the process, then fail with no message beyond an error code. Googling the error code produced a vast number of results, and various schemes for fixing it, some official, some not. None of them were applicable. Many involved loading new drivers, editting the system registry, and all sort of unappealing "solutions".BTW, the error code I got from Windows Backup is 0x80070002. Backup's suggestion for what to do next was to run it again (!). Geez.
Jan 11 2015
to not let ranges succumb to such a problem I wrote: https://github.com/D-Programming-Language/phobos/pull/2724
Jan 11 2015
What is your opinion of approach advertised by various functional languages and now also Rust? Where you return error code packed with actual data and can't access data without visiting error code too, compiler simply won't allow it.
Jan 11 2015
On Sun, 11 Jan 2015 13:06:26 +0000 Dicebot via Digitalmars-d <digitalmars-d puremagic.com> wrote:What is your opinion of approach advertised by various functional=20 languages and now also Rust? Where you return error code packed=20 with actual data and can't access data without visiting error=20 code too, compiler simply won't allow it.from my POV it trashes logic with error checking. hey, i don't care if *each* `fwrite()` is successfull, i only care if all of them are ok or at least one (any one) failed! by the way: how many peope put checks for `close()`/`fclose()` return value? it returns no actual data, so why bother... i believe that exceptions are better for error checking: it's hard to ignore errors, error handling logic is clearly separated and so on.
Jan 11 2015
On Sunday, 11 January 2015 at 13:25:59 UTC, ketmar via Digitalmars-d wrote:On Sun, 11 Jan 2015 13:06:26 +0000 Dicebot via Digitalmars-d <digitalmars-d puremagic.com> wrote:This is where monads and applicatives shine. You can describe the general logic (run 'till first error or collect and combine all errors or something else) in one place and then apply this way of error handling throughout with minimal code, and you can often change the error handling approach later just by changing a type, without editing actual function source code.What is your opinion of approach advertised by various functional languages and now also Rust? Where you return error code packed with actual data and can't access data without visiting error code too, compiler simply won't allow it.from my POV it trashes logic with error checking. hey, i don't care if *each* `fwrite()` is successfull, i only care if all of them are ok or at least one (any one) failed!
Jan 11 2015
On 1/11/15, Dicebot via Digitalmars-d <digitalmars-d puremagic.com> wrote:What is your opinion of approach advertised by various functional languages and now also Rust? Where you return error code packed with actual data and can't access data without visiting error code too, compiler simply won't allow it.Or a noignore attribute. Just having that alone could easily catch the mistake of ignoring a return value. I'm really rooting for this, but is anyone else on board?
Jan 11 2015
On 1/11/15, Andrej Mitrovic <andrej.mitrovich gmail.com> wrote:Or a noignore attribute. Just having that alone could easily catch the mistake of ignoring a return value. I'm really rooting for this, but is anyone else on board?Or perhaps to avoid keyword/attribute bloat, a -noignore switch.
Jan 11 2015
On Sun, 11 Jan 2015 14:32:21 +0100 Andrej Mitrovic via Digitalmars-d <digitalmars-d puremagic.com> wrote:On 1/11/15, Dicebot via Digitalmars-d <digitalmars-d puremagic.com> wrote:`if (myfunc()) {}` ;-) it's still easier than `try myfunc() catch (Exception) {}`. this is a question of "opt-in" versus "opt-out". i believe that ignoring errors should be "opt-in", not vice versa. and unhandled error MUST bomb out the app.What is your opinion of approach advertised by various functional languages and now also Rust? Where you return error code packed with actual data and can't access data without visiting error code too, compiler simply won't allow it.=20 Or a noignore attribute. Just having that alone could easily catch the mistake of ignoring a return value. I'm really rooting for this, but is anyone else on board?
Jan 11 2015
On 1/11/2015 5:06 AM, Dicebot wrote:What is your opinion of approach advertised by various functional languages and now also Rust? Where you return error code packed with actual data and can't access data without visiting error code too, compiler simply won't allow it.It's a great question. I have a lot of experience with error codes, and with exceptions. I have zero with the packed scheme, though that doesn't stop me from having an opinion :-) Perhaps I misunderstand, but given A calls B calls C, A => B => C and C detects an error, and A knows what to do with the error. B then becomes burdened with checking for the error, invoking some sort of cleanup code, and then propagating it. Wouldn't this be uglifying B's source code? With exceptions, C throws, A catches, and B's cleanup happens automatically. This matters very much for pipeline style programming (i.e. ranges and algorithms).
Jan 11 2015
Walter Bright <newshound2 digitalmars.com> wrote:On 1/11/2015 5:06 AM, Dicebot wrote:- Error codes are automatically ignored - Exceptions are automatically propagated IMO both are not ideal and lead to sloppy programming. Ignoring errors is of course worse than aborting where you could have handled the error. Rust-style "packed" errors are nice because you are forced to think about the correct handling.What is your opinion of approach advertised by various functional languages and now also Rust? Where you return error code packed with actual data and can't access data without visiting error code too, compiler simply won't allow it.It's a great question. I have a lot of experience with error codes, and with exceptions. I have zero with the packed scheme, though that doesn't stop me from having an opinion :-) Perhaps I misunderstand, but given A calls B calls C, A => B => C and C detects an error, and A knows what to do with the error. B then becomes burdened with checking for the error, invoking some sort of cleanup code, and then propagating it. Wouldn't this be uglifying B's source code? With exceptions, C throws, A catches, and B's cleanup happens automatically. This matters very much for pipeline style programming (i.e. ranges and algorithms).
Jan 11 2015
On 1/11/2015 11:09 PM, Tobias Müller wrote:- Error codes are automatically ignored - Exceptions are automatically propagated IMO both are not ideal and lead to sloppy programming. Ignoring errors is of course worse than aborting where you could have handled the error. Rust-style "packed" errors are nice because you are forced to think about the correct handling.I don't think this is an answer to my point.
Jan 11 2015
Walter Bright <newshound2 digitalmars.com> wrote:On 1/11/2015 11:09 PM, Tobias Müller wrote:I thought that your question was a rhetorical one and I agree to a certain degree. You can still have automatic cleanup though. Rust has RAII. The point is that I think that trading some prettyness for explicitness is a good thing in that case. But then again I also like checked exceptions (java) that seem to be disliked by most. It a similar tradeoff.- Error codes are automatically ignored - Exceptions are automatically propagated IMO both are not ideal and lead to sloppy programming. Ignoring errors is of course worse than aborting where you could have handled the error. Rust-style "packed" errors are nice because you are forced to think about the correct handling.I don't think this is an answer to my point.
Jan 12 2015
On Monday, 12 January 2015 at 07:09:54 UTC, Tobias Müller wrote:Walter Bright <newshound2 digitalmars.com> wrote:There's nothing stopping you from just throwing errors out in Rust(last I used it anyways) via empty match statements and/or unwrap.On 1/11/2015 5:06 AM, Dicebot wrote:- Error codes are automatically ignored - Exceptions are automatically propagated IMO both are not ideal and lead to sloppy programming. Ignoring errors is of course worse than aborting where you could have handled the error. Rust-style "packed" errors are nice because you are forced to think about the correct handling.What is your opinion of approach advertised by various functional languages and now also Rust? Where you return error code packed with actual data and can't access data without visiting error code too, compiler simply won't allow it.It's a great question. I have a lot of experience with error codes, and with exceptions. I have zero with the packed scheme, though that doesn't stop me from having an opinion :-) Perhaps I misunderstand, but given A calls B calls C, A => B => C and C detects an error, and A knows what to do with the error. B then becomes burdened with checking for the error, invoking some sort of cleanup code, and then propagating it. Wouldn't this be uglifying B's source code? With exceptions, C throws, A catches, and B's cleanup happens automatically. This matters very much for pipeline style programming (i.e. ranges and algorithms).
Jan 11 2015
"weaselcat" <weaselcat gmail.com> wrote:On Monday, 12 January 2015 at 07:09:54 UTC, Tobias Müller wrote:But you have to do so explicitly. You cannot just remove every possibility to ignore errors, because sometimes it is justified. BTW: 'unwrap' panics if it contains an error. But that's not really important here.- Error codes are automatically ignored - Exceptions are automatically propagated IMO both are not ideal and lead to sloppy programming. Ignoring errors is of course worse than aborting where you > could have handled the error. Rust-style "packed" errors are nice because you are forced to > think about the correct handling.There's nothing stopping you from just throwing errors out in Rust(last I used it anyways) via empty match statements and/or unwrap.
Jan 12 2015
On Monday, 12 January 2015 at 00:51:25 UTC, Walter Bright wrote:It's a great question. I have a lot of experience with error codes, and with exceptions. I have zero with the packed scheme, though that doesn't stop me from having an opinion :-) Perhaps I misunderstand, but given A calls B calls C, A => B => C and C detects an error, and A knows what to do with the error. B then becomes burdened with checking for the error, invoking some sort of cleanup code, and then propagating it. Wouldn't this be uglifying B's source code? With exceptions, C throws, A catches, and B's cleanup happens automatically. This matters very much for pipeline style programming (i.e. ranges and algorithms).Here is one approach to it: http://fsharpforfunandprofit.com/posts/recipe-part2/
Jan 11 2015
On 1/11/2015 11:53 PM, Tobias Pankrath wrote:On Monday, 12 January 2015 at 00:51:25 UTC, Walter Bright wrote:I don't fully understand it, but it appears to require that each component have two paths coded into it - the regular path, and the error path, and there has to be adapters for components that only have a regular path. Exceptions means only one path has to be coded. I do understand that the packed error code technique can be made to work, but the inconvenience seems to be high.This matters very much for pipeline style programming (i.e. ranges and algorithms).Here is one approach to it: http://fsharpforfunandprofit.com/posts/recipe-part2/
Jan 12 2015
Walter Bright:I do understand that the packed error code technique can be made to work, but the inconvenience seems to be high.Apparently if the language syntax is designed for that style of functional programming, the "inconvenience" seems to be very low. Bye, bearophile
Jan 12 2015
On Monday, 12 January 2015 at 09:41:08 UTC, bearophile wrote:Walter Bright:Yes, dataflow.I do understand that the packed error code technique can be made to work, but the inconvenience seems to be high.Apparently if the language syntax is designed for that style of functional programming, the "inconvenience" seems to be very low.
Jan 12 2015
On Monday, 12 January 2015 at 09:31:25 UTC, Walter Bright wrote:On 1/11/2015 11:53 PM, Tobias Pankrath wrote:Not really, after you have the library that can handle all code paths, the code just needs to be written using such building blocks[0], as they are quite general. Which is relatively easy in FP first languages. [0] Aka monadic combinatorsOn Monday, 12 January 2015 at 00:51:25 UTC, Walter Bright wrote:I don't fully understand it, but it appears to require that each component have two paths coded into it - the regular path, and the error path, and there has to be adapters for components that only have a regular path. Exceptions means only one path has to be coded. I do understand that the packed error code technique can be made to work, but the inconvenience seems to be high.This matters very much for pipeline style programming (i.e. ranges and algorithms).Here is one approach to it: http://fsharpforfunandprofit.com/posts/recipe-part2/
Jan 12 2015
On Monday, 12 January 2015 at 09:31:25 UTC, Walter Bright wrote:On 1/11/2015 11:53 PM, Tobias Pankrath wrote:As far as I understand is, it requires each component to settle on the same discriminated union that packs the error and result, Now in D we use the opDot to chain components, which works sinceOn Monday, 12 January 2015 at 00:51:25 UTC, Walter Bright wrote:I don't fully understand it, but it appears to require that each component have two paths coded into it - the regular path, and the error path, and there has to be adapters for components that only have a regular path. Exceptions means only one path has to be coded.This matters very much for pipeline style programming (i.e. ranges and algorithms).Here is one approach to it: http://fsharpforfunandprofit.com/posts/recipe-part2/= and >=> which take care of the adaption.
Jan 12 2015
On 1/12/2015 3:02 AM, Tobias Pankrath wrote:As far as I understand is, it requires each component to settle on the same discriminated union that packs the error and result, which he calles Result but Now in D we use the opDot to chain components, which works since we have UFCS. the adaption.Or we could just use exceptions, which require none of that.
Jan 12 2015
On Monday, 12 January 2015 at 11:09:01 UTC, Walter Bright wrote:On 1/12/2015 3:02 AM, Tobias Pankrath wrote:Well, yes. I don't think though, that either exception or "railway" requires more boilerplate than the other. They just offer different strength. The plus side for railway (as compared D exceptions): Easier comprehension of control flow and scope(exit) and scope(failure) are not needed anymore. Using something like this in nogc functions seems easier than making exceptions work by preallocating them.As far as I understand is, it requires each component to settle on the same discriminated union that packs the error and result, which he calles Result but Now in D we use the opDot to chain components, which works since we have UFCS. which take care of the adaption.Or we could just use exceptions, which require none of that.
Jan 12 2015
On Monday, 12 January 2015 at 11:09:01 UTC, Walter Bright wrote:On 1/12/2015 3:02 AM, Tobias Pankrath wrote:.. and have many other issues instead :) Right now my experience from some quick experiments with Rust seems to indicate that it is a good sort inconvenience for medium-to-big applications as it forces you to explicitly consider all possible exceptional paths possible in application without and does not add any runtime overhead. For smaller / less important programs verbosity does seem inconvenient in an unpleasant way indeed.As far as I understand is, it requires each component to settle on the same discriminated union that packs the error and result, which he calles Result but Now in D we use the opDot to chain components, which works since we have UFCS. which take care of the adaption.Or we could just use exceptions, which require none of that.
Jan 12 2015
On 1/12/2015 3:27 AM, Dicebot wrote:.. and have many other issues instead :) Right now my experience from some quick experiments with Rust seems to indicate that it is a good sort inconvenience for medium-to-big applications as it forces you to explicitly consider all possible exceptional paths possible in application without and does not add any runtime overhead. For smaller / less important programs verbosity does seem inconvenient in an unpleasant way indeed.For me the primary advantage of EH is put the code to deal with the error in the place where it is most appropriate to deal with it. With error codes, you have to deal with propagating the errors everywhere. Error codes still require manual insertion of unwinding code, and that can get fairly ugly when dealing with transactions: http://dlang.org/exception-safe.html Scope guard and RAII deal with most of the issues with having correct error recovery.
Jan 12 2015
On Monday, 12 January 2015 at 11:41:06 UTC, Walter Bright wrote:For me the primary advantage of EH is put the code to deal with the error in the place where it is most appropriate to deal with it. With error codes, you have to deal with propagatingDoes this mean that D will get fast EH?
Jan 12 2015
On Monday, 12 January 2015 at 11:43:26 UTC, Ola Fosheim Grøstad wrote:Does this mean that D will get fast EH?It is fast already...
Jan 12 2015
On Monday, 12 January 2015 at 13:25:26 UTC, Adam D. Ruppe wrote:On Monday, 12 January 2015 at 11:43:26 UTC, Ola Fosheim Grøstad wrote:What makes you say that? Doesn't D still use the standard zero-cost EH that was created for Itanium, where you take the performance hit on unwinding?Does this mean that D will get fast EH?It is fast already...
Jan 12 2015
On Monday, 12 January 2015 at 13:54:18 UTC, Ola Fosheim Grøstad wrote:On Monday, 12 January 2015 at 13:25:26 UTC, Adam D. Ruppe wrote:The advantage of return code / union type error handling is that the exceptional case is as fast as the successful case. The disadvantage of return code / union type error handling is that the successful case is as slow as the exceptional case. So which method is faster depends on how exceptional your exceptions are.On Monday, 12 January 2015 at 11:43:26 UTC, Ola Fosheim Grøstad wrote:What makes you say that? Doesn't D still use the standard zero-cost EH that was created for Itanium, where you take the performance hit on unwinding?Does this mean that D will get fast EH?It is fast already...
Jan 12 2015
On Monday, 12 January 2015 at 16:15:34 UTC, Matthias Bentrup wrote:The disadvantage of return code / union type error handling is that the successful case is as slow as the exceptional case.It is in a register...
Jan 12 2015
On Monday, 12 January 2015 at 13:54:18 UTC, Ola Fosheim Grøstad wrote:What makes you say that?try/throw/catch is like 50x slower than doing nothing except returning a value, but D's exceptions still tend to outperform I still wouldn't use them for ordinary flow as a general rule though, but I think they work well for cases where you ask a function to do something and it just can't.Doesn't D still use the standard zero-cost EH that was created for Itanium, where you take the performance hit on unwinding?I don't know, I'm just thinking about the bemchmarks.
Jan 12 2015
On Monday, 12 January 2015 at 17:22:27 UTC, Adam D. Ruppe wrote:On Monday, 12 January 2015 at 13:54:18 UTC, Ola Fosheim Grøstad wrote:Right, I kinda think that C++ EH is awful outside the context of big iron. It is often avoided. I think a clean language should find one single way to deal with errors, so having one mechanism that performs well would make for clean programming. I've suggested many alternatives before, at least 4. And D could do anything the hardware supports. Including having multiple return paths, efficient longjump, implicit errorcode propagation, offsetbased unwinding... Error handling and GC are perhaps the two issues that are the major weak spots for D. I think the language could be changed somewhat and do both reasonably well (as well as one can with stop-the-world-GC).What makes you say that?try/throw/catch is like 50x slower than doing nothing except returning a value, but D's exceptions still tend to outperform I still wouldn't use them for ordinary flow as a general rule though, but I think they work well for cases where you ask a function to do something and it just can't.Doesn't D still use the standard zero-cost EH that was created for Itanium, where you take the performance hit on unwinding?I don't know, I'm just thinking about the bemchmarks.
Jan 12 2015
On Mon, Jan 12, 2015 at 05:22:26PM +0000, Adam D. Ruppe via Digitalmars-d wrote:On Monday, 12 January 2015 at 13:54:18 UTC, Ola Fosheim Grøstad wrote:It's a lot better now, but D exceptions used to be orders of magnitude slower than Java's. There's an interesting little history of D exception performance here: https://issues.dlang.org/show_bug.cgi?id=9584What makes you say that?try/throw/catch is like 50x slower than doing nothing except returning isn't awful.I still wouldn't use them for ordinary flow as a general rule though, but I think they work well for cases where you ask a function to do something and it just can't.Yeah, exceptions are supposed to be ... well, *exceptions*, rather than the norm. :-) If you're using exceptions to do flow control, you're doing something wrong. construction, which was costing most of the time, but I don't remember if that was actually implemented. T -- We are in class, we are supposed to be learning, we have a teacher... Is it too much that I expect him to teach me??? -- RL
Jan 12 2015
"H. S. Teoh via Digitalmars-d" <digitalmars-d puremagic.com> wrote:On Mon, Jan 12, 2015 at 05:22:26PM +0000, Adam D. Ruppe via Digitalmars-d wrote:But what's exceptional for you is normal for me. normal indexing operator [] that throws, exactly for that reason. And this IMO there's something wrong if you have to resort to such code duplification.I still wouldn't use them for ordinary flow as a general rule though, but I think they work well for cases where you ask a function to do something and it just can't.Yeah, exceptions are supposed to be ... well, *exceptions*, rather than the norm. :-) If you're using exceptions to do flow control, you're doing something wrong.
Jan 12 2015
On Mon, Jan 12, 2015 at 06:06:19PM +0000, Tobias Müller via Digitalmars-d wrote:"H. S. Teoh via Digitalmars-d" <digitalmars-d puremagic.com> wrote:And what exactly should operator[] return if a key wasn't found? The only sane way is to have the user specify a default value if the key wasn't found, since not all types have a null value (and besides, what if null is a valid value in the dictionary?). IOW something like TryGetValue that takes multiple arguments instead of operator[]. You really should be using operator[] only when the key is expected to exist. If during normal operations there's a 50% chance the key doesn't already exist, you shouldn't be using []. T -- Маленькие детки - маленькие бедки.On Mon, Jan 12, 2015 at 05:22:26PM +0000, Adam D. Ruppe via Digitalmars-d wrote:But what's exceptional for you is normal for me. normal indexing operator [] that throws, exactly for that reason. And this is no exception (no pun intended), there are several cases like code duplification.I still wouldn't use them for ordinary flow as a general rule though, but I think they work well for cases where you ask a function to do something and it just can't.Yeah, exceptions are supposed to be ... well, *exceptions*, rather than the norm. :-) If you're using exceptions to do flow control, you're doing something wrong.
Jan 12 2015
On Monday, 12 January 2015 at 18:45:22 UTC, H. S. Teoh via Digitalmars-d wrote:The only sane way is to have the user specify a default value if the key wasn't found, since not all types have a null value (and besides, what if null is a valid value in the dictionary?). IOW something like TryGetValue that takes multiple arguments instead of operator[].Yes, and then think about the original foundation for exceptions. An exception is mean to resolve situations where you call from a higher level layer into a lower level layer (or framework). The basic idea is that the higher level layer has information that the lower layer does not. So if you have exceptions with retry you get this: 1. High Level calls into low level 2. Low Level goes "Lord High Level, I am your humble servant, but cannot compute 3/0. What shall I do?" 3. High Level ponders for a while and says "replace it with infinity and continue" 4. Low Level comes back and say "Lord High Level, I cannot find 'flowers.png'". 5. High Level responds "Use 'default.png' instead". 6. Low Level comes back crying "I can't find that either". 7. High Level gives up and says "roll back, backtrack..." Exceptions are basically about deferring decision making from an encapsulated context to the calling context. Without retries, you just have a backtracking mechanism. Don't get hung upon the terminology. Use it for writing maintainable code!You really should be using operator[] only when the key is expected to exist. If during normal operations there's a 50% chance the key doesn't already exist, you shouldn't be using [].This is getting very normative. Where did you get that 50% from? If you had exceptions with retry you could just look it up in a default directory for instance, or even made a "call" to another process to get a substitute value. Besides, if you are doing validation then it is desirable to get an exception for an illegal key. Don't let a crappy implementation of exception handling define general semantics. Improve the implementation instead.
Jan 12 2015
"H. S. Teoh via Digitalmars-d" <digitalmars-d puremagic.com> wrote:On Mon, Jan 12, 2015 at 06:06:19PM +0000, Tobias Müller via Digitalmars-d wrote:Just stick with one. Some kind of Optional<T> would be a good fit.normal indexing operator [] that throws, exactly for that reason. And this is no exception (no pun intended), there are several cases like code duplification.And what exactly should operator[] return if a key wasn't found? The only sane way is to have the user specify a default value if the key wasn't found, since not all types have a null value (and besides, what if null is a valid value in the dictionary?). IOW something like TryGetValue that takes multiple arguments instead of operator[].You really should be using operator[] only when the key is expected to exist. If during normal operations there's a 50% chance the key doesn't already exist, you shouldn't be using [].I know that and this is exactly the point. You have two methods that do almost exactly the same just with different error handling and you have to choose based on what the caller considers an error. It's often the case that only the caller can decide what is an error and what not. The designer of an API has to predict all those cases and provide different methods for each case. The problem with exceptions is, that the *callee* has to decide, not the caller.
Jan 12 2015
On Tue, 2015-01-13 at 06:50 +0000, Tobias Müller via Digitalmars-d wrote:[…]The problem with exceptions is, that the *callee* has to decide, not the caller.Callee always has the responsibility, it is just a question of whether or how callee may propagate the problem back up the chain. -- Russel. ============================================================================= Dr Russel Winder t: +44 20 7585 2200 voip: sip:russel.winder ekiga.net 41 Buckmaster Road m: +44 7770 465 077 xmpp: russel winder.org.uk London SW11 1EN, UK w: www.russel.org.uk skype: russel_winder
Jan 13 2015
On Mon, 2015-01-12 at 10:43 -0800, H. S. Teoh via Digitalmars-d wrote:[…]And what exactly should operator[] return if a key wasn't found?[…] Go has an interesting solution, key lookup in a map return a pair (result, ok), if lookup succeeded then result is the associated value, if ok is false then result is undefined. I quite like this. -- Russel. ============================================================================= Dr Russel Winder t: +44 20 7585 2200 voip: sip:russel.winder ekiga.net 41 Buckmaster Road m: +44 7770 465 077 xmpp: russel winder.org.uk London SW11 1EN, UK w: www.russel.org.uk skype: russel_winder
Jan 12 2015
On 1/12/15 11:30 AM, Russel Winder via Digitalmars-d wrote:On Mon, 2015-01-12 at 10:43 -0800, H. S. Teoh via Digitalmars-d wrote:How can you like undefined? That's a terrible solution again underlining Go's conflation of union and product types. -- Andrei[…]And what exactly should operator[] return if a key wasn't found?[…] Go has an interesting solution, key lookup in a map return a pair (result, ok), if lookup succeeded then result is the associated value, if ok is false then result is undefined. I quite like this.
Jan 12 2015
On Mon, 2015-01-12 at 11:32 -0800, Andrei Alexandrescu via Digitalmars-d wrote:On 1/12/15 11:30 AM, Russel Winder via Digitalmars-d wrote:They may be fiddling with type systems, but I was wrong about undefined: if the key is not found then lookup returns the zero value for the value type. Given this is useless to me, I think in terms of it being undefined, which is technically wrong, but good for my code. -- Russel. ============================================================================= Dr Russel Winder t: +44 20 7585 2200 voip: sip:russel.winder ekiga.net 41 Buckmaster Road m: +44 7770 465 077 xmpp: russel winder.org.uk London SW11 1EN, UK w: www.russel.org.uk skype: russel_winderOn Mon, 2015-01-12 at 10:43 -0800, H. S. Teoh via Digitalmars-d wrote:How can you like undefined? That's a terrible solution again underlining Go's conflation of union and product types. -- Andrei[…]And what exactly should operator[] return if a key wasn't found?[…] Go has an interesting solution, key lookup in a map return a pair (result, ok), if lookup succeeded then result is the associated value, if ok is false then result is undefined. I quite like this.
Jan 13 2015
On Monday, 12 January 2015 at 19:32:22 UTC, Andrei Alexandrescu wrote:On 1/12/15 11:30 AM, Russel Winder via Digitalmars-d wrote:They posted recently a blog post about error handling in Go. https://blog.golang.org/errors-are-values -- PauloOn Mon, 2015-01-12 at 10:43 -0800, H. S. Teoh via Digitalmars-d wrote:How can you like undefined? That's a terrible solution again underlining Go's conflation of union and product types. -- Andrei[…]And what exactly should operator[] return if a key wasn't found?[…] Go has an interesting solution, key lookup in a map return a pair (result, ok), if lookup succeeded then result is the associated value, if ok is false then result is undefined. I quite like this.
Jan 13 2015
That post, to me, only reinforces how much better using exceptions is. AtilaThey posted recently a blog post about error handling in Go. https://blog.golang.org/errors-are-values -- Paulo
Jan 14 2015
On Wednesday, 14 January 2015 at 10:37:31 UTC, Atila Neves wrote:That post, to me, only reinforces how much better using exceptions is. AtilaAgree, on the other hand is another example of the mindset. -- PauloThey posted recently a blog post about error handling in Go. https://blog.golang.org/errors-are-values -- Paulo
Jan 14 2015
On Wed, Jan 14, 2015 at 3:16 AM, Paulo Pinto via Digitalmars-d < digitalmars-d puremagic.com> wrote:On Wednesday, 14 January 2015 at 10:37:31 UTC, Atila Neves wrote:It is interesting to me that as people come up with ways to make error-code handling better it starts looking very much like exceptions.That post, to me, only reinforces how much better using exceptions is. Atila They posted recently a blog post about error handling in Go.Agree, on the other hand is another example of the mindset.https://blog.golang.org/errors-are-values -- Paulo
Jan 14 2015
On Wed, Jan 14, 2015 at 10:56:39AM -0800, Jeremy Powers via Digitalmars-d wrote:On Wed, Jan 14, 2015 at 3:16 AM, Paulo Pinto via Digitalmars-d < digitalmars-d puremagic.com> wrote:+1. T -- Meat: euphemism for dead animal. -- FloraOn Wednesday, 14 January 2015 at 10:37:31 UTC, Atila Neves wrote:It is interesting to me that as people come up with ways to make error-code handling better it starts looking very much like exceptions.That post, to me, only reinforces how much better using exceptions is. Atila They posted recently a blog post about error handling in Go.Agree, on the other hand is another example of the mindset.https://blog.golang.org/errors-are-values -- Paulo
Jan 14 2015
On Tue, 13 Jan 2015 11:56:00 +0000 Paulo Pinto via Digitalmars-d <digitalmars-d puremagic.com> wrote:They posted recently a blog post about error handling in Go. =20 https://blog.golang.org/errors-are-valuesgreat article! it clearly shows how NOT to do error handling and why exceptions are far superior.
Jan 14 2015
On Monday, 12 January 2015 at 19:30:10 UTC, Russel Winder via Digitalmars-d wrote:On Mon, 2015-01-12 at 10:43 -0800, H. S. Teoh via Digitalmars-d wrote:That's basically the same as a pointer to the value. If the pointer is null, dereferencing it is undefined.[…]And what exactly should operator[] return if a key wasn't found?[…] Go has an interesting solution, key lookup in a map return a pair (result, ok), if lookup succeeded then result is the associated value, if ok is false then result is undefined. I quite like this.
Jan 12 2015
On 1/12/2015 11:30 AM, Russel Winder via Digitalmars-d wrote:Go has an interesting solution, key lookup in a map return a pair (result, ok), if lookup succeeded then result is the associated value, if ok is false then result is undefined. I quite like this.That's just putting the responsibility of array bounds checking on the caller. Would you want (result, ok) returned every time you indexed an array? I sure wouldn't. An associative array isn't conceptually any different.
Jan 12 2015
On Mon, 2015-01-12 at 12:58 -0800, Walter Bright via Digitalmars-d wrote:On 1/12/2015 11:30 AM, Russel Winder via Digitalmars-d wrote:Why not? There is a fundamentally different approach to error handling depending on whether exceptions are integral cf. Python or anathema cf. Go and most functional programming languages. It is unreasonable to impose the idioms of one model onto another. And vice versa. arrays, sparse arrays and associative arrays are both very different and quite similar. The properties of the keys makes for very different approaches to algorithms, and possibly error handling of key lookup: contiguity of keys of an array is important. -- Russel. ============================================================================= Dr Russel Winder t: +44 20 7585 2200 voip: sip:russel.winder ekiga.net 41 Buckmaster Road m: +44 7770 465 077 xmpp: russel winder.org.uk London SW11 1EN, UK w: www.russel.org.uk skype: russel_winderGo has an interesting solution, key lookup in a map return a pair (result, ok), if lookup succeeded then result is the associated value, if ok is false then result is undefined. I quite like this.That's just putting the responsibility of array bounds checking on the caller. Would you want (result, ok) returned every time you indexed an array? I sure wouldn't. An associative array isn't conceptually any different.
Jan 13 2015
On 1/13/2015 3:01 AM, Russel Winder via Digitalmars-d wrote:On Mon, 2015-01-12 at 12:58 -0800, Walter Bright via Digitalmars-d wrote:My point was more to the issue of how is array bounds checking done in those languages, and that aa checking should behave the same way when doing the same operations.On 1/12/2015 11:30 AM, Russel Winder via Digitalmars-d wrote:Why not? There is a fundamentally different approach to error handling depending on whether exceptions are integral cf. Python or anathema cf. Go and most functional programming languages. It is unreasonable to impose the idioms of one model onto another. And vice versa.Go has an interesting solution, key lookup in a map return a pair (result, ok), if lookup succeeded then result is the associated value, if ok is false then result is undefined. I quite like this.That's just putting the responsibility of array bounds checking on the caller. Would you want (result, ok) returned every time you indexed an array? I sure wouldn't. An associative array isn't conceptually any different.arrays, sparse arrays and associative arrays are both very different and quite similar. The properties of the keys makes for very different approaches to algorithms, and possibly error handling of key lookup: contiguity of keys of an array is important.In trying to make component programming work, I look for emphasizing similarities between containers rather than differences, in this case, what happens when presenting an invalid index. Similarity in basic operations like that make it easy to swap in and out different containers without having to redo the rest of the code's interface to it.
Jan 13 2015
On 1/12/2015 10:06 AM, Tobias Müller wrote: On Mon, Jan 12, 2015 at 05:22:26PM +0000, Adam D. Ruppe via Digitalmars-d wrote:It's not a subjective truth, it's an objective fact. If code is throwing exceptions as part of its normal operation, it is designed wrong. And yes, if you take that as a challenge to try and concoct a counterexample, the point still stands :-)Yeah, exceptions are supposed to be ... well, *exceptions*, rather than the norm. :-) If you're using exceptions to do flow control, you're doing something wrong.But what's exceptional for you is normal for me.
Jan 12 2015
On Mon, 2015-01-12 at 12:52 -0800, Walter Bright via Digitalmars-d wrote:[…] If code is throwing exceptions as part of its normal operation, it is designed wrong.OK so I know you hate Python, but…And yes, if you take that as a challenge to try and concoct a counterexample, the point still stands :-)Python. -- Russel. ============================================================================= Dr Russel Winder t: +44 20 7585 2200 voip: sip:russel.winder ekiga.net 41 Buckmaster Road m: +44 7770 465 077 xmpp: russel winder.org.uk London SW11 1EN, UK w: www.russel.org.uk skype: russel_winder
Jan 13 2015
On Monday, 12 January 2015 at 20:53:04 UTC, Walter Bright wrote:On 1/12/2015 10:06 AM, Tobias Müller wrote: On Mon, Jan 12, 2015 at 05:22:26PM +0000, Adam D. Ruppe via Digitalmars-d wrote:But what this means that exceptions should only ever be used for catching programming errors, some kind of "soft" assertions. It also means, that exceptions should only be thrown, but never be actually handled, because by handling it you would "admit" that it's actually normal operation. This is not what I call error *handling*.It's not a subjective truth, it's an objective fact. If code is throwing exceptions as part of its normal operation, it is designed wrong.Yeah, exceptions are supposed to be ... well, *exceptions*, rather than the norm. :-) If you're using exceptions to do flow control, you're doing something wrong.But what's exceptional for you is normal for me.
Jan 15 2015
On Thu, Jan 15, 2015 at 09:01:35PM +0000, via Digitalmars-d wrote:On Monday, 12 January 2015 at 20:53:04 UTC, Walter Bright wrote:No, that's wrong. Programming errors are caught by assertions, not exceptions. Exceptions are used for handling unexpected *environmental* circumstances, such as a file that should be there but isn't, a disk that should be able to store data but happens to be full, a network server you need to talk to that ought to be present but is down for some reason, data received from a helper program that ought to have a certain format but for whatever reason sent malformed output instead.On 1/12/2015 10:06 AM, Tobias Müller wrote: On Mon, Jan 12, 2015 at 05:22:26PM +0000, Adam D. Ruppe via Digitalmars-d wrote:But what this means that exceptions should only ever be used for catching programming errors, some kind of "soft" assertions.It's not a subjective truth, it's an objective fact. If code is throwing exceptions as part of its normal operation, it is designed wrong.Yeah, exceptions are supposed to be ... well, *exceptions*, rather than the norm. :-) If you're using exceptions to do flow control, you're doing something wrong.But what's exceptional for you is normal for me.It also means, that exceptions should only be thrown, but never be actually handled, because by handling it you would "admit" that it's actually normal operation. This is not what I call error *handling*.Huh?! Handling unexpected circumstances is hardly "normal operations", it's making the program deal gracefully with problem situations in the environment that normally shouldn't occur. If the default server is down, switch to a backup server. If a required file is missing, fall back to a backup copy, or give up, file a log message, and clean up any resources properly before exiting. Etc.. That doesn't change the fact that problem situations should not be the norm -- if it is, then you're doing something wrong. A server shouldn't be constantly down. A required file shouldn't be mysteriously missing every other hour. Etc.. Unless, of course, the *purpose* of the program is specifically to deal with problem situations -- in which case, you wouldn't be using exceptions to indicate those situations, you'd treat them as "normal" input instead. You wouldn't be using try/throw/catch, but normal flow-control constructs. T -- It won't be covered in the book. The source code has to be useful for something, after all. -- Larry Wall
Jan 15 2015
On Thursday, 15 January 2015 at 21:28:59 UTC, H. S. Teoh via Digitalmars-d wrote:Unless, of course, the *purpose* of the program is specifically to deal with problem situations -- in which case, you wouldn't be using exceptions to indicate those situations, you'd treat them as "normal" input instead. You wouldn't be using try/throw/catch, but normal flow-control constructs.But for almost every environmental error, there's a use case where it is normal or at least expected. That means, you have to have two versions of every function, one that throws and one that uses "normal" flow control. Example: Creating a file: Throws if already exists. Creating a unique filename: Create file.1.exp Create file.2.exp Create file.3.exp ... until it succeeds. Here failure is expected and normal, still trial/error is the only option because a querying the file first is not safe. How can I (as the implementer of the "Create file" function) decide if a failure is actually expected or not? I can't, because I cannot foresee every use case of the function. *Especially* with environmental errors that caller has to decide what's an error and what not. You cannot just require certain environmental preconditions, because they can change unexpectedly.
Jan 15 2015
On Thursday, 15 January 2015 at 21:48:25 UTC, Tobias M wrote:But for almost every environmental error, there's a use case where it is normal or at least expected. That means, you have to have two versions of every function, one that throws and one that uses "normal" flow control.Exactly. Just think about validation of user input. Is it a service or is it a test of environmental failure? Oh, it is a service when you validate the input with a regexp, but if the database does the validation against the db schema then it is an environmental failure. Since it is normal for users to make mistakes we now have to do double work to satisfy the no-exceptions-regime. Or take a XML validation service. Is failure to validate not part of normal operation, yes it is. So we cannot use exceptions. But if we retrieve the XML from a XML database, then failure to validate is not normal operation, so then we can use exceptions in the validator. From a computational view the validators are doing the same work, that means the implementation should be the same too.How can I (as the implementer of the "Create file" function) decide if a failure is actually expected or not? I can't, because I cannot foresee every use case of the function.Reminds me of people who say that you should always test if a file exists before you open it. But to do that correctly you would have to put a lock on the file system since the file could be deleted between the test and the opening... And again, we are doing double work. Great. With this regime in place "no overhead exceptions for normal operations" creates a lot of overhead since the computer have to do double work to avoid exceptions when using libraries and frameworks. No wonder people don't want to use exceptions in C++.
Jan 15 2015
On Thursday, 15 January 2015 at 21:48:25 UTC, Tobias M wrote:Example: Creating a file: Throws if already exists. Creating a unique filename: Create file.1.exp Create file.2.exp Create file.3.exp ... until it succeeds. Here failure is expected and normal, still trial/error is the only option because a querying the file first is not safe. How can I (as the implementer of the "Create file" function) decide if a failure is actually expected or not? I can't, because I cannot foresee every use case of the function. *Especially* with environmental errors that caller has to decide what's an error and what not. You cannot just require certain environmental preconditions, because they can change unexpectedly.auto create_unique_file() { for (uint i = 0;; i++) { auto name = make_name(i); if (file_exists(name)) continue; try { create_file(name); return name: } catch(FileAlreadyExistException e) { } } } You don't need 2 interfaces, the only time when the Exception is going to fire, is if the file is created between the file_exists check and the actual creation: almost never.
Jan 15 2015
On Friday, 16 January 2015 at 00:59:34 UTC, deadalnix wrote:auto create_unique_file() { for (uint i = 0;; i++) { auto name = make_name(i); if (file_exists(name)) continue; try { create_file(name); return name: } catch(FileAlreadyExistException e) { } } } You don't need 2 interfaces, the only time when the Exception is going to fire, is if the file is created between the file_exists check and the actual creation: almost never.IMO, this example just shows that it's a bad idea to follow the "exceptions are for exceptional circumstances only" mantra too strictly. The call to `file_exists()` serves no other purpose than to make the exception truly exceptional, otherwise it's completely superfluous (well, aside from performance concerns, but these are implementation details). Simply leave it out and only rely on the exception, it really isn't evil.
Jan 16 2015
IMO, this example just shows that it's a bad idea to follow the "exceptions are for exceptional circumstances only" mantra too strictly. The call to `file_exists()` serves no other purpose than to make the exception truly exceptional, otherwise it's completely superfluous (well, aside from performance concerns, but these are implementation details). Simply leave it out and only rely on the exception, it really isn't evil.1. Exception are too slow 2. They're used for exceptional cases only, they're fast enough for this 1. But look here, my file handling code might very well throw an exception, if the file already exists. --> Use of a very slow operation, probably involving a spinning disk, at least a system call, to argue that exceptions are too slow does not convince me, sorry.
Jan 16 2015
On Monday, 12 January 2015 at 17:57:10 UTC, H. S. Teoh via Digitalmars-d wrote:construction, which was costing most of the time, but I don't remember if that was actually implemented.Yea, it was (I did it myself for posix, someone else did it on Windows), led to gigantic speed boosts, unless you are printing the trace, of course, when it has to be generated.
Jan 12 2015
On Monday, 12 January 2015 at 18:07:55 UTC, Adam D. Ruppe wrote:On Monday, 12 January 2015 at 17:57:10 UTC, H. S. Teoh via Digitalmars-d wrote:Can you elaborate on the technical details of that stunt ? When you need to build the trace, you often have unwinded part of the stack, so it does seems hard to build it lazily.construction, which was costing most of the time, but I don't remember if that was actually implemented.Yea, it was (I did it myself for posix, someone else did it on Windows), led to gigantic speed boosts, unless you are printing the trace, of course, when it has to be generated.
Jan 12 2015
On Monday, 12 January 2015 at 17:57:10 UTC, H. S. Teoh via Digitalmars-d wrote:Yeah, exceptions are supposed to be ... well, *exceptions*, rather than the norm. :-) If you're using exceptions to do flow control, you're doing something wrong.I'm sorry, but this is just a crappy excuse invented to defend C++ EH. It has no merits, except being an excuse for writing exceptionally convoluted code to save performance because real exceptions are slow. (Everything that involves a state change is essentially flow control if we are going down to basics.) There are plenty of situations where exceptions used for retries is the most sensible solution. Heck, that's even how x86 floating point exceptions work. There are plenty of situations where returning state with exceptions makes the most sense, e.g. a web service request handler.
Jan 12 2015
On Mon, 12 Jan 2015 18:11:01 +0000 via Digitalmars-d <digitalmars-d puremagic.com> wrote:returning state with exceptions(banging his head against a wall) NO. THIS NEVER HAS ANY SENSE.
Jan 12 2015
On Monday, 12 January 2015 at 19:24:35 UTC, ketmar via Digitalmars-d wrote:On Mon, 12 Jan 2015 18:11:01 +0000 via Digitalmars-d <digitalmars-d puremagic.com> wrote:Sure it has. It is a state machine. You cannot not return state. :-Preturning state with exceptions(banging his head against a wall) NO. THIS NEVER HAS ANY SENSE.
Jan 12 2015
On 1/12/2015 10:11 AM, "Ola Fosheim =?UTF-8?B?R3LDuHN0YWQi?= <ola.fosheim.grostad+dlang gmail.com>" wrote:There are plenty of situations where exceptions used for retries is the most sensible solution. Heck, that's even how x86 floating point exceptions work. There are plenty of situations where returning state with exceptions makes the most sense, e.g. a web service request handler.I've never encountered any IEEE FP code that ever, and I mean ever, checked the 'exception' sticky flag that IEEE operations require. Outside of a FP test suite, that is. Nobody has ever turned on the FP hardware exception faults, either. Bringing up IEEE 754 FP exceptions as an example of it being "done right" when it is a complete failure severely damages your case.
Jan 12 2015
On Monday, 12 January 2015 at 21:03:43 UTC, Walter Bright wrote:Bringing up IEEE 754 FP exceptions as an example of it being "done right" when it is a complete failure severely damages your case.I am bringing up exception-handling as what it is. Handling an exception involves resolving an issue and continue if suitable. Traps. Exceptions. Whatever. Has been available in CPUs since the dawn of time. It is just a means to defer decisions to the context before continuing. The fact that this puts an unrealistic demand on library authors to be excellent designers, and language design issues, resulted in the cheap solution which basically is transactional: roll back and try again with new parameters. That is of course more inefficient than resolving the issue in situ, but easier to design.
Jan 12 2015
On Monday, 12 January 2015 at 18:11:03 UTC, Ola Fosheim Grøstad wrote:On Monday, 12 January 2015 at 17:57:10 UTC, H. S. Teoh via Digitalmars-d wrote:No, Exception are a bail out mechanism. It is the, I have no idea what to do about this mechanism. If you put aside performance concerns, exceptions for control flow also tend to make many code path implicit and makes for very unreadable/unmaintainable code.Yeah, exceptions are supposed to be ... well, *exceptions*, rather than the norm. :-) If you're using exceptions to do flow control, you're doing something wrong.I'm sorry, but this is just a crappy excuse invented to defend C++ EH. It has no merits, except being an excuse for writing exceptionally convoluted code to save performance because real exceptions are slow.
Jan 12 2015
On Monday, 12 January 2015 at 22:06:32 UTC, deadalnix wrote:No, Exception are a bail out mechanism. It is the, I have no idea what to do about this mechanism.The way it is done in C++, yes.If you put aside performance concerns, exceptions for control flow also tend to make many code path implicit and makes for very unreadable/unmaintainable code.But exceptions are control flow. There is no such thing as normalized control flow, all state changes implies "control flow". Think in terms of a state machine. You could just remove all variables and only have a big state machine (assuming finite dimensions). Every single state transition implies flow of control. The control flow you see in the source code is just the programmer's "rendition" of control flow. Exceptions is a mechanism for getting cluttering resolution out of that rendition to make the code more readable and maintainable. The goal is to retain the meat of the computation and remove the noise. Or to put it differently, there are many ways to write the same function. Good use of exceptions removes the clutter and leaves the things you care about visible. It's a mechanism, not a moral issue or a religion. So there is nothing wrong with throwing HTTPStatus(409) or HTTPStatus(201), even though it returns state to the environment. If that means the code is more maintainable and more likely to be correct, then that is good use of the mechanism.
Jan 12 2015
On Monday, 12 January 2015 at 23:01:53 UTC, Ola Fosheim Grøstad wrote:On Monday, 12 January 2015 at 22:06:32 UTC, deadalnix wrote:I usually don't do this, but I really need to post a "+1" here: +1No, Exception are a bail out mechanism. It is the, I have no idea what to do about this mechanism.The way it is done in C++, yes.If you put aside performance concerns, exceptions for control flow also tend to make many code path implicit and makes for very unreadable/unmaintainable code.But exceptions are control flow. There is no such thing as normalized control flow, all state changes implies "control flow". Think in terms of a state machine. You could just remove all variables and only have a big state machine (assuming finite dimensions). Every single state transition implies flow of control. The control flow you see in the source code is just the programmer's "rendition" of control flow. Exceptions is a mechanism for getting cluttering resolution out of that rendition to make the code more readable and maintainable. The goal is to retain the meat of the computation and remove the noise. Or to put it differently, there are many ways to write the same function. Good use of exceptions removes the clutter and leaves the things you care about visible. It's a mechanism, not a moral issue or a religion. So there is nothing wrong with throwing HTTPStatus(409) or HTTPStatus(201), even though it returns state to the environment. If that means the code is more maintainable and more likely to be correct, then that is good use of the mechanism.
Jan 13 2015
On Tuesday, 13 January 2015 at 19:36:31 UTC, Marc Schütz wrote:On Monday, 12 January 2015 at 23:01:53 UTC, Ola Fosheim Grøstad wrote:Too bad you chose to do this on a bad strawmman.On Monday, 12 January 2015 at 22:06:32 UTC, deadalnix wrote:I usually don't do this, but I really need to post a "+1" here: +1No, Exception are a bail out mechanism. It is the, I have no idea what to do about this mechanism.The way it is done in C++, yes.If you put aside performance concerns, exceptions for control flow also tend to make many code path implicit and makes for very unreadable/unmaintainable code.But exceptions are control flow. There is no such thing as normalized control flow, all state changes implies "control flow". Think in terms of a state machine. You could just remove all variables and only have a big state machine (assuming finite dimensions). Every single state transition implies flow of control. The control flow you see in the source code is just the programmer's "rendition" of control flow. Exceptions is a mechanism for getting cluttering resolution out of that rendition to make the code more readable and maintainable. The goal is to retain the meat of the computation and remove the noise. Or to put it differently, there are many ways to write the same function. Good use of exceptions removes the clutter and leaves the things you care about visible. It's a mechanism, not a moral issue or a religion. So there is nothing wrong with throwing HTTPStatus(409) or HTTPStatus(201), even though it returns state to the environment. If that means the code is more maintainable and more likely to be correct, then that is good use of the mechanism.
Jan 13 2015
On Tuesday, 13 January 2015 at 20:58:43 UTC, deadalnix wrote:On Tuesday, 13 January 2015 at 19:36:31 UTC, Marc Schütz wrote:Your claims: - Exceptions are for "I have no idea what to do about this" situations. - "exceptions for control flow [...] makes for very unreadable/unmaintainable code" Ola's answer directly addresses these claims and provides a counter-example (in the last paragraph), which I happen to agree with. => Not a strawman.On Monday, 12 January 2015 at 23:01:53 UTC, Ola Fosheim Grøstad wrote:Too bad you chose to do this on a bad strawmman.On Monday, 12 January 2015 at 22:06:32 UTC, deadalnix wrote:I usually don't do this, but I really need to post a "+1" here: +1No, Exception are a bail out mechanism. It is the, I have no idea what to do about this mechanism.The way it is done in C++, yes.If you put aside performance concerns, exceptions for control flow also tend to make many code path implicit and makes for very unreadable/unmaintainable code.But exceptions are control flow. There is no such thing as normalized control flow, all state changes implies "control flow". Think in terms of a state machine. You could just remove all variables and only have a big state machine (assuming finite dimensions). Every single state transition implies flow of control. The control flow you see in the source code is just the programmer's "rendition" of control flow. Exceptions is a mechanism for getting cluttering resolution out of that rendition to make the code more readable and maintainable. The goal is to retain the meat of the computation and remove the noise. Or to put it differently, there are many ways to write the same function. Good use of exceptions removes the clutter and leaves the things you care about visible. It's a mechanism, not a moral issue or a religion. So there is nothing wrong with throwing HTTPStatus(409) or HTTPStatus(201), even though it returns state to the environment. If that means the code is more maintainable and more likely to be correct, then that is good use of the mechanism.
Jan 14 2015
On Wednesday, 14 January 2015 at 11:17:52 UTC, Marc Schütz wrote:Your claims: - Exceptions are for "I have no idea what to do about this" situations. - "exceptions for control flow [...] makes for very unreadable/unmaintainable code" Ola's answer directly addresses these claims and provides a counter-example (in the last paragraph), which I happen to agree with. => Not a strawman.Being precise is important. The example presented (ie throwing a exception signaling a http code) is a good one and never contradict what I said. It is an example "I have no idea what to do about this". The code throwing the exception is faced with a situation where it cannot continue (assuming this code is expected to generate a webpage or something like that) but at the same time, is not in a position to perform the custom http so it is bailing out. It is signaling to higher level "I would have liked to return this http code, but have no idea how to do so and so I'm giving up." Now I see how you can consider this as a control flow, but it is vastly different from usual control flow (loops, branches, calls, ...). It is vastly different. You have no idea where you send your program into. In fact, you may not even be in in the framework that can make sens of this exception, you have no idea. Conversely, the framwork that is catching this exception have no idea where it came from, and it do not care either. It simply know that the page failed to render and that instead it should return a specific error code.
Jan 14 2015
On Wednesday, 14 January 2015 at 17:45:28 UTC, deadalnix wrote:It is an example "I have no idea what to do about this". The code throwing the exception is faced with a situation where it cannot continue (assuming this code is expected to generate a webpage or something like that) but at the same time, is not in a position to perform the custom http so it is bailing out.No, "201" means success, resource created, and "409" means that it was not carried out because of conflict in the request. However, these may be meaningful responses to the client based on the input, like "204" no content. So it is output. Throwing an exception is just an efficient way to abort all resources and queries that may be in progress and return a status to the client in a clean fashion when you don't need a text body. Just a mechanism. And it makes sense if used consistently.
Jan 14 2015
On Wednesday, 14 January 2015 at 17:45:28 UTC, deadalnix wrote:On Wednesday, 14 January 2015 at 11:17:52 UTC, Marc Schütz wrote:Indeed I was assuming that the code handling the HTTP request ("controller" in MVC terms) is aware of the framework, because I don't think it's common to have exchangeable server frameworks for any given web application. Given that, the use of exceptions becomes part of the framework's API. Furthermore, the code also has a very clear idea of how to handle the situation, namely instructing the framework to return a specific error code. That is not giving up, in my eyes, but actually the opposite, to answer a specific request (e.g. "GET /non-existant-file.txt") with the response prescribed by the protocal (e.g. "404 Not found"). It could just as well have called a helper `respondWith404()` provided by the framework to set a flag somewhere and return to the framework in order to it act accordingly. It's just that the mechanism of using an exception seems more convenient (but of course, de gustibus...). Now, for a more generic library, you are right that using exceptions in this way is not a good idea. The crucial difference is IMO that in an MVC framework the application code is the one down the stack, while in many other applications it is further up the stack. Therefore, the use of exceptions (which always propagate upwards) have to be assessed differently.Your claims: - Exceptions are for "I have no idea what to do about this" situations. - "exceptions for control flow [...] makes for very unreadable/unmaintainable code" Ola's answer directly addresses these claims and provides a counter-example (in the last paragraph), which I happen to agree with. => Not a strawman.Being precise is important. The example presented (ie throwing a exception signaling a http code) is a good one and never contradict what I said. It is an example "I have no idea what to do about this". The code throwing the exception is faced with a situation where it cannot continue (assuming this code is expected to generate a webpage or something like that) but at the same time, is not in a position to perform the custom http so it is bailing out. It is signaling to higher level "I would have liked to return this http code, but have no idea how to do so and so I'm giving up." Now I see how you can consider this as a control flow, but it is vastly different from usual control flow (loops, branches, calls, ...). It is vastly different. You have no idea where you send your program into. In fact, you may not even be in in the framework that can make sens of this exception, you have no idea. Conversely, the framwork that is catching this exception have no idea where it came from, and it do not care either. It simply know that the page failed to render and that instead it should return a specific error code.
Jan 15 2015
On Thursday, 15 January 2015 at 19:37:33 UTC, Marc Schütz wrote:Now, for a more generic library, you are right that using exceptions in this way is not a good idea. The crucial difference is IMO that in an MVC framework the application code is the one down the stack, while in many other applications it is further up the stack. Therefore, the use of exceptions (which always propagate upwards) have to be assessed differently.FWIW, although the framework I use provides exceptions for returning HTTP status, I don't use them. I have my own super-class request handler that captures everything so that I can "reformulate" caught exceptions into something that fits the REST api I expose, which have to fit what browser support (like IE9)... The only problem I see is if the "exception type system" lacks an ontology that the framework can rely upon.E.g. that a function pass the exception on, but mistakenly executes an action because it makes a classification mistake. But that is more a problem with having an inadequate/unspecified/fuzzy exception classification mechanisms... The language could define an ontology that allows you to express "pass through unknown", "not a failure", "no rollback" or similar as part of the exception type. Finding the exact right semantics is perhaps tricky, but also an area where there is room for practical innovation.
Jan 15 2015
13-Jan-2015 02:01, "Ola Fosheim =?UTF-8?B?R3LDuHN0YWQi?= <ola.fosheim.grostad+dlang gmail.com>" пишет:On Monday, 12 January 2015 at 22:06:32 UTC, deadalnix wrote:Actually I agree - exceptions are mechanism. Stressing _exceptional_ as in "happening very rarely" is just a poor excuse to never optimize this control-flow mechanism. I guess C/C++ inspired languages would never have fast exceptions simply because of the mindset with the perception of them being non-important or rare. Big adopters of C++ often avoided exceptions entirely to not pollute binaries with EH-code only making this issue worse. In contrast, Java optimizes exceptions and re-writes many try/catch to plain "gotos on error" control flows. -- Dmitry OlshanskyIf you put aside performance concerns, exceptions for control flow also tend to make many code path implicit and makes for very unreadable/unmaintainable code.But exceptions are control flow. There is no such thing as normalized control flow, all state changes implies "control flow". Think in terms of a state machine. You could just remove all variables and only have a big state machine (assuming finite dimensions). Every single state transition implies flow of control. The control flow you see in the source code is just the programmer's "rendition" of control flow. Exceptions is a mechanism for getting cluttering resolution out of that rendition to make the code more readable and maintainable. The goal is to retain the meat of the computation and remove the noise. Or to put it differently, there are many ways to write the same function. Good use of exceptions removes the clutter and leaves the things you care about visible. It's a mechanism, not a moral issue or a religion. So there is nothing wrong with throwing HTTPStatus(409) or HTTPStatus(201), even though it returns state to the environment. If that means the code is more maintainable and more likely to be correct, then that is good use of the mechanism.
Jan 13 2015
On Mon, 2015-01-12 at 09:54 -0800, H. S. Teoh via Digitalmars-d wrote:[…]Yeah, exceptions are supposed to be ... well, *exceptions*, rather than the norm. :-) If you're using exceptions to do flow control, you're doing something wrong.[…] Unless you are writing Python code. -- Russel. ============================================================================= Dr Russel Winder t: +44 20 7585 2200 voip: sip:russel.winder ekiga.net 41 Buckmaster Road m: +44 7770 465 077 xmpp: russel winder.org.uk London SW11 1EN, UK w: www.russel.org.uk skype: russel_winder
Jan 12 2015
On 1/12/2015 11:27 AM, Russel Winder via Digitalmars-d wrote:Unless you are writing Python code.Nobody would be in this forum if we preferred Python :-)
Jan 12 2015
On Mon, 2015-01-12 at 13:04 -0800, Walter Bright via Digitalmars-d wrote:On 1/12/2015 11:27 AM, Russel Winder via Digitalmars-d wrote:Why does one have to "prefer" a language to the exclusion of all others (unless one is the author of a language ;-) I like Python for lots of things, but it is crap for some, hence Java/Groovy/Scala/Kotlin/Ceylon/Clojure for JVM-based work and C++/D/Rust/Go/Chapel for native-based work. PyD needs more support from the D community! -- Russel. ============================================================================= Dr Russel Winder t: +44 20 7585 2200 voip: sip:russel.winder ekiga.net 41 Buckmaster Road m: +44 7770 465 077 xmpp: russel winder.org.uk London SW11 1EN, UK w: www.russel.org.uk skype: russel_winderUnless you are writing Python code.Nobody would be in this forum if we preferred Python :-)
Jan 13 2015
On Tuesday, 13 January 2015 at 10:51:33 UTC, Russel Winder via Digitalmars-d wrote:On Mon, 2015-01-12 at 13:04 -0800, Walter Bright via Digitalmars-d wrote:I've just seen that PyD has moved to github[1] and has had a considerable cleanup of documentation. Hooray :) Now if only we had shared library support on OS X then I could actually use it.... [1] https://github.com/ariovistus/pydOn 1/12/2015 11:27 AM, Russel Winder via Digitalmars-d wrote:Why does one have to "prefer" a language to the exclusion of all others (unless one is the author of a language ;-) I like Python for lots of things, but it is crap for some, hence Java/Groovy/Scala/Kotlin/Ceylon/Clojure for JVM-based work and C++/D/Rust/Go/Chapel for native-based work. PyD needs more support from the D community!Unless you are writing Python code.Nobody would be in this forum if we preferred Python :-)
Jan 13 2015
On Tue, 2015-01-13 at 10:58 +0000, John Colvin via Digitalmars-d wrote:[…]I've just seen that PyD has moved to github[1] and has had a considerable cleanup of documentation. Hooray :) Now if only we had shared library support on OS X then I could actually use it....The standard Go development team response to the "we must have dynamic library support in Go" is "you do not need it, use operating system processes and pipes". For operating systems that support many small processes, this is a most viable solution. UNIX and Linux are supposed to be such. Of course it helps that Go is channel and dataflow focused, so it is just a variation of the standard model. Conversely CPython (and now by necessity PyPy, but not Jython or IronPython since it is irrelevant for them) trivially supports C and C++ for extensions. If D is to compete in this milieu, it is essentially to support C linkage dynamic linking on all platforms. I'm on Linux, it works for me :-) On the other hand if it doesn't work on Windows and OSX then perhaps it is broken?[1] https://github.com/ariovistus/pyd-- Russel. ============================================================================= Dr Russel Winder t: +44 20 7585 2200 voip: sip:russel.winder ekiga.net 41 Buckmaster Road m: +44 7770 465 077 xmpp: russel winder.org.uk London SW11 1EN, UK w: www.russel.org.uk skype: russel_winder
Jan 13 2015
On Tuesday, 13 January 2015 at 11:23:27 UTC, Russel Winder via Digitalmars-d wrote:On Tue, 2015-01-13 at 10:58 +0000, John Colvin via Digitalmars-d wrote:Which doesn't scale in software that requires plugins. Imagine Eclipse, for example, implemented as a collection of processes. -- Paulo[…]I've just seen that PyD has moved to github[1] and has had a considerable cleanup of documentation. Hooray :) Now if only we had shared library support on OS X then I could actually use it....The standard Go development team response to the "we must have dynamic library support in Go" is "you do not need it, use operating system processes and pipes". For operating systems that support many small processes, this is a most viable solution. UNIX and Linux are supposed to be such. Of course it helps that Go is channel and dataflow focused, so it is just a variation of the standard model.
Jan 13 2015
On Tue, 2015-01-13 at 11:50 +0000, Paulo Pinto via Digitalmars-d wrote: [=E2=80=A6]Which doesn't scale in software that requires plugins.Probably true.Imagine Eclipse, for example, implemented as a collection of=20 processes.Imagine Eclipse actually working=E2=80=A6 ;-) --=20 Russel. =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D Dr Russel Winder t: +44 20 7585 2200 voip: sip:russel.winder ekiga.n= et 41 Buckmaster Road m: +44 7770 465 077 xmpp: russel winder.org.uk London SW11 1EN, UK w: www.russel.org.uk skype: russel_winder
Jan 13 2015
On Tuesday, 13 January 2015 at 10:51:33 UTC, Russel Winder via Digitalmars-d wrote:On Mon, 2015-01-12 at 13:04 -0800, Walter Bright via Digitalmars-d wrote:I have found that Python quite nicely can replace perl, php, bash, awk and sed... What's not to like about this swiss army knife that can get rid of all that junk?On 1/12/2015 11:27 AM, Russel Winder via Digitalmars-d wrote:Why does one have to "prefer" a language to the exclusion of all others (unless one is the author of a language ;-)Unless you are writing Python code.Nobody would be in this forum if we preferred Python :-)I like Python for lots of things, but it is crap for some, hence Java/Groovy/Scala/Kotlin/Ceylon/Clojure for JVM-based work andI have almost no experience with these, why so many? What do they bring to the table versus one another?PyD needs more support from the D community!I'd consider using Python with D if the memory model was integrated. Not a bad idea, actually, but would take some real work.
Jan 13 2015
On Tuesday, 13 January 2015 at 12:14:07 UTC, Ola Fosheim Grøstad wrote:On Tuesday, 13 January 2015 at 10:51:33 UTC, Russel Winder via Digitalmars-d wrote:As long as it is only used for scripting nothing. But when people remember to use it for writing applications, then it is just plain slow and I don't see PyPy fixing that. There is a reason why Go is getting Ruby and Python developers. -- PauloOn Mon, 2015-01-12 at 13:04 -0800, Walter Bright via Digitalmars-d wrote:I have found that Python quite nicely can replace perl, php, bash, awk and sed... What's not to like about this swiss army knife that can get rid of all that junk?On 1/12/2015 11:27 AM, Russel Winder via Digitalmars-d wrote:Why does one have to "prefer" a language to the exclusion of all others (unless one is the author of a language ;-)Unless you are writing Python code.Nobody would be in this forum if we preferred Python :-)
Jan 13 2015
On Tuesday, 13 January 2015 at 12:53:15 UTC, Paulo Pinto wrote:As long as it is only used for scripting nothing. But when people remember to use it for writing applications, then it is just plain slow and I don't see PyPy fixing that.But you don't need everything in an application to be fast, so you can tie components together with scripting. Python is essentially built up around having reasonably fast components that are plugged together. But I expect that Python will be displaced with something that can compile efficiently to javascript. One should keep in mind that javascript is plenty fast for UI-bindings. It is the C++ part of browsers (layout engine) that is the slowest part today, IMO. When WebCL becomes available in browsers you probably could do the majority of applications in the browser. That is kinda scary. So the utility of compiled languages is shrinking fast. A language that makes it easy to connect building blocks and also can be used on both a server and in a webclient (compilable to javascript) will probably win in the long run.There is a reason why Go is getting Ruby and Python developers.Sure, Go is useful for web related infrastructure, has static typing, provides decent concurrency and latency and is more "lightweight" than JVM. Good all round qualities.
Jan 13 2015
On Tuesday, 13 January 2015 at 13:28:04 UTC, Ola Fosheim Grøstad wrote:On Tuesday, 13 January 2015 at 12:53:15 UTC, Paulo Pinto wrote:Sure, but JavaScript enjoys some of the best JIT compilers for dynamic languages, whereas Python has a JIT compiler that still is catching up with the current language version, doesn't support most of the extensions and is ignored by the CPython developers. If PyPy becomes the official implementation, then I will change my mind. Please note I used Python a lot while at CERN, so I do know the eco-system. -- PauloAs long as it is only used for scripting nothing. But when people remember to use it for writing applications, then it is just plain slow and I don't see PyPy fixing that.But you don't need everything in an application to be fast, so you can tie components together with scripting. Python is essentially built up around having reasonably fast components that are plugged together. But I expect that Python will be displaced with something that can compile efficiently to javascript. One should keep in mind that javascript is plenty fast for UI-bindings. It is the C++ part of browsers (layout engine) that is the slowest part today, IMO. When WebCL becomes available in browsers you probably could do the majority of applications in the browser. That is kinda scary. So the utility of compiled languages is shrinking fast. A language that makes it easy to connect building blocks and also can be used on both a server and in a webclient (compilable to javascript) will probably win in the long run.There is a reason why Go is getting Ruby and Python developers.Sure, Go is useful for web related infrastructure, has static typing, provides decent concurrency and latency and is more "lightweight" than JVM. Good all round qualities.
Jan 13 2015
On Monday, 12 January 2015 at 19:27:34 UTC, Russel Winder via Digitalmars-d wrote:On Mon, 2015-01-12 at 09:54 -0800, H. S. Teoh via Digitalmars-d wrote:You are doing Python a disservice here. Their use of exceptions for control flow statements is an implementation detail, introduced because Python exceptions are fast. (In the C runtime a Python exception is a global variable and functions that return 0;. ) The whole argument stands on its head. The discussion of good Python coding style and Python's usefulness might be found elsewhere.[…]Yeah, exceptions are supposed to be ... well, *exceptions*, rather than the norm. :-) If you're using exceptions to do flow control, you're doing something wrong.[…] Unless you are writing Python code.
Jan 13 2015
On Tue, 2015-01-13 at 13:29 +0000, via Digitalmars-d wrote: [=E2=80=A6]You are doing Python a disservice here. Their use of exceptions=20 for control flow statements is an implementation detail,=20 introduced because Python exceptions are fast. (In the C runtime=20 a Python exception is a global variable and functions that return=20 0;. ) The whole argument stands on its head.I think you miss my point. The thread appeared to be assuming the use of exception handling was a boolean, whereas it is a scale. The cost of raising and handling an exception compared to the cost of a function call is a critical metric. Thus C++ exception handling is at one scale, Python at a totally different one, with Java somewhere between. The cost determines the idioms. Because exceptions are so cheap in Python it can be used for implementation detail, and is. I assume D sits with C++ rather than Java, so exception handling is for termination semantics, not error handling. Certainly not for control flow as with Python. Go does have exceptions, there is one and it terminates execution. --=20 Russel. =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D Dr Russel Winder t: +44 20 7585 2200 voip: sip:russel.winder ekiga.n= et 41 Buckmaster Road m: +44 7770 465 077 xmpp: russel winder.org.uk London SW11 1EN, UK w: www.russel.org.uk skype: russel_winder
Jan 13 2015
On Monday, 12 January 2015 at 13:54:18 UTC, Ola Fosheim Grøstad wrote:On Monday, 12 January 2015 at 13:25:26 UTC, Adam D. Ruppe wrote:LDC, GDC and SDC do all use this.On Monday, 12 January 2015 at 11:43:26 UTC, Ola Fosheim Grøstad wrote:What makes you say that? Doesn't D still use the standard zero-cost EH that was created for Itanium, where you take the performance hit on unwinding?Does this mean that D will get fast EH?It is fast already...
Jan 12 2015
On Monday, 12 January 2015 at 11:09:01 UTC, Walter Bright wrote:On 1/12/2015 3:02 AM, Tobias Pankrath wrote:Interesting little rant about exceptions (and more), from the author of a large and successful project in C++ http://250bpm.com/blog:4As far as I understand is, it requires each component to settle on the same discriminated union that packs the error and result, which he calles Result but Now in D we use the opDot to chain components, which works since we have UFCS. which take care of the adaption.Or we could just use exceptions, which require none of that.
Jan 12 2015
On Monday, 12 January 2015 at 13:33:40 UTC, John Colvin wrote:On Monday, 12 January 2015 at 11:09:01 UTC, Walter Bright wrote:Exception in C++ is different. It is full of pitfalls and generally a usability disaster. I can understand that C++ dev do not like exception, but that say more about C++ than it does about exceptions.On 1/12/2015 3:02 AM, Tobias Pankrath wrote:Interesting little rant about exceptions (and more), from the author of a large and successful project in C++ http://250bpm.com/blog:4As far as I understand is, it requires each component to settle on the same discriminated union that packs the error and result, which he calles Result but Now in D we use the opDot to chain components, which works since we have UFCS. which take care of the adaption.Or we could just use exceptions, which require none of that.
Jan 12 2015
On Mon, Jan 12, 2015 at 08:32:57PM +0000, deadalnix via Digitalmars-d wrote:On Monday, 12 January 2015 at 13:33:40 UTC, John Colvin wrote:[...]I used to throw char* in C++. 'Nuff said. :-) T -- This is a tpyo.Interesting little rant about exceptions (and more), from the author of a large and successful project in C++ http://250bpm.com/blog:4Exception in C++ is different. It is full of pitfalls and generally a usability disaster. I can understand that C++ dev do not like exception, but that say more about C++ than it does about exceptions.
Jan 12 2015
On Monday, 12 January 2015 at 20:32:59 UTC, deadalnix wrote:On Monday, 12 January 2015 at 13:33:40 UTC, John Colvin wrote:Back when C++ got exceptions I never understood why so much paper was being wasted explained them, with articles on "The C/C++ Users Journal" and "C++ Report". Having learned exceptions in more sane languages, they just felt natural to me. But then it hit me, many of the issues are caused by the compatibility with C semantics and the pay only for what you use mantra. -- PauloOn Monday, 12 January 2015 at 11:09:01 UTC, Walter Bright wrote:Exception in C++ is different. It is full of pitfalls and generally a usability disaster. I can understand that C++ dev do not like exception, but that say more about C++ than it does about exceptions.On 1/12/2015 3:02 AM, Tobias Pankrath wrote:Interesting little rant about exceptions (and more), from the author of a large and successful project in C++ http://250bpm.com/blog:4As far as I understand is, it requires each component to settle on the same discriminated union that packs the error and result, which he calles Result but Now in D we use the opDot to chain components, which works since we have UFCS. which take care of the adaption.Or we could just use exceptions, which require none of that.
Jan 12 2015
On Monday, 12 January 2015 at 21:34:20 UTC, Paulo Pinto wrote:But then it hit me, many of the issues are caused by the compatibility with C semantics and the pay only for what you use mantra.Yeah, C++ exceptions were originally so troubled that no sane person would use them, thus you did not get the culture where most code was designed for it and "C++ with no exceptions and no rtti" became a mantra... For exception handling to shine you need consistency backed by culture. It is probably the culture around languages like Java and Python that foster that. (Most of std::C++ is optional, templated and inefficient... There is no consistent culture. Though they got some thing right with unique_ptr and new language features recently.)
Jan 12 2015
Ola Fosheim Grøstad:(Most of std::C++ is optional, templated and inefficient... There is no consistent culture. Though they got some thing right with unique_ptr and new language features recently.)I don't agree. The basic ideas of STL by Alexander Stepanov are very good. Phobos contains related ideas, repackaged in ranges. Ranges are a little more fundamental, ma but in practice they are often good enough and they are often more handy. Rust has chosen a different way so far, but will improve with higher order generics later. Bye, bearophile
Jan 12 2015
On Monday, 12 January 2015 at 23:18:52 UTC, bearophile wrote:I don't agree. The basic ideas of STL by Alexander Stepanov are very good. Phobos contains related ideas, repackaged in ranges. Ranges are a little more fundamental, ma but in practice they are often good enough and they are often more handy. Rust has chosen a different way so far, but will improve with higher order generics later.I've tried to like STL for 17 years, and whenever speed and clear programming matters it is basically a good idea to throw it out. It goes like this: 1. code up prototype with STL, 2. write STL code and add the features I need, 3. being annoyed by the bloat and noise, 4. rewrite code with my own container for log2 efficiency and ease of use... I wish Phobos would have stuck to the term "iterators" used in GoF since a "range" usually is something else... but digressions aside. Phobos is kinda like the "functional" parts of Python. The downside there is that you need to remember lots of verbs. A solution like list comprehensions is a lot easier on the programmer, if convenience is the goal. But in system level programming efficiency is the goal. That means log2 sizes, operations that are optimized for log2 sizes, datastructures that are optimized for SIMD. Phobos "ranges" need a next_simd() to be efficient. Right?
Jan 12 2015
Ola Fosheim Grøstad:I've tried to like STL for 17 years, and whenever speed and clear programming matters it is basically a good idea to throw it out.Take a look at the ideas of "C++ seasoning" (http://channel9.msdn.com/Events/GoingNative/2013/Cpp-Seasoning ), where they suggest to do kind of the opposite of what you do, it means throwing out loops and other things, and replacing them with standard algorithms.A solution like list comprehensions is a lot easier on the programmer, if convenience is the goal.There's still time to add lazy and eager sequence comprehensions suggestions were not welcomed. D has lot of features, adding more and more has costs.Phobos "ranges" need a next_simd() to be efficient. Right?Perhaps, but first std.simd needs to be finished. Bye, bearophile
Jan 13 2015
On Tuesday, 13 January 2015 at 09:58:56 UTC, bearophile wrote:Take a look at the ideas of "C++ seasoning" (http://channel9.msdn.com/Events/GoingNative/2013/Cpp-Seasoning ), where they suggest to do kind of the opposite of what you do, it means throwing out loops and other things, and replacing them with standard algorithms.Yes... you can do that. For little gain since C++'s support for high level programming is bloat inducing. Just take a look of all the symbols you need to have a conforming iterator or allocator... An allocator should be a simple 5 line snippet, it is bloatsome in STL: https://gist.github.com/donny-dont/1471329 Then I needed a circular buffer. STL didn't have one. So I downloaded the Boost one. It was terribly inefficient, because it was generic and STLish. I ended up writing my own using a fixed size log2 array with a start and end index. Clean conditional-free efficient code due to the log2 property and modular arithmetics. So, in the end I get something faster, that produce more readable code, give faster compiles, is easier to read, is easier to debug and was implemented in less time than finding and figuring out the Boost one... I have no problem with using array<T> and vector<T> where it fits, but in the end templated libraries prevent transparency. If you want speed you need to understand layout. Concrete implementations make that easier.Right, but you need to support masked simd if you want to do filtering. Maybe autovectorization is the only path. Still, you also need to keep your loops tiny if you want to benefit from X86 loop buffer. The CPU is capable of unrolling tight loops in hardware before hitting the execution pipeline. Thus getting the conditionals out of the pipeline. Then you have cache locality. You need to break up long loops so you don't push things out of the caches. So, if you can gain 2x by good cache locality/prefetching and 4x by using AVX over scalars, then you gain 8x performance over a naive implementation. That hurts.A solution like list comprehensions is a lot easier on the programmer, if convenience is the goal.There's still time to add lazy and eager sequence comprehensions (or even better the computational thinghies of features, adding more and more has costs.Phobos "ranges" need a next_simd() to be efficient. Right?Perhaps, but first std.simd needs to be finished.
Jan 13 2015
On Monday, 12 January 2015 at 23:18:52 UTC, bearophile wrote:Ola Fosheim Grøstad:Lets not forget Alexander Stepanov created STL for Ada originally, and managed to convince the C++ committee to improve templates in a way that could properly support STL. His ideas from generic programming precede C++. -- Paulo(Most of std::C++ is optional, templated and inefficient... There is no consistent culture. Though they got some thing right with unique_ptr and new language features recently.)I don't agree. The basic ideas of STL by Alexander Stepanov are very good. Phobos contains related ideas, repackaged in ranges. Ranges are a little more fundamental, ma but in practice they are often good enough and they are often more handy. Rust has chosen a different way so far, but will improve with higher order generics later. Bye, bearophile
Jan 13 2015
On Monday, 12 January 2015 at 00:51:25 UTC, Walter Bright wrote:On 1/11/2015 5:06 AM, Dicebot wrote:The general solution in functional programming is error chaining. An example, C is a function that reads in lines of a program and B is a function that takes all those lines and counts words. C will either return an error or lines and B will either immediately return that error to A or convert the lines to word counts. This works especially well with function chaining, because you can hide the error propagation in a generic chaining method (called map). http://danielwestheide.com/blog/2012/12/26/the-neophytes-guide-to-scala-part-6-error-handling-with-try.htmlWhat is your opinion of approach advertised by various functional languages and now also Rust? Where you return error code packed with actual data and can't access data without visiting error code too, compiler simply won't allow it.It's a great question. I have a lot of experience with error codes, and with exceptions. I have zero with the packed scheme, though that doesn't stop me from having an opinion :-) Perhaps I misunderstand, but given A calls B calls C, A => B => C
Jan 12 2015
On 1/12/2015 6:57 AM, Martin Nowak wrote:The general solution in functional programming is error chaining. An example, C is a function that reads in lines of a program and B is a function that takes all those lines and counts words. C will either return an error or lines and B will either immediately return that error to A or convert the lines to word counts. This works especially well with function chaining, because you can hide the error propagation in a generic chaining method (called map). http://danielwestheide.com/blog/2012/12/26/the-neophytes-guide-to-scala-part-6-error-handling-with-try.htmlYes, it still appears to be just a wrapper around returning two values, and that has to be done for everything. There's another downside to returning two values - extra code is generated, and it consumes another register. It allocates very scarce resources to rare cases - not a recipe for high performance.
Jan 12 2015
On Monday, 12 January 2015 at 21:11:44 UTC, Walter Bright wrote:There's another downside to returning two values - extra code is generated, and it consumes another register. It allocates very scarce resources to rare cases - not a recipe for high performance.In server applications there is no such thing as a rare case though and this trade-off looks quite appealing.
Jan 12 2015
On 1/12/2015 1:22 PM, Dicebot wrote:In server applications there is no such thing as a rare case though and this trade-off looks quite appealing.Don't use exceptions for normal operations.
Jan 12 2015
On Monday, 12 January 2015 at 22:13:10 UTC, Walter Bright wrote:On 1/12/2015 1:22 PM, Dicebot wrote:Which is equivalent to "don't use exceptions on servers" :) Yes, I know, this is why any alternative approach is worth interest.In server applications there is no such thing as a rare case though and this trade-off looks quite appealing.Don't use exceptions for normal operations.
Jan 12 2015
On Monday, 12 January 2015 at 22:54:08 UTC, Dicebot wrote:Which is equivalent to "don't use exceptions on servers" :) Yes, I know, this is why any alternative approach is worth interest.I think error handling chains like Maybe!(Result) or Either!(Error, Result) could be nicely implemented in a library. I thought about using something like that for error handling in the dub-registey.
Jan 12 2015
Walter Bright:Yes, it still appears to be just a wrapper around returning two values, and that has to be done for everything.There's lot of functional theory behind such ideas.There's another downside to returning two values - extra code is generated, and it consumes another register. It allocates very scarce resources to rare cases - not a recipe for high performance.I suggest to start inverting your point of view: try to look why languages... In bugzilla I asked for a "maybeTo" that is similar to the "to" Phobos function, but it's nogc nothrow because it returns an Nullable!T result. Bye, bearophile
Jan 12 2015
On 1/12/15 1:35 PM, bearophile wrote:Walter Bright:I can't believe I agree with everything bearophile just said :o). -- AndreiYes, it still appears to be just a wrapper around returning two values, and that has to be done for everything.There's lot of functional theory behind such ideas.There's another downside to returning two values - extra code is generated, and it consumes another register. It allocates very scarce resources to rare cases - not a recipe for high performance.I suggest to start inverting your point of view: try to look why the In bugzilla I asked for a "maybeTo" that is similar to the "to" Phobos function, but it's nogc nothrow because it returns an Nullable!T result.
Jan 12 2015
On Monday, 12 January 2015 at 21:41:48 UTC, Andrei Alexandrescu wrote:I can't believe I agree with everything bearophile just said :o). -- AndreiBut we knew that already. channel9.msdn.com/Shows/Going+Deep/C-and-Beyond-2012-Andrei-Alexandrescu-Systematic-Error-Handling-in-C stackoverflow.com/questions/14923346/how-would-you-use-alexandrescus-expectedt-with-void-functions
Jan 12 2015
On Monday, 12 January 2015 at 21:11:44 UTC, Walter Bright wrote:On 1/12/2015 6:57 AM, Martin Nowak wrote:Yes, you wrap the return values, but it doesn't require to deal with errors in B.The general solution in functional programming is error chaining. An example, C is a function that reads in lines of a program and B is a function that takes all those lines and counts words. C will either return an error or lines and B will either immediately return that error to A or convert the lines to word counts. This works especially well with function chaining, because you can hide the error propagation in a generic chaining method (called map). http://danielwestheide.com/blog/2012/12/26/the-neophytes-guide-to-scala-part-6-error-handling-with-try.htmlYes, it still appears to be just a wrapper around returning two values, and that has to be done for everything.There's another downside to returning two values - extra code is generated, and it consumes another register. It allocates very scarce resources to rare cases - not a recipe for high performance.To defend that argument we'd first have to fix our own codegen. https://issues.dlang.org/show_bug.cgi?id=12442 It doesn't really consume the register, because the error value is only needed directly after the call for a possible early return. But of course returning tuples can be less efficient. OT!! Reminds me of Manu's request for more efficient register return. www.digitalmars.com/d/archives/digitalmars/D/Multiple_return_values..._160353.html
Jan 12 2015
On 1/12/2015 1:46 PM, Martin Nowak wrote:That issue has nothing to do with exception handling vs error codes.There's another downside to returning two values - extra code is generated, and it consumes another register. It allocates very scarce resources to rare cases - not a recipe for high performance.To defend that argument we'd first have to fix our own codegen. https://issues.dlang.org/show_bug.cgi?id=12442It doesn't really consume the register, because the error value is only needed directly after the call for a possible early return. But of course returning tuples can be less efficient.Yes, it does. Returning an int in EAX now becomes returning a pair [EAX,EDX].
Jan 12 2015
On Monday, 12 January 2015 at 22:17:57 UTC, Walter Bright wrote:Yes, it does. Returning an int in EAX now becomes returning a pair [EAX,EDX].It is not that big of a deal, EDX is a trash register anyway if memory serve, but then, it become very bad when it do not fit in register anymore.
Jan 12 2015
On Mon, Jan 12, 2015 at 11:17:24PM +0000, deadalnix via Digitalmars-d wrote:On Monday, 12 January 2015 at 22:17:57 UTC, Walter Bright wrote:On the contrary, it's an extremely big deal. Registers are a rare resource, and allocating them for maximal usefulness is a major issue in code optimization. T -- A mathematician is a device for turning coffee into theorems. -- P. ErdosYes, it does. Returning an int in EAX now becomes returning a pair [EAX,EDX].It is not that big of a deal, EDX is a trash register anyway if memory serve, but then, it become very bad when it do not fit in register anymore.
Jan 12 2015
On Monday, 12 January 2015 at 23:29:08 UTC, H. S. Teoh via Digitalmars-d wrote:On Mon, Jan 12, 2015 at 11:17:24PM +0000, deadalnix via Digitalmars-d wrote:These are trash register. Meaning the callee can put whatever in them. The caller must consider them trashed after the call. So no, it do NOT increase register pressure.On Monday, 12 January 2015 at 22:17:57 UTC, Walter Bright wrote:On the contrary, it's an extremely big deal. Registers are a rare resource, and allocating them for maximal usefulness is a major issue in code optimization.Yes, it does. Returning an int in EAX now becomes returning a pair [EAX,EDX].It is not that big of a deal, EDX is a trash register anyway if memory serve, but then, it become very bad when it do not fit in register anymore.
Jan 12 2015
On 1/12/2015 4:40 PM, deadalnix wrote:These are trash register. Meaning the callee can put whatever in them. The caller must consider them trashed after the call. So no, it do NOT increase register pressure.1. the register must be assigned a value - that has a cost 2. functions often get inlined 3. it prevents EDX from being used by the return value, when the return value is larger than a single register size 4. static functions may not need to follow the C ABI register convention, and can be so optimized
Jan 12 2015
On Tuesday, 13 January 2015 at 03:05:57 UTC, Walter Bright wrote:On 1/12/2015 4:40 PM, deadalnix wrote:xor on Haswell has a reciprocal throughput of 0.25, meaning execution of 4 instructions per cycle per core.These are trash register. Meaning the callee can put whatever in them. The caller must consider them trashed after the call. So no, it do NOT increase register pressure.1. the register must be assigned a value - that has a cost3. it prevents EDX from being used by the return value, when the return value is larger than a single register sizeStore the error in TLS memory using a write-through (no load from memory) instruction and use the carry-flag instead then (assuming the return instruction does not clear carry). CLC has reciprocal throughput of 0.25 too.4. static functions may not need to follow the C ABI register convention, and can be so optimizedD calls D, no problem. D calls C, no problem, C can't return D errors... C calls D, a problem no matter what solution you pick.
Jan 13 2015
Of course the best solution might be to just implement reasonably fast exceptions by: 1. Restricting the exception object to 512 bytes, preallocated in TLS. 2. Only have one exception in flight per thread 3. Require that the first 8 bytes of the exception buffer are 0 when no exception is in flight, and use them for encoding exception type when one is in flight. 4. Leave it to the implementation how to propagate awareness of an exception. (LLVM has many different calling conventions, it might be desirable to do it different for each one). 5. Constrain calls from C to D to nothrow functions.
Jan 13 2015
On Tuesday, 13 January 2015 at 22:46:26 UTC, Ola Fosheim Grøstad wrote:Of course the best solution might be to just implement reasonably fast exceptions by: 1. Restricting the exception object to 512 bytes, preallocated in TLS.Exception can bubble from one thread to another.2. Only have one exception in flight per threadI don't see how you could have more.3. Require that the first 8 bytes of the exception buffer are 0 when no exception is in flight, and use them for encoding exception type when one is in flight.The first 8 bytes are already what is used to do matching. But, because of inheritance, it is not as simple as you think it is. Also, who the fuck care what is in the buffer when no exception are in flight ? Skipping 4 and 5, you obviously didn't though that through.
Jan 13 2015
On Tuesday, 13 January 2015 at 23:40:41 UTC, deadalnix wrote:How? If you are thinking coroutines, then the exception can be moved with it.1. Restricting the exception object to 512 bytes, preallocated in TLS.Exception can bubble from one thread to another.In a catch you can throw a new one and initialize with the old, but if they are both in the same memory space it will go wrong.2. Only have one exception in flight per threadI don't see how you could have more.1. So that you can mark a non-D function with checkexception and allow it to both cast D-exceptions and propagate D-exceptions.3. Require that the first 8 bytes of the exception buffer are 0 when no exception is in flight, and use them for encoding exception type when one is in flight.The first 8 bytes are already what is used to do matching. But, because of inheritance, it is not as simple as you think it is. Also, who the fuck care what is in the buffer when no exception are in flight ?Skipping 4 and 5, you obviously didn't though that through.?
Jan 13 2015
On Tuesday, 13 January 2015 at 23:47:58 UTC, Ola Fosheim Grøstad wrote:On Tuesday, 13 January 2015 at 23:40:41 UTC, deadalnix wrote:How? If you are thinking coroutines, then the exception can be moved with it.1. Restricting the exception object to 512 bytes, preallocated in TLS.Exception can bubble from one thread to another.When you are in the catch, you are not unwinding anymore. You could indeed loose chaining to be able to reuse the memory, that is not what is slow when it come to exception, so it won't make it fast.In a catch you can throw a new one and initialize with the old, but if they are both in the same memory space it will go wrong.2. Only have one exception in flight per threadI don't see how you could have more.Empty hand waving, not not addressing the point.1. So that you can mark a non-D function with checkexception and allow it to both cast D-exceptions and propagate D-exceptions.3. Require that the first 8 bytes of the exception buffer are 0 when no exception is in flight, and use them for encoding exception type when one is in flight.The first 8 bytes are already what is used to do matching. But, because of inheritance, it is not as simple as you think it is. Also, who the fuck care what is in the buffer when no exception are in flight ?What you propose in point 1 - 3 is weak at best. You haven't put serious effort thinking about what you are proposing, so there is no reason anyone should spend any serious effort criticizing it.Skipping 4 and 5, you obviously didn't though that through.?
Jan 13 2015
On Tuesday, 13 January 2015 at 23:58:53 UTC, deadalnix wrote:I don't see the problem. I'm suggesting value semantics, it can be copied.When you are in the catch, you are not unwinding anymore. You could indeed loose chaining to be able to reuse the memory, that is not what is slow when it come to exception, so it won't make it fast.You can't have chaining with this scheme...? But since you can only have one instance it has to be initialized with value semantics. It's a small constraint. Not using regular Itanium-style unwinding will make it potentially faster. Using TLS makes it possible to propagate over non-D code or code where the register pressure is high, it can be optimized away so you don't need TLS if you don't span over non-D code.Who are you waving at? Non D functions can cast by setting the TLS memory to an exception value, then do a regular return. The D function that called the non-D function will check TLS memory to see if there is an exception in flight.1. So that you can mark a non-D function with checkexception and allow it to both cast D-exceptions and propagate D-exceptions.Empty hand waving, not not addressing the point.Then don't bother. If your point regarding point 4 was that it would limit cross compiler linking, then yes. And that would be intentional, since it is far too soon to standardize D at that level. It just prevents experimentation and performance gains. If your point regarding point 5 is that it would be too limiting, well then you would need to differentiate between D and C entrypoints to D functions to overcome the problem. That's possible, but IMO not worth it.What you propose in point 1 - 3 is weak at best. You haven't put serious effort thinking about what you are proposing, so there is no reason anyone should spend any serious effort criticizing it.Skipping 4 and 5, you obviously didn't though that through.?
Jan 13 2015
On Wednesday, 14 January 2015 at 00:24:41 UTC, Ola Fosheim Grøstad wrote:I don't see the problem. I'm suggesting value semantics, it can be copied.Then you can't catch by super class. This is not going to fly.You write that paragraph like there is some logical links between elements in it, but there is none.When you are in the catch, you are not unwinding anymore. You could indeed loose chaining to be able to reuse the memory, that is not what is slow when it come to exception, so it won't make it fast.You can't have chaining with this scheme...? But since you can only have one instance it has to be initialized with value semantics. It's a small constraint.Not using regular Itanium-style unwinding will make it potentially faster.The only things that is sure here is that it is going to make the non exception path slower. Without specific we can say anything else than that.Using TLS makes it possible to propagate over non-D code or code where the register pressure is high,It won't help with register pressure. Having a pointer to an exception object in a register is equivalent to having a pointer to a TLS memory area in a register.it can be optimized away so you don't need TLS if you don't span over non-D code.Without specifics, it is another instance of the sufficiently smart compiler running gag.Non D functions can cast by setting the TLS memory to an exception value, then do a regular return. The D function that called the non-D function will check TLS memory to see if there is an exception in flight.You don't make things faster by making the calling convention easier. There is chance that this is gonna fly any better than a flat iron.
Jan 13 2015
On Wednesday, 14 January 2015 at 01:37:46 UTC, deadalnix wrote:On Wednesday, 14 January 2015 at 00:24:41 UTC, Ola Fosheim Grøstad wrote:I said value. Use bitmasks. Class hierarchies don't work very well.I don't see the problem. I'm suggesting value semantics, it can be copied.Then you can't catch by super class. This is not going to fly.If you cannot follow the logic... Where did you get lost?You can't have chaining with this scheme...? But since you can only have one instance it has to be initialized with value semantics. It's a small constraint.You write that paragraph like there is some logical links between elements in it, but there is none.The only things that is sure here is that it is going to make the non exception path slower. Without specific we can say anything else than that.? You essentially have two options: 1. A single branch on return. 2. Multiple return paths. 2a) returning to the calling function 2b) using a landing pad (current solution)It won't help with register pressure. Having a pointer to an exception object in a register is equivalent to having a pointer to a TLS memory area in a register.TLS is in a register already.This does not take a very smart compiler. You can use heuristics, such as ones using the function signature.it can be optimized away so you don't need TLS if you don't span over non-D code.Without specifics, it is another instance of the sufficiently smart compiler running gag.You don't make things faster by making the calling convention easier. There is chance that this is gonna fly any better than a flat iron.? Of course you make it faster by allowing the compiler to use it's own calling conventions.
Jan 13 2015
Just to be clear on this. Sandy Bridge and later have 16 x 256 bits registers. There is really no reason for an optimizing compiler to not just stuff the whole exception value into 1 or 2 of those in most cases where it has full control. In the rare case where that is not enough, or possible, stuff it into a fixed TLS buffer (or a similar arrangement) and clear a signal value in that buffer when the exception is caught.
Jan 14 2015
On Wednesday, 14 January 2015 at 07:49:09 UTC, Ola Fosheim Grøstad wrote:Once again, no specifics.Then you can't catch by super class. This is not going to fly.I said value. Use bitmasks. Class hierarchies don't work very well.I was not lost. I know how to recognize a non sequitur when I read one.You write that paragraph like there is some logical links between elements in it, but there is none.If you cannot follow the logic... Where did you get lost?It's a good thing that you can do all of these in D already.The only things that is sure here is that it is going to make the non exception path slower. Without specific we can say anything else than that.? You essentially have two options: 1. A single branch on return. 2. Multiple return paths. 2a) returning to the calling function 2b) using a landing pad (current solution)You only need this when you are using static TLS variable, which is not that common in practice. The change would makes this required all over the place.It won't help with register pressure. Having a pointer to an exception object in a register is equivalent to having a pointer to a TLS memory area in a register.TLS is in a register already.It is a known fact that a sufficiently smart compiler will use heuristic. It is also fairly obvious that this heuristic will use function signature.This does not take a very smart compiler. You can use heuristics, such as ones using the function signature.it can be optimized away so you don't need TLS if you don't span over non-D code.Without specifics, it is another instance of the sufficiently smart compiler running gag.Making a new calling convention is not going to magically make things faster. If the calling convention need to do more, it is certainly going to make things slower. Hopefully, function calls are not common at all, so that shouldn't be a problem.You don't make things faster by making the calling convention easier. There is chance that this is gonna fly any better than a flat iron.? Of course you make it faster by allowing the compiler to use it's own calling conventions.
Jan 14 2015
On Wednesday, 14 January 2015 at 17:53:54 UTC, deadalnix wrote:Once again, no specifics.How is a bitmask (e.g. a set) not specific?I was not lost. I know how to recognize a non sequitur when I read one.Then you should also be able to explain where you got lost.This is about what the compiler does, not what you can do in D. So no. You cannot have multiple return paths (like returning to the returnaddress+OFFSET) or branch implicitly based on the carry flag upon return.1. A single branch on return. 2. Multiple return paths. 2a) returning to the calling function 2b) using a landing pad (current solution)It's a good thing that you can do all of these in D already.You only need this when you are using static TLS variable, which is not that common in practice. The change would makes this required all over the place.That's the point. To have a buffer in TLS so that you don have to do transient heap allocations (allocation directly followed by deallocation of same object).Making a new calling convention is not going to magically make things faster. If the calling convention need to do more, it is certainly going to make things slower.One needs a calling convention to ensure that the backend puts the exception signal in the right register or to do offset return. Or to free up more registers. Haskell has a register heavy calling convention to speed up Haskell code. So it is not certainly going to make things slower. Clearing a flag/register will most likely fill in a bubble in the pipeline. A predicted non-branching branch instruction is fairly cheap.Hopefully, function calls are not common at all, so that shouldn't be a problem.?
Jan 14 2015
On Wednesday, 14 January 2015 at 00:24:41 UTC, Ola Fosheim Grøstad wrote:On Tuesday, 13 January 2015 at 23:58:53 UTC, deadalnix wrote:Without addressing anything else: You want moving here, not copying.I don't see the problem. I'm suggesting value semantics, it can be copied.
Jan 14 2015
On Wednesday, 14 January 2015 at 11:20:49 UTC, Marc Schütz wrote:Without addressing anything else: You want moving here, not copying.How can you move a value, it has no identity? (Not that I really care much for Thread.join(). How many actually use it?)
Jan 14 2015
On Wednesday, 14 January 2015 at 12:15:03 UTC, Ola Fosheim Grøstad wrote:On Wednesday, 14 January 2015 at 11:20:49 UTC, Marc Schütz wrote:When you say "copy", I expect that to mean "do a bitwise copy and call the postblit". Postblit is obviously not desired here, but merely doing the bitwise copy is enough. That's a move, because afterwards the original location isn't usable anymore (the originating thread is dead).Without addressing anything else: You want moving here, not copying.How can you move a value, it has no identity?
Jan 15 2015
On Thursday, 15 January 2015 at 19:19:22 UTC, Marc Schütz wrote:When you say "copy", I expect that to mean "do a bitwise copy and call the postblit". Postblit is obviously not desired here, but merely doing the bitwise copy is enough. That's a move, because afterwards the original location isn't usable anymore (the originating thread is dead).Ah, yes, I was not thinking current D compiler internals, just "it is implementable". :) But I think cheap/efficient exceptions are important since API consistency makes it easier to write correct code. Single inheritance makes it difficult to use exceptions with multiple libraries since they want their own root exception. A solid ontology that outlines the most common qualities you want to filter would help a lot. Maybe also a classification mechanism that allows you to group exceptions from multiple libraries. E.g.: classify library1.EntityNotFound, library2.LookupFailure as NotFound;
Jan 15 2015
On 1/12/2015 3:17 PM, deadalnix wrote:On Monday, 12 January 2015 at 22:17:57 UTC, Walter Bright wrote:Returning a slice, for example, already consumes EAX and EDX. Adding an error code pushes that into returning via a pointer to a stack temporary. Also, there's the code needed to load the value into EDX in the first place. It adds up.Yes, it does. Returning an int in EAX now becomes returning a pair [EAX,EDX].It is not that big of a deal, EDX is a trash register anyway if memory serve, but then, it become very bad when it do not fit in register anymore.
Jan 12 2015
On Tuesday, 13 January 2015 at 00:05:31 UTC, Walter Bright wrote:On 1/12/2015 3:17 PM, deadalnix wrote:Quoting myself:On Monday, 12 January 2015 at 22:17:57 UTC, Walter Bright wrote:Returning a slice, for example, already consumes EAX and EDX. Adding an error code pushes that into returning via a pointer to a stack temporary. Also, there's the code needed to load the value into EDX in the first place. It adds up.Yes, it does. Returning an int in EAX now becomes returning a pair [EAX,EDX].It is not that big of a deal, EDX is a trash register anyway if memory serve, but then, it become very bad when it do not fit in register anymore.it become very bad when it do not fit in register anymore.So we agree :)
Jan 12 2015
On Tuesday, 13 January 2015 at 00:05:31 UTC, Walter Bright wrote:On 1/12/2015 3:17 PM, deadalnix wrote:There are techniques to mitigate that. For example, Rust is smart enough to encode the "not-present" state for pointers as a NULL pointer (Rust pointers are non-null). If an error code needs to be returned, it can be encoded as a misaligned pointer (if you're feeling adventurous). Don't know whether Rust does the latter, though. Of course, these things have costs of their own.On Monday, 12 January 2015 at 22:17:57 UTC, Walter Bright wrote:Returning a slice, for example, already consumes EAX and EDX. Adding an error code pushes that into returning via a pointer to a stack temporary.Yes, it does. Returning an int in EAX now becomes returning a pair [EAX,EDX].It is not that big of a deal, EDX is a trash register anyway if memory serve, but then, it become very bad when it do not fit in register anymore.
Jan 13 2015
On 1/13/2015 11:42 AM, "Marc =?UTF-8?B?U2Now7x0eiI=?= <schuetzm gmx.net>" wrote:On Tuesday, 13 January 2015 at 00:05:31 UTC, Walter Bright wrote:Such mitigation techniques pretty much confirms there's an efficiency cost to having the error code.On 1/12/2015 3:17 PM, deadalnix wrote:There are techniques to mitigate that. For example, Rust is smart enough to encode the "not-present" state for pointers as a NULL pointer (Rust pointers are non-null).On Monday, 12 January 2015 at 22:17:57 UTC, Walter Bright wrote:Returning a slice, for example, already consumes EAX and EDX. Adding an error code pushes that into returning via a pointer to a stack temporary.Yes, it does. Returning an int in EAX now becomes returning a pair [EAX,EDX].It is not that big of a deal, EDX is a trash register anyway if memory serve, but then, it become very bad when it do not fit in register anymore.If an error code needs to be returned, it can be encoded as a misaligned pointer (if you're feeling adventurous). Don't know whether Rust does the latter, though. Of course, these things have costs of their own.
Jan 13 2015
On Monday, 12 January 2015 at 22:17:57 UTC, Walter Bright wrote:If you start to discuss register allocation than the actual cost of "zero-cost" EH has quite a lot to do with it.To defend that argument we'd first have to fix our own codegen. https://issues.dlang.org/show_bug.cgi?id=12442That issue has nothing to do with exception handling vs error codes.
Jan 12 2015
On Monday, 12 January 2015 at 00:51:25 UTC, Walter Bright wrote:Perhaps I misunderstand, but given A calls B calls C, A => B => C and C detects an error, and A knows what to do with the error. B then becomes burdened with checking for the error, invoking some sort of cleanup code, and then propagating it.There's another reason why this is not that bad (under the assumption that errors are _not_ only used for premature termination): The meaning of an error ist in most cases only clear in combination with the function call. With every step in the call stack you lose information about the error and the handling becomes more difficult. In a well-designed interface, every function specifies all the errors that might happen. And by this I mean a meaningful set of errors that is useful for the caller, not just the union of all errors resulting from the implementation. Simply propagating errors is not really an option in that case, because higher-level functions often also (should) have higher-level errors (the lower-level root-error should still be available though). Some random example: Some function needs some configuration from a file, but has no permission -> signals a permission error. But it doesn't make sense for C to just propagate a permission error. Instead it should signal a configuration error and store the permission error as the cause of that configuration error.
Jan 15 2015
On Thursday, 15 January 2015 at 21:29:28 UTC, Tobias Müller wrote:In a well-designed interface, every function specifies all the errors that might happen.BWAHAHAHAHAHAHAHAHAHAHAHAHAHAHAHAHAHAHAHAHAHAHAHAHA !
Jan 15 2015
On Friday, 16 January 2015 at 00:48:57 UTC, deadalnix wrote:On Thursday, 15 January 2015 at 21:29:28 UTC, Tobias Müller wrote:'tis true, unfortunately implementations don't match the spec... But that ought to be machine verifiable. Shitty compilers.In a well-designed interface, every function specifies all the errors that might happen.BWAHAHAHAHAHAHAHAHAHAHAHAHAHAHAHAHAHAHAHAHAHAHAHAHA !
Jan 15 2015
On Fri, 16 Jan 2015 01:01:26 +0000 via Digitalmars-d <digitalmars-d puremagic.com> wrote:On Friday, 16 January 2015 at 00:48:57 UTC, deadalnix wrote:so human must do all machine wants from him, until that machine is pleased? i like it. and i hate humans anyway, so it's fun to see them suffering.On Thursday, 15 January 2015 at 21:29:28 UTC, Tobias M=C3=BCller=20 wrote:=20 'tis true, unfortunately implementations don't match the spec... =20 But that ought to be machine verifiable. Shitty compilers.In a well-designed interface, every function specifies all the=20 errors that might happen.BWAHAHAHAHAHAHAHAHAHAHAHAHAHAHAHAHAHAHAHAHAHAHAHAHA !
Jan 16 2015
On Sunday, 11 January 2015 at 13:06:27 UTC, Dicebot wrote:What is your opinion of approach advertised by various functional languages and now also Rust? Where you return error code packed with actual data and can't access data without visiting error code too, compiler simply won't allow it.Rust has an approach very similar to exception, but they do not unwind and trash the whole task instead. Under the hood, it is exception, but tend to be faster as you don't go through the landing pad/personality function dance, but do not fundamentally differ. The second approach is to pack the result in some kind of object that require checking (but you often don't have anything meaningful to do on failure where you need to check) or propagate the wrapper, Ã la maybe monad, and get rid of the wrapper where you know what to do on error. These approach tend to be faster while keeping safety, but requiring more work from the dev. They make sense if the error is common, but are not pulling their weight for very rare failure scenarios like disc running out of space.
Jan 12 2015
On Monday, 12 January 2015 at 20:07:17 UTC, deadalnix wrote:On Sunday, 11 January 2015 at 13:06:27 UTC, Dicebot wrote:It is difficult to introduce exceptions without causing problems for linear typing. http://www.serc.iisc.ernet.in/~govind/NHC-07/final/ExceptionsSlides.pdfWhat is your opinion of approach advertised by various functional languages and now also Rust? Where you return error code packed with actual data and can't access data without visiting error code too, compiler simply won't allow it.Rust has an approach very similar to exception, but they do not unwind and trash the whole task instead. Under the hood, it is exception, but tend to be faster as you don't go through the landing pad/personality function dance, but do not fundamentally differ.
Jan 12 2015
On Monday, 12 January 2015 at 20:07:17 UTC, deadalnix wrote:On Sunday, 11 January 2015 at 13:06:27 UTC, Dicebot wrote:Exceptions in Rust are more like Errors in D (but they terminate tasks, not the process!), Result wrapper seems to be standard approach for tasks D uses exceptions for, thus my original question.What is your opinion of approach advertised by various functional languages and now also Rust? Where you return error code packed with actual data and can't access data without visiting error code too, compiler simply won't allow it.Rust has an approach very similar to exception, but they do not unwind and trash the whole task instead. Under the hood, it is exception, but tend to be faster as you don't go through the landing pad/personality function dance, but do not fundamentally differ. The second approach is to pack the result in some kind of object that require checking (but you often don't have anything meaningful to do on failure where you need to check) or propagate the wrapper, Ã la maybe monad, and get rid of the wrapper where you know what to do on error. These approach tend to be faster while keeping safety, but requiring more work from the dev. They make sense if the error is common, but are not pulling their weight for very rare failure scenarios like disc running out of space.
Jan 12 2015
On Monday, 12 January 2015 at 20:07:17 UTC, deadalnix wrote:On Sunday, 11 January 2015 at 13:06:27 UTC, Dicebot wrote:I think it does unwind (i.e. call destructors), but the exception cannot be called and ultimately will lead to task failure. At least this was the case some time ago, maybe it has changed?What is your opinion of approach advertised by various functional languages and now also Rust? Where you return error code packed with actual data and can't access data without visiting error code too, compiler simply won't allow it.Rust has an approach very similar to exception, but they do not unwind and trash the whole task instead. Under the hood, it is exception, but tend to be faster as you don't go through the landing pad/personality function dance, but do not fundamentally differ.
Jan 13 2015
On 11 January 2015 at 10:48, Walter Bright via Digitalmars-d <digitalmars-d puremagic.com> wrote:Over the last few days, I have been getting weird errors from various programs I run on Windows 7. Programs would just fail, or produce corrupt output (I'm looking at you, Windows Moviemaker). I have a 128Gb SSD drive for my system drive, for speed, and a 4T secondary spinning drive for storage. I knew I was getting nearly out of space on the SSD drive, and had a 256Gb SSD drive ready to swap in. To migrate the system to the new disk, the idea was to use Windows Backup/Restore of the image. Unfortunately, Windows Backup would get a ways through the process, then fail with no message beyond an error code. Googling the error code produced a vast number of results, and various schemes for fixing it, some official, some not. None of them were applicable. Many involved loading new drivers, editting the system registry, and all sort of unappealing "solutions".Up to this point, I you reminded me of a talk by Joe Armstrong (inventor of Erlang) https://www.youtube.com/watch?v=lKXe3HUG2l4
Jan 12 2015
On 1/12/2015 2:04 AM, Iain Buclaw via Digitalmars-d wrote:Up to this point, I you reminded me of a talk by Joe Armstrong (inventor of Erlang) https://www.youtube.com/watch?v=lKXe3HUG2l4I've found another problem with Windows Moviemaker, it can read, but cannot write, movies larger than 4Gb. Looks like some sort of internal 32 bit overflow. I am once again a unique snowflake, because in googling around I can find nobody else with this issue. (I have many 6Gb video files, as they come from my old 6 hour VHS tapes made in the 80's. I decided to digitize them and throw the tapes away. VHS tapes are only supposed to last 10 years, I am frankly amazed that they are in good shape after 30. My video capture device generates about 1Gb per hour of video.)
Jan 12 2015
On 1/12/15, Walter Bright via Digitalmars-d <digitalmars-d puremagic.com> wrote:I've found another problem with Windows Moviemaker, it can read, but cannot write, movies larger than 4Gb. Looks like some sort of internal 32 bit overflow.If you haven't read this I highly recommend it, just for the entertainment value: http://blog.seattlepi.com/microsoft/2008/06/24/full-text-an-epic-bill-gates-e-mail-rant/
Jan 12 2015
On 1/12/2015 3:28 AM, Andrej Mitrovic via Digitalmars-d wrote:If you haven't read this I highly recommend it, just for the entertainment value: http://blog.seattlepi.com/microsoft/2008/06/24/full-text-an-epic-bill-gates-e-mail-rant/Epic! But we all have such issues. And it ain't just software, either, every machine that has a user interface, like cars and airplanes and dishwashers, have these usability issues that are so obviously wrong yet nobody thought of them.
Jan 12 2015
On Sun, Jan 11, 2015 at 02:48:29AM -0800, Walter Bright via Digitalmars-d wrote: [...]What I'm pretty sure is happening is those programs use error codes for error reporting, and then don't check the error codes. This is common practice for C code. I'm a little surprised that with Windows' long history, it still has problems detecting when it runs out of disk space. However, if exceptions are thrown for errors instead, the programmer has to deliberately add code if he wishes to ignore the error.While I agree with the general sentiment, I think the current convention of using a class hierarchy to implement exceptions is suboptimal. The problem with using a class hierarchy is that, like anything put into a hierarchy, some things just don't fit very well in a single-rooted hierarchy. This is especially true in D because there is no multiple inheritance (and for good reason too; multiple inheritance brings with it a whole set of nasty problems). A recent example was what to do with an exception that wraps around OS-level errors. From an implementor's POV, it makes sense to segregate exception types by implementation, that is, ErrnoException for Posix systems and SystemErrorCodeException (or some such) for Windows. However, this is totally useless to the end user: when you're traversing the filesystem, under this scheme you'd have to catch ErrnoException or catch SystemErrorCodeException (with a static-if on OS type, aka utter ugliness), and then ferret out the specific error code(s) you wish to handle, like what to do with an I/O error vs. a permission-denied error. Why should the *user* have to work with low-level implementation details like mapping Posix errno's and their corresponding Windows error codes to the semantic categories of real interest: i.e., access error / hardware failure / network error, etc.? So from the user's POV, the exception hierarchy ought to be semantically driven, rather than implementationally driven. Instead of ErrnoException and SystemErrorCodeException, one ought to have semantic categories like FileNotFoundException, AccessDeniedException, NetworkException, etc.. However, this is burdensome on the implementor, because now a single underlying implementation like ErrnoException now has to be split across multiple unrelated semantic categories (FileNotFoundException must have an errno field on Posix and a systemErrorCode field on Windows, ditto for NetworkException, IOException, etc. -- and you can't factor it out into a base class because the class hierarchy is semantics driven, so ErrnoException doesn't fit anywhere in the hierarchy). Recently Dmitry (IIRC) came up with the rather clever idea of using interfaces instead of a single-rooted hierarchy for marking up semantic categories instead. Instead of trying to decide on whether we should have implementationally-driven OSException with subclasses ErrnoException and SystemErrorCodeException, or semantically-driven FileSystemException with subclasses FileNotFoundException and AccessDeniedException, we can have both: the class hierarchy itself (i.e. without the interfaces) uses base classes for factoring out implementation details, but tag each exception type with semantic categories derived from a distinct interface hierarchy. So we could have something like this: // Implementational hierarchy class Throwable { ... } class Exception : Throwable { ... } class ErrnoException : Exception { int errno; ... } class SystemErrorCodeException : Exception { int systemErrorCode; ... } // Semantic tags interface FilesystemException; interface FileNotFoundException : FilesystemException; interface AccessDeniedException : FilesystemException; .. // Actual implementations static if (Posix) { // Note: you can even have multiple exception types that // inherit from FileNotFoundException, e.g., one thrown // by a manual stat() check, and one from a common // routine that interprets OS error codes. The user // wouldn't need to know the difference. class FileNotFoundExceptionImpl : ErrnoException, FileNotFoundException { ... } ... // other Posix-specific exceptions here } else static if (Windows) { class FileNotFoundExceptionImpl : SystemErrorCodeException, FileNotFoundException { ... } ... // other Windows-specific exceptions here } So now, user code doesn't have to worry about implementation details like whether the exception came from errno or a Windows error code, you can just catch semantic categories like FileNotFoundException instead. Currently, however, this scheme doesn't quite work because the compiler only allows catching classes derived from Throwable, and interfaces can't derive from non-interface base classes. Besides, I'm not sure the druntime exception catching code can deal with interfaces correctly as opposed to just base classes. But this is definitely an area that D could improve on! T -- Why have vacation when you can work?? -- EC
Jan 12 2015
On 2015-01-12 19:28, H. S. Teoh via Digitalmars-d wrote:While I agree with the general sentiment, I think the current convention of using a class hierarchy to implement exceptions is suboptimal. The problem with using a class hierarchy is that, like anything put into a hierarchy, some things just don't fit very well in a single-rooted hierarchy. This is especially true in D because there is no multiple inheritance (and for good reason too; multiple inheritance brings with it a whole set of nasty problems). A recent example was what to do with an exception that wraps around OS-level errors. From an implementor's POV, it makes sense to segregate exception types by implementation, that is, ErrnoException for Posix systems and SystemErrorCodeException (or some such) for Windows. However, this is totally useless to the end user: when you're traversing the filesystem, under this scheme you'd have to catch ErrnoException or catch SystemErrorCodeException (with a static-if on OS type, aka utter ugliness), and then ferret out the specific error code(s) you wish to handle, like what to do with an I/O error vs. a permission-denied error. Why should the *user* have to work with low-level implementation details like mapping Posix errno's and their corresponding Windows error codes to the semantic categories of real interest: i.e., access error / hardware failure / network error, etc.? So from the user's POV, the exception hierarchy ought to be semantically driven, rather than implementationally driven. Instead of ErrnoException and SystemErrorCodeException, one ought to have semantic categories like FileNotFoundException, AccessDeniedException, NetworkException, etc.. However, this is burdensome on the implementor, because now a single underlying implementation like ErrnoException now has to be split across multiple unrelated semantic categories (FileNotFoundException must have an errno field on Posix and a systemErrorCode field on Windows, ditto for NetworkException, IOException, etc. -- and you can't factor it out into a base class because the class hierarchy is semantics driven, so ErrnoException doesn't fit anywhere in the hierarchy). Recently Dmitry (IIRC) came up with the rather clever idea of using interfaces instead of a single-rooted hierarchy for marking up semantic categories instead. Instead of trying to decide on whether we should have implementationally-driven OSException with subclasses ErrnoException and SystemErrorCodeException, or semantically-driven FileSystemException with subclasses FileNotFoundException and AccessDeniedException, we can have both: the class hierarchy itself (i.e. without the interfaces) uses base classes for factoring out implementation details, but tag each exception type with semantic categories derived from a distinct interface hierarchy. So we could have something like this: // Implementational hierarchy class Throwable { ... } class Exception : Throwable { ... } class ErrnoException : Exception { int errno; ... } class SystemErrorCodeException : Exception { int systemErrorCode; ... } // Semantic tags interface FilesystemException; interface FileNotFoundException : FilesystemException; interface AccessDeniedException : FilesystemException; .. // Actual implementations static if (Posix) { // Note: you can even have multiple exception types that // inherit from FileNotFoundException, e.g., one thrown // by a manual stat() check, and one from a common // routine that interprets OS error codes. The user // wouldn't need to know the difference. class FileNotFoundExceptionImpl : ErrnoException, FileNotFoundException { ... } ... // other Posix-specific exceptions here } else static if (Windows) { class FileNotFoundExceptionImpl : SystemErrorCodeException, FileNotFoundException { ... } ... // other Windows-specific exceptions here } So now, user code doesn't have to worry about implementation details like whether the exception came from errno or a Windows error code, you can just catch semantic categories like FileNotFoundException instead. Currently, however, this scheme doesn't quite work because the compiler only allows catching classes derived from Throwable, and interfaces can't derive from non-interface base classes. Besides, I'm not sure the druntime exception catching code can deal with interfaces correctly as opposed to just base classes. But this is definitely an area that D could improve on!I prefer a semantically driven hierarchy. I agree with you that the user should not need to care if the exception originated from D code with a native D exception or in C code with a error code converted to a D exception. The reason this gets complicated seems to be the need for preserving the error code. Is that actually necessary? If all error codes are properly mapped to a D exception in a nice semantically driven hierarchy I'm not so sure if the error code is needed. It seems to me that the interface approach is quite complicated on the implementation side with a static-if and all. But I guess that can be factored out. I was thinking there could be one function that converts a given error code to a D exception. This hopefully only needs to be written once. Then it will be easy both on the user side and on the implementation side. If you really do need the original error code, perhaps we could put that in a exception in Throwable.next. The implementation side of some operation could look like this: void openFile (in char* path) { FILE* file = fopen(path, "w"); if (!file) throwSystemException(); } The implementation of throwSystemException would be something like: void throwSystemException () { static if (Posix) auto errorCode = errno(); else auto errorCode = GetLastError(); throw errorCodeToThrowable(errorCode); } Throwable errorCodeToThrowable (int errorCode) { Throwable throwable; static if (Posix) { switch (errorCode) { case ENOENT: thorwable = new FileNotFoundException; ... } // not sure if this is necessary throwable.next = new ErrnoException(errorCode); } else { ... } return throwable; } -- /Jacob Carlborg
Jan 13 2015
On 1/11/2015 2:48 AM, Walter Bright wrote:However, if exceptions are thrown for errors instead, the programmer has to deliberately add code if he wishes to ignore the error.Interesting that this article just appeared: https://blog.golang.org/errors-are-values by Rob Pike on error handling in Go. He concludes with: "But remember: Whatever you do, always check your errors!" Which sums up why exceptions are better!
Jan 13 2015
On Wednesday, 14 January 2015 at 00:38:21 UTC, Walter Bright wrote:On 1/11/2015 2:48 AM, Walter Bright wrote:But checked exceptions are definitely not.However, if exceptions are thrown for errors instead, the programmer has to deliberately add code if he wishes to ignore the error.Interesting that this article just appeared: https://blog.golang.org/errors-are-values by Rob Pike on error handling in Go. He concludes with: "But remember: Whatever you do, always check your errors!" Which sums up why exceptions are better!
Jan 13 2015
On Wednesday, 14 January 2015 at 00:40:10 UTC, Meta wrote:On Wednesday, 14 January 2015 at 00:38:21 UTC, Walter Bright wrote:http://www.artima.com/intv/handcuffs.html The best sum up of checked exception you'll find.On 1/11/2015 2:48 AM, Walter Bright wrote:But checked exceptions are definitely not.However, if exceptions are thrown for errors instead, the programmer has to deliberately add code if he wishes to ignore the error.Interesting that this article just appeared: https://blog.golang.org/errors-are-values by Rob Pike on error handling in Go. He concludes with: "But remember: Whatever you do, always check your errors!" Which sums up why exceptions are better!
Jan 13 2015
http://www.artima.com/intv/handcuffs.html The best sum up of checked exception you'll find.False. Unless you mean 'the best sum up of the problems people have with checked exceptions' - it gives a good description of how use of checked exceptions often goes wrong. For those of us that regularly use checked exceptions, these are known problems and easily worked around. Some are not problems but features (i.e. ignoring them is painful and obvious). Granted, a better solution to the problem may be able to avoid problems all together. But as discussed in this newsgroup before, no better solution presents itself. NB: It also seems to have a very skewed view of how to handle errors (exceptions) that come up, preferring to just throw everything up to one handler instead of dealing with at the proper abstraction level. This may be appropriate for certain systems but is not a good strategy in general. If you are doing this then you will likely not see the advantages of checked exceptions.
Jan 13 2015
On Wednesday, 14 January 2015 at 02:40:20 UTC, Jeremy Powers via Digitalmars-d wrote:Full stop. I made that mistake myself various time, so I can talk from experience here. This is a completely wrong mindset. If people have problem with checked exception, then there IS a problem with checked exception, not people. You won't be able to change people, so you'd better focus on changing checked exceptions.http://www.artima.com/intv/handcuffs.html The best sum up of checked exception you'll find.False. Unless you mean 'the best sum up of the problems people have with checked exceptions' - it gives a good description of how use of checked exceptions often goes wrong.
Jan 13 2015
On Tue, Jan 13, 2015 at 8:26 PM, deadalnix via Digitalmars-d < digitalmars-d puremagic.com> wrote:On Wednesday, 14 January 2015 at 02:40:20 UTC, Jeremy Powers via Digitalmars-d wrote:You are completely right. Let me rephrase: The article gives a good overview of the places where checked exceptions are weak, and where people have problems with them. The things it mentions have best-practice work arounds (or are actually features), and do not invalidate all the usefulness that checked exceptions provide. It would be great if there were some other scheme instead of checked exceptions, or a slightly different design of them, if it gave me the same advantages without the issues. Unfortunately I have not seen one yet.Full stop. I made that mistake myself various time, so I can talk from experience here. This is a completely wrong mindset. If people have problem with checked exception, then there IS a problem with checked exception, not people. You won't be able to change people, so you'd better focus on changing checked exceptions.http://www.artima.com/intv/handcuffs.html The best sum up of checked exception you'll find.False. Unless you mean 'the best sum up of the problems people have with checked exceptions' - it gives a good description of how use of checked exceptions often goes wrong.
Jan 14 2015