January 12, 2015
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.
January 12, 2015
On Monday, 12 January 2015 at 17:57:10 UTC, H. S. Teoh via Digitalmars-d wrote:
> Yeah, exceptions are supposed to be ... well, *exceptions*, rather than
> the norm. :-) If you're using exceptions to do flow control, you're
> doing something wrong.

I'm sorry, but this is just a crappy excuse invented to defend C++ EH. It has no merits, except being an excuse for writing exceptionally convoluted code to save performance because real exceptions are slow.

(Everything that involves a state change is essentially flow control if we are going down to basics.)

There are plenty of situations where exceptions used for retries is the most sensible solution. Heck, that's even how x86 floating point exceptions work.

There are plenty of situations where returning state with exceptions makes the most sense, e.g. a web service request handler.
January 12, 2015
On Sun, Jan 11, 2015 at 02:48:29AM -0800, Walter Bright via Digitalmars-d wrote: [...]
> What I'm pretty sure is happening is those programs use error codes for error reporting, and then don't check the error codes. This is common practice for C code. I'm a little surprised that with Windows' long history, it still has problems detecting when it runs out of disk space.
> 
> However, if exceptions are thrown for errors instead, the programmer has to deliberately add code if he wishes to ignore the error.

While I agree with the general sentiment, I think the current convention of using a class hierarchy to implement exceptions is suboptimal.

The problem with using a class hierarchy is that, like anything put into a hierarchy, some things just don't fit very well in a single-rooted hierarchy. This is especially true in D because there is no multiple inheritance (and for good reason too; multiple inheritance brings with it a whole set of nasty problems).

A recent example was what to do with an exception that wraps around OS-level errors. From an implementor's POV, it makes sense to segregate exception types by implementation, that is, ErrnoException for Posix systems and SystemErrorCodeException (or some such) for Windows. However, this is totally useless to the end user: when you're traversing the filesystem, under this scheme you'd have to catch ErrnoException or catch SystemErrorCodeException (with a static-if on OS type, aka utter ugliness), and then ferret out the specific error code(s) you wish to handle, like what to do with an I/O error vs. a permission-denied error. Why should the *user* have to work with low-level implementation details like mapping Posix errno's and their corresponding Windows error codes to the semantic categories of real interest: i.e., access error / hardware failure / network error, etc.?

So from the user's POV, the exception hierarchy ought to be semantically driven, rather than implementationally driven. Instead of ErrnoException and SystemErrorCodeException, one ought to have semantic categories like FileNotFoundException, AccessDeniedException, NetworkException, etc.. However, this is burdensome on the implementor, because now a single underlying implementation like ErrnoException now has to be split across multiple unrelated semantic categories (FileNotFoundException must have an errno field on Posix and a systemErrorCode field on Windows, ditto for NetworkException, IOException, etc. -- and you can't factor it out into a base class because the class hierarchy is semantics driven, so ErrnoException doesn't fit anywhere in the hierarchy).

Recently Dmitry (IIRC) came up with the rather clever idea of using interfaces instead of a single-rooted hierarchy for marking up semantic categories instead. Instead of trying to decide on whether we should have implementationally-driven OSException with subclasses ErrnoException and SystemErrorCodeException, or semantically-driven FileSystemException with subclasses FileNotFoundException and AccessDeniedException, we can have both: the class hierarchy itself (i.e. without the interfaces) uses base classes for factoring out implementation details, but tag each exception type with semantic categories derived from a distinct interface hierarchy. So we could have something like this:

	// Implementational hierarchy
	class Throwable { ... }
	class Exception : Throwable { ... }
	class ErrnoException : Exception {
		int errno;
		...
	}
	class SystemErrorCodeException : Exception {
		int systemErrorCode;
		...
	}

	// Semantic tags
	interface FilesystemException;
	interface FileNotFoundException : FilesystemException;
	interface AccessDeniedException : FilesystemException;
	..

	// Actual implementations
	static if (Posix) {
		// Note: you can even have multiple exception types that
		// inherit from FileNotFoundException, e.g., one thrown
		// by a manual stat() check, and one from a common
		// routine that interprets OS error codes. The user
		// wouldn't need to know the difference.
		class FileNotFoundExceptionImpl :
			ErrnoException,
			FileNotFoundException { ... }
		... // other Posix-specific exceptions here
	} else static if (Windows) {
		class FileNotFoundExceptionImpl :
			SystemErrorCodeException,
			FileNotFoundException { ... }
		... // other Windows-specific exceptions here
	}

So now, user code doesn't have to worry about implementation details like whether the exception came from errno or a Windows error code, you can just catch semantic categories like FileNotFoundException instead.

