www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - Why exceptions for error handling is so important

reply Walter Bright <newshound2 digitalmars.com> writes:
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
next sibling parent reply Walter Bright <newshound2 digitalmars.com> writes:
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
parent "Robert burner Schadek" <rburners gmail.com> writes:
to not let ranges succumb to such a problem I wrote: 
https://github.com/D-Programming-Language/phobos/pull/2724
Jan 11 2015
prev sibling next sibling parent reply "Dicebot" <public dicebot.lv> writes:
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
next sibling parent reply ketmar via Digitalmars-d <digitalmars-d puremagic.com> writes:
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
parent "thedeemon" <dlang thedeemon.com> writes:
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:

 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!
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.
Jan 11 2015
prev sibling next sibling parent Andrej Mitrovic via Digitalmars-d <digitalmars-d puremagic.com> writes:
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
prev sibling next sibling parent Andrej Mitrovic via Digitalmars-d <digitalmars-d puremagic.com> writes:
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
prev sibling next sibling parent ketmar via Digitalmars-d <digitalmars-d puremagic.com> writes:
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:
 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?
`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.
Jan 11 2015
prev sibling next sibling parent reply Walter Bright <newshound2 digitalmars.com> writes:
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
next sibling parent reply =?UTF-8?Q?Tobias=20M=C3=BCller?= <troplin bluewin.ch> writes:
Walter Bright <newshound2 digitalmars.com> wrote:
 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).
- 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.
Jan 11 2015
next sibling parent reply Walter Bright <newshound2 digitalmars.com> writes:
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
parent =?UTF-8?Q?Tobias=20M=C3=BCller?= <troplin bluewin.ch> writes:
Walter Bright <newshound2 digitalmars.com> wrote:
 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.
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.
Jan 12 2015
prev sibling parent reply "weaselcat" <weaselcat gmail.com> writes:
On Monday, 12 January 2015 at 07:09:54 UTC, Tobias Müller wrote:
 Walter Bright <newshound2 digitalmars.com> wrote:
 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).
- 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 11 2015
parent =?UTF-8?Q?Tobias=20M=C3=BCller?= <troplin bluewin.ch> writes:
"weaselcat" <weaselcat gmail.com> wrote:
 On Monday, 12 January 2015 at 07:09:54 UTC, 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.
There's nothing stopping you from just throwing errors out in Rust(last I used it anyways) via empty match statements and/or unwrap.
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.
Jan 12 2015
prev sibling next sibling parent reply "Tobias Pankrath" <tobias pankrath.net> writes:
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
parent reply Walter Bright <newshound2 digitalmars.com> writes:
On 1/11/2015 11:53 PM, Tobias Pankrath wrote:
 On Monday, 12 January 2015 at 00:51:25 UTC, Walter Bright wrote:
 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/
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.
Jan 12 2015
next sibling parent reply "bearophile" <bearophileHUGS lycos.com> writes:
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
parent "Ola Fosheim =?UTF-8?B?R3LDuHN0YWQi?= writes:
On Monday, 12 January 2015 at 09:41:08 UTC, bearophile wrote:
 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.
Yes, dataflow.
Jan 12 2015
prev sibling next sibling parent "Paulo Pinto" <pjmlp progtools.org> writes:
On Monday, 12 January 2015 at 09:31:25 UTC, Walter Bright wrote:
 On 1/11/2015 11:53 PM, Tobias Pankrath wrote:
 On Monday, 12 January 2015 at 00:51:25 UTC, Walter Bright 
 wrote:
 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/
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.
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 combinators
Jan 12 2015
prev sibling parent reply "Tobias Pankrath" <tobias pankrath.net> writes:
On Monday, 12 January 2015 at 09:31:25 UTC, Walter Bright wrote:
 On 1/11/2015 11:53 PM, Tobias Pankrath wrote:
 On Monday, 12 January 2015 at 00:51:25 UTC, Walter Bright 
 wrote:
 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/
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.
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 is usually Choice in F#. Now in D we use the opDot to chain components, which works since we have UFCS. In F# there a simply three different opDots: >>,
= and >=> which take care of the adaption.
Jan 12 2015
parent reply Walter Bright <newshound2 digitalmars.com> writes:
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
 is usually Choice in F#.

 Now in D we use the opDot to chain components, which works since we have UFCS.
 In F# there a simply three different opDots: >>, >>= and >=> which take care of
 the adaption.
Or we could just use exceptions, which require none of that.
Jan 12 2015
next sibling parent "Tobias Pankrath" <tobias pankrath.net> writes:
On Monday, 12 January 2015 at 11:09:01 UTC, Walter Bright wrote:
 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
 is usually Choice in F#.

 Now in D we use the opDot to chain components, which works 
 since we have UFCS.
 In F# there a simply three different opDots: >>, >>= and >=> 
 which take care of
 the adaption.
Or we could just use exceptions, which require none of that.
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. Haven't programmed anything of value in F# though.
Jan 12 2015
prev sibling next sibling parent reply "Dicebot" <public dicebot.lv> writes:
On Monday, 12 January 2015 at 11:09:01 UTC, Walter Bright wrote:
 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
 is usually Choice in F#.

 Now in D we use the opDot to chain components, which works 
 since we have UFCS.
 In F# there a simply three different opDots: >>, >>= and >=> 
 which take care of
 the adaption.
Or we could just use exceptions, which require none of that.
.. 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.
Jan 12 2015
parent reply Walter Bright <newshound2 digitalmars.com> writes:
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
parent reply "Ola Fosheim =?UTF-8?B?R3LDuHN0YWQi?= writes:
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 propagating
Does this mean that D will get fast EH?
Jan 12 2015
parent reply "Adam D. Ruppe" <destructionator gmail.com> writes:
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
parent reply "Ola Fosheim =?UTF-8?B?R3LDuHN0YWQi?= writes:
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:
 Does this mean that D will get fast EH?
It is fast already...
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?
Jan 12 2015
next sibling parent reply "Matthias Bentrup" <matthias.bentrup googlemail.com> writes:
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:
 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...
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?
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.
Jan 12 2015
parent "Ola Fosheim =?UTF-8?B?R3LDuHN0YWQi?= writes:
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
prev sibling next sibling parent reply "Adam D. Ruppe" <destructionator gmail.com> writes:
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 Java and C#; it 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.
 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
next sibling parent "Ola Fosheim =?UTF-8?B?R3LDuHN0YWQi?= writes:
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:
 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 Java and C#; it 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.
 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.
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).
Jan 12 2015
prev sibling next sibling parent reply "H. S. Teoh via Digitalmars-d" <digitalmars-d puremagic.com> writes:
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 Grstad 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 Java and C#; it isn't awful.
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=9584
 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. Still, #9584 had some discussions about doing lazy stack trace 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
next sibling parent reply =?UTF-8?Q?Tobias=20M=C3=BCller?= <troplin bluewin.ch> writes:
"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: 
 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.
But what's exceptional for you is normal for me. C#'s Dictionary has TryGetValue that returns a bool in addition to the normal indexing operator [] that throws, exactly for that reason. And this is no exception (no pun intended), there are several cases like that in C#. IMO there's something wrong if you have to resort to such code duplification.
Jan 12 2015
next sibling parent reply "H. S. Teoh via Digitalmars-d" <digitalmars-d puremagic.com> writes:
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:
 On Mon, Jan 12, 2015 at 05:22:26PM +0000, Adam D. Ruppe via Digitalmars-d
wrote: 
 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.
But what's exceptional for you is normal for me. C#'s Dictionary has TryGetValue that returns a bool in addition to the normal indexing operator [] that throws, exactly for that reason. And this is no exception (no pun intended), there are several cases like that in C#. IMO there's something wrong if you have to resort to such 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 []. T -- Маленькие детки - маленькие бедки.
Jan 12 2015
next sibling parent "Ola Fosheim =?UTF-8?B?R3LDuHN0YWQi?= writes:
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
prev sibling parent reply =?UTF-8?Q?Tobias=20M=C3=BCller?= <troplin bluewin.ch> writes:
"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:
 C#'s Dictionary has TryGetValue that returns a bool in addition to the
 normal indexing operator []  that throws, exactly for that reason. And
 this is no exception (no pun intended), there are several cases like
 that in C#.  IMO there's something wrong if you have to resort to such
 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[].
Just stick with one. Some kind of Optional<T> would be a good fit.
 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
parent Russel Winder via Digitalmars-d <digitalmars-d puremagic.com> writes:
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
prev sibling next sibling parent reply Russel Winder via Digitalmars-d <digitalmars-d puremagic.com> writes:
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
next sibling parent reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
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:

 […]
 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.
How can you like undefined? That's a terrible solution again underlining Go's conflation of union and product types. -- Andrei
Jan 12 2015
next sibling parent Russel Winder via Digitalmars-d <digitalmars-d puremagic.com> writes:
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:
 
 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.
How can you like undefined? That's a terrible solution again underlining Go's conflation of union and product types. -- Andrei
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_winder
Jan 13 2015
prev sibling parent reply "Paulo Pinto" <pjmlp progtools.org> writes:
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:
 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.
How can you like undefined? That's a terrible solution again underlining Go's conflation of union and product types. -- Andrei
They posted recently a blog post about error handling in Go. https://blog.golang.org/errors-are-values -- Paulo
Jan 13 2015
next sibling parent reply "Atila Neves" <atila.neves gmail.com> writes:
That post, to me, only reinforces how much better using 
exceptions is.

Atila

 They posted recently a blog post about error handling in Go.

 https://blog.golang.org/errors-are-values

 --
 Paulo
Jan 14 2015
parent reply "Paulo Pinto" <pjmlp progtools.org> writes:
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.

 Atila

 They posted recently a blog post about error handling in Go.

 https://blog.golang.org/errors-are-values

 --
 Paulo
Agree, on the other hand is another example of the mindset. -- Paulo
Jan 14 2015
next sibling parent Jeremy Powers via Digitalmars-d <digitalmars-d puremagic.com> writes:
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:

 That post, to me, only reinforces how much better using exceptions is.

 Atila

  They posted recently a blog post about error handling in Go.
 https://blog.golang.org/errors-are-values

 --
 Paulo
Agree, on the other hand is another example of the mindset.
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.
Jan 14 2015
prev sibling parent "H. S. Teoh via Digitalmars-d" <digitalmars-d puremagic.com> writes:
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:
 
 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.

 Atila

  They posted recently a blog post about error handling in Go.
 https://blog.golang.org/errors-are-values

 --
 Paulo
Agree, on the other hand is another example of the mindset.
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.
+1. T -- Meat: euphemism for dead animal. -- Flora
Jan 14 2015
prev sibling parent ketmar via Digitalmars-d <digitalmars-d puremagic.com> writes:
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-values
great article! it clearly shows how NOT to do error handling and why exceptions are far superior.
Jan 14 2015
prev sibling next sibling parent "Tobias Pankrath" <tobias pankrath.net> writes:
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:
 
[…]
 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.
That's basically the same as a pointer to the value. If the pointer is null, dereferencing it is undefined.
Jan 12 2015
prev sibling parent reply Walter Bright <newshound2 digitalmars.com> writes:
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
parent reply Russel Winder via Digitalmars-d <digitalmars-d puremagic.com> writes:
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:
 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.
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_winder
Jan 13 2015
parent Walter Bright <newshound2 digitalmars.com> writes:
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:
 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.
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.
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.
 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
prev sibling parent reply Walter Bright <newshound2 digitalmars.com> writes:
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:
 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'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 :-)
Jan 12 2015
next sibling parent Russel Winder via Digitalmars-d <digitalmars-d puremagic.com> writes:
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
prev sibling parent reply "Tobias =?UTF-8?B?TcO8bGxlciI=?= <troplin bluewin.ch> writes:
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:
 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'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.
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*.
Jan 15 2015
parent reply "H. S. Teoh via Digitalmars-d" <digitalmars-d puremagic.com> writes:
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:
On 1/12/2015 10:06 AM, Tobias Mller wrote:
On Mon, Jan 12, 2015 at 05:22:26PM +0000, Adam D. Ruppe 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.
But what's exceptional for you is normal for me.
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.
But what this means that exceptions should only ever be used for catching programming errors, some kind of "soft" assertions.
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.
 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
parent reply "Tobias M" <troplin bluewin.ch> writes:
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
next sibling parent "Ola Fosheim =?UTF-8?B?R3LDuHN0YWQi?= writes:
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
prev sibling parent reply "deadalnix" <deadalnix gmail.com> writes:
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
parent reply "Marc =?UTF-8?B?U2Now7x0eiI=?= <schuetzm gmx.net> writes:
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
parent "Tobias Pankrath" <tobias pankrath.net> writes:
 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
prev sibling next sibling parent reply "Adam D. Ruppe" <destructionator gmail.com> writes:
On Monday, 12 January 2015 at 17:57:10 UTC, H. S. Teoh via 
Digitalmars-d wrote:
 Still, #9584 had some discussions about doing lazy stack trace
 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
parent "deadalnix" <deadalnix gmail.com> writes:
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:
 Still, #9584 had some discussions about doing lazy stack trace
 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.
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.
Jan 12 2015
prev sibling parent reply "Ola Fosheim =?UTF-8?B?R3LDuHN0YWQi?= writes:
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
next sibling parent reply ketmar via Digitalmars-d <digitalmars-d puremagic.com> writes:
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
parent "Ola Fosheim =?UTF-8?B?R3LDuHN0YWQi?= writes:
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:

 returning state with exceptions
(banging his head against a wall) NO. THIS NEVER HAS ANY SENSE.
Sure it has. It is a state machine. You cannot not return state. :-P
Jan 12 2015
prev sibling next sibling parent reply Walter Bright <newshound2 digitalmars.com> writes:
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
parent "Ola Fosheim =?UTF-8?B?R3LDuHN0YWQi?= writes:
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
prev sibling parent reply "deadalnix" <deadalnix gmail.com> writes:
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:
 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.
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.
Jan 12 2015
parent reply "Ola Fosheim =?UTF-8?B?R3LDuHN0YWQi?= writes:
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
next sibling parent reply "Marc =?UTF-8?B?U2Now7x0eiI=?= <schuetzm gmx.net> writes:
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:
 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.
I usually don't do this, but I really need to post a "+1" here: +1
Jan 13 2015
parent reply "deadalnix" <deadalnix gmail.com> writes:
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:
 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.
I usually don't do this, but I really need to post a "+1" here: +1
Too bad you chose to do this on a bad strawmman.
Jan 13 2015
parent reply "Marc =?UTF-8?B?U2Now7x0eiI=?= <schuetzm gmx.net> writes:
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:
 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:
 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.
I usually don't do this, but I really need to post a "+1" here: +1
Too bad you chose to do this on a bad strawmman.
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.
Jan 14 2015
parent reply "deadalnix" <deadalnix gmail.com> writes:
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
next sibling parent "Ola Fosheim =?UTF-8?B?R3LDuHN0YWQi?= writes:
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
prev sibling parent reply "Marc =?UTF-8?B?U2Now7x0eiI=?= <schuetzm gmx.net> writes:
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:
 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.
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.
Jan 15 2015
parent "Ola Fosheim =?UTF-8?B?R3LDuHN0YWQi?= writes:
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
prev sibling parent Dmitry Olshansky <dmitry.olsh gmail.com> writes:
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:
 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.
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 Olshansky
Jan 13 2015
prev sibling parent reply Russel Winder via Digitalmars-d <digitalmars-d puremagic.com> writes:
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
next sibling parent reply Walter Bright <newshound2 digitalmars.com> writes:
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
parent reply Russel Winder via Digitalmars-d <digitalmars-d puremagic.com> writes:
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:
 Unless you are writing Python code.
Nobody would be in this forum if we preferred Python :-)
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_winder
Jan 13 2015
next sibling parent reply "John Colvin" <john.loughran.colvin gmail.com> writes:
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:
 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 :-)
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!
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/pyd
Jan 13 2015
parent reply Russel Winder via Digitalmars-d <digitalmars-d puremagic.com> writes:
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
parent reply "Paulo Pinto" <pjmlp progtools.org> writes:
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:
 
[…]
 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.
Which doesn't scale in software that requires plugins. Imagine Eclipse, for example, implemented as a collection of processes. -- Paulo
Jan 13 2015
parent Russel Winder via Digitalmars-d <digitalmars-d puremagic.com> writes:
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
prev sibling parent reply "Ola Fosheim =?UTF-8?B?R3LDuHN0YWQi?= writes:
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:
 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 :-)
Why does one have to "prefer" a language to the exclusion of all others (unless one is the author of a language ;-)
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?
 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
I 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
parent reply "Paulo Pinto" <pjmlp progtools.org> writes:
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:
 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:
 Unless you are writing Python code.
Nobody would be in this forum if we preferred Python :-)
Why does one have to "prefer" a language to the exclusion of all others (unless one is the author of a language ;-)
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?
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. -- Paulo
Jan 13 2015
parent reply "Ola Fosheim =?UTF-8?B?R3LDuHN0YWQi?= writes:
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
parent "Paulo Pinto" <pjmlp progtools.org> writes:
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:
 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.
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. -- Paulo
Jan 13 2015
prev sibling parent reply "Ulrich =?UTF-8?B?S8O8dHRsZXIi?= <kuettler gmail.com> writes:
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:
 
[…]
 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.
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.
Jan 13 2015
parent Russel Winder via Digitalmars-d <digitalmars-d puremagic.com> writes:
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
prev sibling parent "deadalnix" <deadalnix gmail.com> writes:
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:
 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...
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?
LDC, GDC and SDC do all use this.
Jan 12 2015
prev sibling parent reply "John Colvin" <john.loughran.colvin gmail.com> writes:
On Monday, 12 January 2015 at 11:09:01 UTC, Walter Bright wrote:
 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
 is usually Choice in F#.

 Now in D we use the opDot to chain components, which works 
 since we have UFCS.
 In F# there a simply three different opDots: >>, >>= and >=> 
 which take care of
 the adaption.
Or we could just use exceptions, which require none of that.
Interesting little rant about exceptions (and more), from the author of a large and successful project in C++ http://250bpm.com/blog:4
Jan 12 2015
parent reply "deadalnix" <deadalnix gmail.com> writes:
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:
 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
 is usually Choice in F#.

 Now in D we use the opDot to chain components, which works 
 since we have UFCS.
 In F# there a simply three different opDots: >>, >>= and >=> 
 which take care of
 the adaption.
Or we could just use exceptions, which require none of that.
Interesting little rant about exceptions (and more), from the author of a large and successful project in C++ http://250bpm.com/blog:4
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.
Jan 12 2015
next sibling parent "H. S. Teoh via Digitalmars-d" <digitalmars-d puremagic.com> writes:
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:
[...]
Interesting little rant about exceptions (and more), from the author
of a large and successful project in C++     http://250bpm.com/blog:4
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.
I used to throw char* in C++. 'Nuff said. :-) T -- This is a tpyo.
Jan 12 2015
prev sibling parent reply "Paulo Pinto" <pjmlp progtools.org> writes:
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:
 On Monday, 12 January 2015 at 11:09:01 UTC, Walter Bright 
 wrote:
 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
 is usually Choice in F#.

 Now in D we use the opDot to chain components, which works 
 since we have UFCS.
 In F# there a simply three different opDots: >>, >>= and >=> 
 which take care of
 the adaption.
Or we could just use exceptions, which require none of that.
Interesting little rant about exceptions (and more), from the author of a large and successful project in C++ http://250bpm.com/blog:4
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.
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. -- Paulo
Jan 12 2015
parent reply "Ola Fosheim =?UTF-8?B?R3LDuHN0YWQi?= writes:
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
parent reply "bearophile" <bearophileHUGS lycos.com> writes:
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
next sibling parent reply "Ola Fosheim =?UTF-8?B?R3LDuHN0YWQi?= writes:
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
parent reply "bearophile" <bearophileHUGS lycos.com> writes:
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 (or even better the computational thinghies of F#) to D, but past 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
parent "Ola Fosheim =?UTF-8?B?R3LDuHN0YWQi?= writes:
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.
 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 F#) to D, but past 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.
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.
Jan 13 2015
prev sibling parent "Paulo Pinto" <pjmlp progtools.org> writes:
On Monday, 12 January 2015 at 23:18:52 UTC, bearophile wrote:
 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
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
Jan 13 2015
prev sibling next sibling parent reply "Martin Nowak" <code dawg.eu> writes:
On Monday, 12 January 2015 at 00:51:25 UTC, Walter Bright wrote:
 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
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.html
Jan 12 2015
parent reply Walter Bright <newshound2 digitalmars.com> writes:
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.html
Yes, 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
next sibling parent reply "Dicebot" <public dicebot.lv> writes:
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
parent reply Walter Bright <newshound2 digitalmars.com> writes:
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
parent reply "Dicebot" <public dicebot.lv> writes:
On Monday, 12 January 2015 at 22:13:10 UTC, Walter Bright wrote:
 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.
Which is equivalent to "don't use exceptions on servers" :) Yes, I know, this is why any alternative approach is worth interest.
Jan 12 2015
parent "Martin Nowak" <code dawg.eu> writes:
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
prev sibling next sibling parent reply "bearophile" <bearophileHUGS lycos.com> writes:
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 the F#/Haskell way of managing errors is good in those 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
parent reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
On 1/12/15 1:35 PM, bearophile wrote:
 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 the F#/Haskell way of managing errors is good in those 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.
I can't believe I agree with everything bearophile just said :o). -- Andrei
Jan 12 2015
parent "Martin Nowak" <code dawg.eu> writes:
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). -- Andrei
But 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
prev sibling parent reply "Martin Nowak" <code dawg.eu> writes:
On Monday, 12 January 2015 at 21:11:44 UTC, Walter Bright wrote:
 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.html
Yes, it still appears to be just a wrapper around returning two values, and that has to be done for everything.
Yes, you wrap the return values, but it doesn't require to deal with errors in B.
 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
parent reply Walter Bright <newshound2 digitalmars.com> writes:
On 1/12/2015 1:46 PM, Martin Nowak 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.
To defend that argument we'd first have to fix our own codegen. https://issues.dlang.org/show_bug.cgi?id=12442
That issue has nothing to do with exception handling vs error codes.
 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.
Yes, it does. Returning an int in EAX now becomes returning a pair [EAX,EDX].
Jan 12 2015
next sibling parent reply "deadalnix" <deadalnix gmail.com> writes:
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
next sibling parent reply "H. S. Teoh via Digitalmars-d" <digitalmars-d puremagic.com> writes:
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:
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.
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. Erdos
Jan 12 2015
parent reply "deadalnix" <deadalnix gmail.com> writes:
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:
 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.
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.
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.
Jan 12 2015
parent reply Walter Bright <newshound2 digitalmars.com> writes:
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
parent reply "Ola Fosheim =?UTF-8?B?R3LDuHN0YWQi?= writes:
On Tuesday, 13 January 2015 at 03:05:57 UTC, Walter Bright wrote:
 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
xor on Haswell has a reciprocal throughput of 0.25, meaning execution of 4 instructions per cycle per core.
 3. it prevents EDX from being used by the return value, when 
 the return value is larger than a single register size
Store 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 optimized
D 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
parent reply "Ola Fosheim =?UTF-8?B?R3LDuHN0YWQi?= writes:
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
parent reply "deadalnix" <deadalnix gmail.com> writes:
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 thread
I 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
parent reply "Ola Fosheim =?UTF-8?B?R3LDuHN0YWQi?= writes:
On Tuesday, 13 January 2015 at 23:40:41 UTC, deadalnix wrote:
 1. Restricting the exception object to 512 bytes, preallocated 
 in TLS.
Exception can bubble from one thread to another.
How? If you are thinking coroutines, then the exception can be moved with it.
 2. Only have one exception in flight per thread
I don't see how you could have more.
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.
 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 ?
1. So that you can mark a non-D function with checkexception and allow it to both cast D-exceptions and propagate D-exceptions.
 Skipping 4 and 5, you obviously didn't though that through.
?
Jan 13 2015
parent reply "deadalnix" <deadalnix gmail.com> writes:
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:
 1. Restricting the exception object to 512 bytes, 
 preallocated in TLS.
Exception can bubble from one thread to another.
How? If you are thinking coroutines, then the exception can be moved with it.
http://dlang.org/phobos/core_thread.html#.Thread.join
 2. Only have one exception in flight per thread
I don't see how you could have more.
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.
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.
 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 ?
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.
 Skipping 4 and 5, you obviously didn't though that through.
?
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.
Jan 13 2015
parent reply "Ola Fosheim =?UTF-8?B?R3LDuHN0YWQi?= writes:
On Tuesday, 13 January 2015 at 23:58:53 UTC, deadalnix wrote:
 http://dlang.org/phobos/core_thread.html#.Thread.join
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.
 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.
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.
 Skipping 4 and 5, you obviously didn't though that through.
?
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.
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.
Jan 13 2015
next sibling parent reply "deadalnix" <deadalnix gmail.com> writes:
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.
 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.
You write that paragraph like there is some logical links between elements in it, but there is none.
 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
parent reply "Ola Fosheim =?UTF-8?B?R3LDuHN0YWQi?= writes:
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 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.
I said value. Use bitmasks. Class hierarchies don't work very well.
 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.
If you cannot follow the logic... Where did you get lost?
 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.
 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.
This does not take a very smart compiler. You can use heuristics, such as ones using the function signature.
 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
next sibling parent "Ola Fosheim =?UTF-8?B?R3LDuHN0YWQi?= writes:
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
prev sibling parent reply "deadalnix" <deadalnix gmail.com> writes:
On Wednesday, 14 January 2015 at 07:49:09 UTC, Ola Fosheim 
Grøstad wrote:
 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.
Once again, no specifics.
 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?
I was not lost. I know how to recognize a non sequitur when I read one.
 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's a good thing that you can do all of these in D already.
 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.
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 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.
This does not take a very smart compiler. You can use heuristics, such as ones using the function signature.
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.
 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.
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.
Jan 14 2015
parent "Ola Fosheim =?UTF-8?B?R3LDuHN0YWQi?= writes:
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.
 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.
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.
 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
prev sibling parent reply "Marc =?UTF-8?B?U2Now7x0eiI=?= <schuetzm gmx.net> writes:
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:
 http://dlang.org/phobos/core_thread.html#.Thread.join
I don't see the problem. I'm suggesting value semantics, it can be copied.
Without addressing anything else: You want moving here, not copying.
Jan 14 2015
parent reply "Ola Fosheim =?UTF-8?B?R3LDuHN0YWQi?= writes:
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
parent reply "Marc =?UTF-8?B?U2Now7x0eiI=?= <schuetzm gmx.net> writes:
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:
 Without addressing anything else: You want moving here, not 
 copying.
How can you move a value, it has no identity?
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).
Jan 15 2015
parent "Ola Fosheim =?UTF-8?B?R3LDuHN0YWQi?= writes:
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
prev sibling parent reply Walter Bright <newshound2 digitalmars.com> writes:
On 1/12/2015 3:17 PM, deadalnix wrote:
 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.
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.
Jan 12 2015
next sibling parent "deadalnix" <deadalnix gmail.com> writes:
On Tuesday, 13 January 2015 at 00:05:31 UTC, Walter Bright wrote:
 On 1/12/2015 3:17 PM, deadalnix wrote:
 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.
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.
Quoting myself:
 it become very bad when it do not fit in register anymore.
So we agree :)
Jan 12 2015
prev sibling parent reply "Marc =?UTF-8?B?U2Now7x0eiI=?= <schuetzm gmx.net> writes:
On Tuesday, 13 January 2015 at 00:05:31 UTC, Walter Bright wrote:
 On 1/12/2015 3:17 PM, deadalnix wrote:
 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.
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.
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.
Jan 13 2015
parent Walter Bright <newshound2 digitalmars.com> writes:
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:
 On 1/12/2015 3:17 PM, deadalnix wrote:
 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.
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.
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).
Such mitigation techniques pretty much confirms there's an efficiency cost to having the error code.
 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
prev sibling parent "Martin Nowak" <code dawg.eu> writes:
On Monday, 12 January 2015 at 22:17:57 UTC, Walter Bright wrote:
 To defend that argument we'd first have to fix our own codegen.
 https://issues.dlang.org/show_bug.cgi?id=12442
That issue has nothing to do with exception handling vs error codes.
If you start to discuss register allocation than the actual cost of "zero-cost" EH has quite a lot to do with it.
Jan 12 2015
prev sibling parent reply "Tobias =?UTF-8?B?TcO8bGxlciI=?= <troplin bluewin.ch> writes:
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
parent reply "deadalnix" <deadalnix gmail.com> writes:
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
parent reply "Ola Fosheim =?UTF-8?B?R3LDuHN0YWQi?= writes:
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:
 In a well-designed interface, every function specifies all the 
 errors that might happen.
BWAHAHAHAHAHAHAHAHAHAHAHAHAHAHAHAHAHAHAHAHAHAHAHAHA !
'tis true, unfortunately implementations don't match the spec... But that ought to be machine verifiable. Shitty compilers.
Jan 15 2015
parent ketmar via Digitalmars-d <digitalmars-d puremagic.com> writes:
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:
 On Thursday, 15 January 2015 at 21:29:28 UTC, Tobias M=C3=BCller=20
 wrote:
 In a well-designed interface, every function specifies all the=20
 errors that might happen.
BWAHAHAHAHAHAHAHAHAHAHAHAHAHAHAHAHAHAHAHAHAHAHAHAHA !
=20 'tis true, unfortunately implementations don't match the spec... =20 But that ought to be machine verifiable. Shitty compilers.
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.
Jan 16 2015
prev sibling parent reply "deadalnix" <deadalnix gmail.com> writes:
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
next sibling parent "Ola Fosheim =?UTF-8?B?R3LDuHN0YWQi?= writes:
On Monday, 12 January 2015 at 20:07:17 UTC, deadalnix wrote:
 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.
It is difficult to introduce exceptions without causing problems for linear typing. http://www.serc.iisc.ernet.in/~govind/NHC-07/final/ExceptionsSlides.pdf
Jan 12 2015
prev sibling next sibling parent "Dicebot" <public dicebot.lv> writes:
On Monday, 12 January 2015 at 20:07:17 UTC, deadalnix wrote:
 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.
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.
Jan 12 2015
prev sibling parent "Marc =?UTF-8?B?U2Now7x0eiI=?= <schuetzm gmx.net> writes:
On Monday, 12 January 2015 at 20:07:17 UTC, deadalnix wrote:
 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.
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?
Jan 13 2015
prev sibling next sibling parent reply Iain Buclaw via Digitalmars-d <digitalmars-d puremagic.com> writes:
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
parent reply Walter Bright <newshound2 digitalmars.com> writes:
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=lKXe3HUG2l4
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. 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
parent reply Andrej Mitrovic via Digitalmars-d <digitalmars-d puremagic.com> writes:
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
parent Walter Bright <newshound2 digitalmars.com> writes:
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
prev sibling next sibling parent reply "H. S. Teoh via Digitalmars-d" <digitalmars-d puremagic.com> writes:
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
parent Jacob Carlborg <doob me.com> writes:
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
prev sibling parent reply Walter Bright <newshound2 digitalmars.com> writes:
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
parent reply "Meta" <jared771 gmail.com> writes:
On Wednesday, 14 January 2015 at 00:38:21 UTC, Walter Bright 
wrote:
 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!
But checked exceptions are definitely not.
Jan 13 2015
parent reply "deadalnix" <deadalnix gmail.com> writes:
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:
 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!
But checked exceptions are definitely not.
http://www.artima.com/intv/handcuffs.html The best sum up of checked exception you'll find.
Jan 13 2015
parent reply Jeremy Powers via Digitalmars-d <digitalmars-d puremagic.com> writes:
 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
parent reply "deadalnix" <deadalnix gmail.com> writes:
On Wednesday, 14 January 2015 at 02:40:20 UTC, Jeremy Powers via 
Digitalmars-d wrote:
 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.
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.
Jan 13 2015
parent Jeremy Powers via Digitalmars-d <digitalmars-d puremagic.com> writes:
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:

 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.
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.
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.
Jan 14 2015