Currently, however, this scheme doesn't quite work because the compiler only allows catching classes derived from Throwable, and interfaces can't derive from non-interface base classes. Besides, I'm not sure the druntime exception catching code can deal with interfaces correctly as opposed to just base classes. But this is definitely an area that D could improve on!


T

-- 
Why have vacation when you can work?? -- EC
January 12, 2015
On Mon, Jan 12, 2015 at 06:06:19PM +0000, Tobias Müller via Digitalmars-d wrote:
> "H. S. Teoh via Digitalmars-d" <digitalmars-d@puremagic.com> wrote:
> > 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

-- 
Маленькие детки - маленькие бедки.
January 12, 2015
On Monday, 12 January 2015 at 18:45:22 UTC, H. S. Teoh via Digitalmars-d wrote:
> The only sane way is to have the user specify a default value if the key
> wasn't found, since not all types have a null value (and besides, what
> if null is a valid value in the dictionary?). IOW something like
> TryGetValue that takes multiple arguments instead of operator[].

Yes, and then think about the original foundation for exceptions.

An exception is mean to resolve situations where you call from a higher level layer into a lower level layer (or framework). The basic idea is that the higher level layer has information that the lower layer does not.

So if you have exceptions with retry you get this:

1. High Level calls into low level

2. Low Level goes "Lord High Level, I am your humble servant, but cannot compute 3/0. What shall I do?"

3. High Level ponders for a while and says "replace it with infinity and continue"

4. Low Level comes back and say "Lord High Level, I cannot find 'flowers.png'".

5. High Level responds "Use 'default.png' instead".

6. Low Level comes back crying "I can't find that either".

7. High Level gives up and says "roll back, backtrack..."

Exceptions are basically about deferring decision making from an encapsulated context to the calling context.

Without retries, you just have a backtracking mechanism. Don't get hung upon the terminology. Use it for writing maintainable code!


> You really should be using operator[] only when the key is expected to
> exist. If during normal operations there's a 50% chance the key doesn't
> already exist, you shouldn't be using [].

This is getting very normative. Where did you get that 50% from?

If you had exceptions with retry you could just look it up in a default directory for instance, or even made a "call" to another process to get a substitute value.

Besides, if you are doing validation then it is desirable to get an exception for an illegal key.

Don't let a crappy implementation of exception handling define general semantics. Improve the implementation instead.
January 12, 2015
On Mon, 12 Jan 2015 18:11:01 +0000
via Digitalmars-d <digitalmars-d@puremagic.com> wrote:

> returning state with exceptions
(banging his head against a wall) NO. THIS NEVER HAS ANY SENSE.


January 12, 2015
On Mon, 2015-01-12 at 09:54 -0800, H. S. Teoh via Digitalmars-d wrote:
> 
[…]
> Yeah, exceptions are supposed to be ... well, *exceptions*, rather than the norm. :-) If you're using exceptions to do flow control, you're doing something wrong.
> 
[…]

Unless you are writing Python code.

-- 
Russel. ============================================================================= Dr Russel Winder      t: +44 20 7585 2200   voip: sip:russel.winder@ekiga.net 41 Buckmaster Road    m: +44 7770 465 077   xmpp: russel@winder.org.uk London SW11 1EN, UK   w: www.russel.org.uk  skype: russel_winder

January 12, 2015
On Mon, 2015-01-12 at 10:43 -0800, H. S. Teoh via Digitalmars-d wrote:
> 
[…]
> And what exactly should operator[] return if a key wasn't found?
> 
[…]

Go has an interesting solution, key lookup in a map return a pair (result, ok), if lookup succeeded then result is the associated value, if ok is false then result is undefined. I quite like this.

-- 
Russel. ============================================================================= Dr Russel Winder      t: +44 20 7585 2200   voip: sip:russel.winder@ekiga.net 41 Buckmaster Road    m: +44 7770 465 077   xmpp: russel@winder.org.uk London SW11 1EN, UK   w: www.russel.org.uk  skype: russel_winder

January 12, 2015
On 1/12/15 11:30 AM, Russel Winder via Digitalmars-d wrote:
>
> On Mon, 2015-01-12 at 10:43 -0800, H. S. Teoh via Digitalmars-d wrote:
>>
> […]
>> 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
January 12, 2015
On Monday, 12 January 2015 at 19:24:35 UTC, ketmar via Digitalmars-d wrote:
> On Mon, 12 Jan 2015 18:11:01 +0000
> via Digitalmars-d <digitalmars-d@puremagic.com> wrote:
>
>> 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