Re: The Right Approach to Exceptions

2012-03-05 Thread Don Clugston

On 24/02/12 13:47, Regan Heath wrote:

On Thu, 23 Feb 2012 15:13:17 -, James Miller  wrote:

On 23 February 2012 05:09, Regan Heath  wrote:

On Tue, 21 Feb 2012 14:19:17 -, Andrei Alexandrescu
 wrote:


On 2/21/12 5:55 AM, Regan Heath wrote:


On Sun, 19 Feb 2012 23:04:59 -, Andrei Alexandrescu
 wrote:


On 2/19/12 4:00 PM, Nick Sabalausky wrote:




Seriously, how is this not *already* crystal-clear? I feel as if
every few
weeks you're just coming up with deliberately random shit to
argue so
the
rest of us have to waste our time spelling out the obvious in
insanely
pedantic detail.



It sometimes happened to me to be reach the hypothesis that my
interlocutor must be some idiot. Most often I was missing something.



I get the impression that you find "Devil's advocate" a useful tool
for
generating debate and out of the box thinking.. there is something
to be
said for that, but it's probably less annoying to some if you're clear
about that from the beginning. :p



Where did it seem I was playing devil's advocate? Thanks.



"Devil's Advocate" is perhaps not the right term, as you don't seem
to ever
argue the opposite to what you believe. But, it occasionally seems to me
that you imply ignorance on your part, in order to draw more information
from other posters on exactly what they think or are proposing. So, some
get frustrated as they feel they have to explain "everything" to you
(and
not just you, there have been times where - for whatever reason - it
seems
that anything less than a description of every single minute detail
results
in a miss understanding - no doubt partly due to the medium in which
we are
communicating).


Regan

--
Using Opera's revolutionary email client: http://www.opera.com/mail/


I think that is technically called being facetious.


Doesn't seem quite right to me:
http://dictionary.reference.com/browse/facetious

R


Socratic irony?



Re: The Right Approach to Exceptions

2012-03-05 Thread Ben Hanson

On Saturday, 18 February 2012 at 22:35:58 UTC, Jonathan M Davis
wrote:
I'd hate to see us have a crippled exception hierarchy just 
because some
people mishandle exceptions and simply print out messages on 
failure in all
cases. Obviously sometimes that's what you have to do, but 
often you can
handle them much better, and that's part of the reason that we  
have
_Exception_ and not just Error. And a proper exception 
hierarchy can be very

benificial to programmers who actually use it correctly.


+1

Inheritance in general can (and is) misused too. Does that mean
that should also be banned?

Regards,

Ben


Re: The Right Approach to Exceptions

2012-02-26 Thread Timon Gehr

On 02/26/2012 01:12 PM, deadalnix wrote:

Le 25/02/2012 14:10, Timon Gehr a écrit :

On 02/21/2012 07:57 PM, deadalnix wrote:

opDispatch is nice, but rather incomplete. It doesn't handle template
methods for example.


It surely does.

struct S{
template opDispatch(string op){
auto opDispatch(T...)(T args){
writeln(op, args);
}
}
}
void main() {
S s;
s.foo(1,2.0,"bar");
}


Explicit template argument, I should have precised.


Please validate your non-trivial claims before you make them. Otherwise, 
time gets wasted discussing non-existent problems. A good way to ensure 
what you claim is true is to always provide a minimal code example that 
demonstrates the claim, after having verified that the code indeed 
exposes the problematic behavior when compiled with the latest version 
of the reference compiler.


Re: The Right Approach to Exceptions

2012-02-26 Thread deadalnix

Le 25/02/2012 14:10, Timon Gehr a écrit :

On 02/21/2012 07:57 PM, deadalnix wrote:

opDispatch is nice, but rather incomplete. It doesn't handle template
methods for example.


It surely does.

struct S{
template opDispatch(string op){
auto opDispatch(T...)(T args){
writeln(op, args);
}
}
}
void main() {
S s;
s.foo(1,2.0,"bar");
}


Explicit template argument, I should have precised.


Re: The Right Approach to Exceptions

2012-02-25 Thread Timon Gehr

On 02/25/2012 09:18 PM, Andrei Alexandrescu wrote:

On 2/25/12 7:19 AM, Timon Gehr wrote:

On 02/24/2012 08:14 PM, Andrei Alexandrescu wrote:

On 2/24/12 1:13 PM, H. S. Teoh wrote:

In my mind, contract code belongs in the function signature, because
they document how the function expects to be called, and what it
guarantees in return. It doesn't seem to make sense to me that
contracts
would be hidden from the user of the library. Sorta defeats the
purpose,
since how is the user supposed to know what the function expects? Rely
on documentation, perhaps, but docs aren't as reliable as actual
contract code.


Yah, and that's why we managed, with great implementation effort, to
allow contract checks in interfaces. The concept has still to take off
though.

Andrei


'In' contracts are hardly usable at all at the moment, because they are
not inherited by default.


I thought that was fixed. Is there a bug report on it? Thanks!

Andrei


This is the bug report:
http://d.puremagic.com/issues/show_bug.cgi?id=6856

( Furthermore, there is an issue with the design:
http://d.puremagic.com/issues/show_bug.cgi?id=7584 )



Re: The Right Approach to Exceptions

2012-02-25 Thread Andrei Alexandrescu

On 2/25/12 7:19 AM, Timon Gehr wrote:

On 02/24/2012 08:14 PM, Andrei Alexandrescu wrote:

On 2/24/12 1:13 PM, H. S. Teoh wrote:

In my mind, contract code belongs in the function signature, because
they document how the function expects to be called, and what it
guarantees in return. It doesn't seem to make sense to me that contracts
would be hidden from the user of the library. Sorta defeats the purpose,
since how is the user supposed to know what the function expects? Rely
on documentation, perhaps, but docs aren't as reliable as actual
contract code.


Yah, and that's why we managed, with great implementation effort, to
allow contract checks in interfaces. The concept has still to take off
though.

Andrei


'In' contracts are hardly usable at all at the moment, because they are
not inherited by default.


I thought that was fixed. Is there a bug report on it? Thanks!

Andrei


Re: The Right Approach to Exceptions

2012-02-25 Thread Timon Gehr

On 02/24/2012 08:14 PM, Andrei Alexandrescu wrote:

On 2/24/12 1:13 PM, H. S. Teoh wrote:

In my mind, contract code belongs in the function signature, because
they document how the function expects to be called, and what it
guarantees in return. It doesn't seem to make sense to me that contracts
would be hidden from the user of the library. Sorta defeats the purpose,
since how is the user supposed to know what the function expects? Rely
on documentation, perhaps, but docs aren't as reliable as actual
contract code.


Yah, and that's why we managed, with great implementation effort, to
allow contract checks in interfaces. The concept has still to take off
though.

Andrei


'In' contracts are hardly usable at all at the moment, because they are 
not inherited by default.


Re: The Right Approach to Exceptions

2012-02-25 Thread Timon Gehr

On 02/21/2012 07:57 PM, deadalnix wrote:

opDispatch is nice, but rather incomplete. It doesn't handle template
methods for example.


It surely does.

struct S{
template opDispatch(string op){
auto opDispatch(T...)(T args){
writeln(op, args);
}
}
}
void main() {
S s;
s.foo(1,2.0,"bar");
}


Re: The Right Approach to Exceptions

2012-02-24 Thread H. S. Teoh
On Fri, Feb 24, 2012 at 07:37:03PM -0500, Jonathan M Davis wrote:
> On Friday, February 24, 2012 16:18:59 H. S. Teoh wrote:
> > On Fri, Feb 24, 2012 at 06:48:47PM -0500, Jonathan M Davis wrote:
[...]
> > > which, as you point out, would have to be done at runtime, or by
> > > doing something similar to what Java 7 is doing and make it so
> > > that multiple exceptions can be caught with the same block
> > > 
> > > catch(Ex1, Ex2, Ex3 e)
> > > 
> > > and e ends up being the most derived type that is common to them.
> > 
> > This can also be done by catch conditions by using is-expressions or
> > derived class casts, though it would be more verbose. We may not
> > need to implement both.
> 
> You do run into the who rethrowing issue with that though, if you
> catch the wrong type. It also can force you to combine disparate catch
> blocks, since you caught the common type, and you're forced to use
> if-else statements to separate out the various cases instead of
> letting catch take care of it. All in all, it would just be _way_
> cleaner to have a way to give a list of exception types that you want
> that catch to catch.

I think you misunderstood me, what I'm referring to is this:

// MyException is common base of DerivedEx1 and DerivedEx2
catch(MyException e : cast(DerivedEx1)e !is null ||
cast(DerivedEx2)e !is null)
{
// catch code
}

There's no rethrowing necessary, the catch block doesn't execute unless
the exception is either DerivedEx1 or DerivedEx2. You also get to
specify the common base type to assign to e.

But yes, this is more verbose. I was just pointing out that it may not
be necessary to extend the language to catch multiple exception types if
we're already going to implement catch conditions.


> Also, I think that having a syntax for simply giving a list of
> exceptions that an exception block catches is plenty without needing
> the whole condition thing. The condition thing just seems like
> overkill. But maybe someone has a valid use case where giving a
> specific list of exceptions doesn't cut it.

The condition thing may be a another justification for the whole
Variant[string] thing, though, if you could do something like this:

catch(MyException e : ("myattr" in e.info) !is null)
{
// catch code
}

This then lets intermediate code on the call stack add annotations to a
passing exception, and a catch block further up the call stack to only
catch annotated exceptions.

I can't think of any actual real-world use case for this, but maybe
Jose(?), or whoever it was that wanted to add extra info to exceptions
in transit, can.


> > I don't think D's job is to stop people from doing stupid things.
> > Otherwise we might as well go back to Pascal or something. :)
> 
> Indeed. What we need is a setup which enables (and hopefully encourages) 
> people to do the right thing. We can't stop people from being stupid.

Having a proper exception hierarchy in Phobos will, in my mind, set a
good precedent to proper use of exceptions. To be quite honest, I was
shocked when I discovered the current exception-by-module situation in
Phobos---after reading TDPL I was under the impression that we had a
properly-designed exception hierarchy. :)


> For instance, much as Java generally tries to protect you from your
> own stupidity, the fact that it has very good exception hierarchy
> doesn't stop people (and in some cases the developers of its standard
> library) from doing stupid things with exceptions.
[...]

Trying to prevent stupidity often cripples the language/system. As the
old Unix adage goes: 

Unix was not designed to stop people from doing stupid things,
because that would also stop them from doing clever things. --
Doug Gwyn

This is one area where D trumps Java by a long shot IMO. I find Java
really straitjacketed sometimes in the way it tries to "protect" me from
myself. I have to jump through hoops to convince it that, yes, I really
*really* want to do this.


T

-- 
Always remember that you are unique. Just like everybody else. -- despair.com


Re: The Right Approach to Exceptions

2012-02-24 Thread Jonathan M Davis
On Friday, February 24, 2012 16:18:59 H. S. Teoh wrote:
> On Fri, Feb 24, 2012 at 06:48:47PM -0500, Jonathan M Davis wrote:

> > 1. Being able to annotate catch blocks in some manner to enable them
> > to catch multiple, specific exceptions - either by using some kind of
> > condition
> > 
> > catch(Ex1 e) if(condition)
> 
> Even though I was (one of) the one(s) who proposed this syntax, I think
> a better syntax would be something like:
> 
> catch(Ex1 e: condition)
> 
> but that's just bikeshedding.
> 
> > which, as you point out, would have to be done at runtime, or by doing
> > something similar to what Java 7 is doing and make it so that multiple
> > exceptions can be caught with the same block
> > 
> > catch(Ex1, Ex2, Ex3 e)
> > 
> > and e ends up being the most derived type that is common to them.
> 
> This can also be done by catch conditions by using is-expressions or
> derived class casts, though it would be more verbose. We may not need to
> implement both.

You do run into the who rethrowing issue with that though, if you catch the 
wrong type. It also can force you to combine disparate catch blocks, since you 
caught the common type, and you're forced to use if-else statements to 
separate out the various cases instead of letting catch take care of it. All 
in all, it would just be _way_ cleaner to have a way to give a list of 
exception types that you want that catch to catch.

Also, I think that having a syntax for simply giving a list of exceptions that 
an exception block catches is plenty without needing the whole condition 
thing. The condition thing just seems like overkill. But maybe someone has a 
valid use case where giving a specific list of exceptions doesn't cut it.

> I don't think D's job is to stop people from doing stupid things.
> Otherwise we might as well go back to Pascal or something. :)

Indeed. What we need is a setup which enables (and hopefully encourages) 
people to do the right thing. We can't stop people from being stupid.

For instance, much as Java generally tries to protect you from your own 
stupidity, the fact that it has very good exception hierarchy doesn't stop 
people (and in some cases the developers of its standard library) from doing 
stupid things with exceptions. And D, which tries to protect you without 
preventing you from being stupid (unlike Java, which generally tries to 
prevent you from doing something stupid), certainly isn't going to stop you 
from being stupid.

- Jonathan M Davis


Re: The Right Approach to Exceptions

2012-02-24 Thread H. S. Teoh
On Fri, Feb 24, 2012 at 06:48:47PM -0500, Jonathan M Davis wrote:
[...]
> However, Java does have a great exception hierarchy, much of which is
> standard. And that's a lot better than having only a handful of
> unrelated exceptions, let alone having all exceptions be specific to a
> module like we do now.
> 
> So, we really have 2 problems here:
> 
> 1. Phobos doesn't have a proper exception hierarchy. Its exceptions
> need to be better organized according to the problem that they
> indicate rather than by module.

+1.


> 2. There are something things that can be done to Exception and how
> exceptions work in D to help resolve issues related to wanting to
> catch and handle exceptions without having to catch all exceptions
> that match their common base type or otherwise have to duplicate code
> among catch blocks.
> 
> The two proposals that seem best for this are
> 
> 1. Being able to annotate catch blocks in some manner to enable them
> to catch multiple, specific exceptions - either by using some kind of
> condition
> 
> catch(Ex1 e) if(condition)

Even though I was (one of) the one(s) who proposed this syntax, I think
a better syntax would be something like:

catch(Ex1 e: condition)

but that's just bikeshedding.


> which, as you point out, would have to be done at runtime, or by doing
> something similar to what Java 7 is doing and make it so that multiple
> exceptions can be caught with the same block
> 
> catch(Ex1, Ex2, Ex3 e)
> 
> and e ends up being the most derived type that is common to them.

This can also be done by catch conditions by using is-expressions or
derived class casts, though it would be more verbose. We may not need to
implement both.


[...]
> So, if we better organize Phobos' exceptions, implement a multi-catch
> feature, and implement Andrei's Variant[string], then Phobos'
> exceptions will work far better (and by far more reusable), and some
> of the greater shortcomings of D's exceptions themselves should be
> mitigated, if not eliminated.

+1.


> Naturally, that still won't stop people from doing stupid things with
> exceptions in their own code (like throwing on EOF), but we'll have a
> good setup for those who use exceptions properly, and we should be
> able to avoid putting stupid uses of exceptions in Phobos, simply
> because the code is open and peer-reviewed, and I think that most of
> the people around here agree that using exceptions for stuff like EOF
> is stupid.
[...]

I don't think D's job is to stop people from doing stupid things.
Otherwise we might as well go back to Pascal or something. :)


T

-- 
For every argument for something, there is always an equal and opposite
argument against it. Debates don't give answers, only wounded or
inflated egos.


Re: The Right Approach to Exceptions

2012-02-24 Thread Jonathan M Davis
On Friday, February 24, 2012 16:38:31 Steven Schveighoffer wrote: 
> On to my second point. One of the issues I have with Java is that
> exceptions are *overused*. For example, EOF should not be an exception,
> most files have ends, it's not a very exceptional situation. If there is
> an intuitive way to use an existing return value to convey an error rather
> than an exception, I'd prefer the return value. There is a runtime cost
> just for setting up a try/catch block, even if no exceptions are thrown.

It's definitely true that Java uses exceptions when they shouldn't - EOF being 
a good example of that. Exceptions should almost always only be used when 
something actually goes wrong. EOF is not something going wrong.

However, Java does have a great exception hierarchy, much of which is 
standard. And that's a lot better than having only a handful of unrelated 
exceptions, let alone having all exceptions be specific to a module like we do 
now.

So, we really have 2 problems here:

1. Phobos doesn't have a proper exception hierarchy. Its exceptions need to be 
better organized according to the problem that they indicate rather than by 
module.

2. There are something things that can be done to Exception and how exceptions 
work in D to help resolve issues related to wanting to catch and handle 
exceptions without having to catch all exceptions that match their common base 
type or otherwise have to duplicate code among catch blocks.

The two proposals that seem best for this are

1. Being able to annotate catch blocks in some manner to enable them to catch 
multiple, specific exceptions - either by using some kind of condition

catch(Ex1 e) if(condition)

which, as you point out, would have to be done at runtime, or by doing 
something similar to what Java 7 is doing and make it so that multiple 
exceptions can be caught with the same block

catch(Ex1, Ex2, Ex3 e)

and e ends up being the most derived type that is common to them.

2. Andrei's Variant[string] proposal, which would enable a generic formatting 
scheme with string templates which a free function could use to generate 
messages from exceptions without caring about the actual exception type. It 
would also allow you to tack on extra information to the exception which 
wouldn't normally be part of that type of exception but your particular 
program needs for some reason.

So, if we better organize Phobos' exceptions, implement a multi-catch feature, 
and implement Andrei's Variant[string], then Phobos' exceptions will work far 
better (and by far more reusable), and some of the greater shortcomings of D's 
exceptions themselves should be mitigated, if not eliminated.

Naturally, that still won't stop people from doing stupid things with 
exceptions in their own code (like throwing on EOF), but we'll have a good 
setup for those who use exceptions properly, and we should be able to avoid 
putting stupid uses of exceptions in Phobos, simply because the code is open 
and peer-reviewed, and I think that most of the people around here agree that 
using exceptions for stuff like EOF is stupid.

- Jonathan M Davis


Re: The Right Approach to Exceptions

2012-02-24 Thread H. S. Teoh
On Fri, Feb 24, 2012 at 04:38:31PM -0500, Steven Schveighoffer wrote:
[...]
> On to my second point.  One of the issues I have with Java is that
> exceptions are *overused*.  For example, EOF should not be an
> exception, most files have ends, it's not a very exceptional
> situation.  If there is an intuitive way to use an existing return
> value to convey an error rather than an exception, I'd prefer the
> return value.  There is a runtime cost just for setting up a try/catch
> block, even if no exceptions are thrown.
[...]

Yeah, EOF shouldn't be an exception. It should simply be a terminal
state that file-reading code gets into, whereupon it will simply return
some special value (e.g., dchar.init, or empty array) upon further
reading. Preferably also have a .eof or .empty method or something
equivalent that indicates EOF, so you can write "while (!obj.eof) { ...
}". Using an exception for EOF is abuse, IMO.

Another beef I have about EOFs is that I've seen code where EOF is
returned only once, and then you're not supposed to call that code ever
again after that because it will crash. I think that's really stupid. If
you hit EOF, attempting to read further should simply return EOF again
without changing the state of the system any further. Multiple attempts
to read past EOF should still return EOF. (One would think this was
obvious, but obviously it's not very obvious...)


T

-- 
To err is human; to forgive is not our policy. -- Samuel Adler


Re: The Right Approach to Exceptions

2012-02-24 Thread Steven Schveighoffer
On Sat, 18 Feb 2012 13:52:05 -0500, Andrei Alexandrescu  
 wrote:



There's a discussion that started in a pull request:

https://github.com/alexrp/phobos/commit/4b87dcf39efeb4ddafe8fe99a0ef9a529c0dcaca

Let's come up with a good doctrine for exception defining and handling  
in Phobos. From experience I humbly submit that catching by type is most  
of the time useless.


OK, so after reading about 100 or so of these messages, I stopped.  Sorry  
if this has been said before, but here is my take:


many many times when dealing with exceptions I hate to do this:

class ExceptionTypeA : Exception {...}
class ExceptionTypeB : Exception {...}


try
{
}
catch(ExceptionTypeA ex)
{
   // code
}
catch(ExceptionTypeB ex)
{
   // same f'ing code
}

I know, I could do this:

catch(Exception e)
{
   if(cast(ExceptionTypeA)e || cast(ExceptionTypeB)e)
   {
  // code
   }
   else
 throw e;
}

But that sucks, and as others have pointed out, it kills the stack trace.

So I love the idea that others have specified to have a 'template  
constraint' type piece for catch.  IMO, it should not be a template, but a  
runtime check.  i.e., I don't think we need to templatize the catch, we  
just need to add extra code to the 'should we catch' check that currently  
consists of 'does the type match'.  This would be a huge improvement over  
existing exception catching techniques.


So the above would become:

catch(Exception e) if(e is ExceptionTypeA or ExceptionTypeB) // not sure  
of the exact syntax, maybe the if statement I used above?


I agree the constraints should be pure and nothrow.


On to my second point.  One of the issues I have with Java is that  
exceptions are *overused*.  For example, EOF should not be an exception,  
most files have ends, it's not a very exceptional situation.  If there is  
an intuitive way to use an existing return value to convey an error rather  
than an exception, I'd prefer the return value.  There is a runtime cost  
just for setting up a try/catch block, even if no exceptions are thrown.


-Steve


Re: The Right Approach to Exceptions

2012-02-24 Thread Andrei Alexandrescu

On 2/24/12 1:13 PM, H. S. Teoh wrote:

In my mind, contract code belongs in the function signature, because
they document how the function expects to be called, and what it
guarantees in return. It doesn't seem to make sense to me that contracts
would be hidden from the user of the library. Sorta defeats the purpose,
since how is the user supposed to know what the function expects? Rely
on documentation, perhaps, but docs aren't as reliable as actual
contract code.


Yah, and that's why we managed, with great implementation effort, to 
allow contract checks in interfaces. The concept has still to take off 
though.


Andrei


Re: The Right Approach to Exceptions

2012-02-24 Thread H. S. Teoh
On Fri, Feb 24, 2012 at 01:46:56PM -0500, Jonathan M Davis wrote:
> On Friday, February 24, 2012 07:57:13 H. S. Teoh wrote:
> > Actually, I wonder if it makes sense for the compiler to insert
> > in-contract code in the *caller* instead of the callee. Conceptually
> > speaking, an in-contract means "you have to fulfill these conditions
> > before calling this function". So why not put the check in the
> > caller?
> > 
> > Similarly, an out-contract means "this function's return value will
> > satisfy these conditions" - so let the caller verify that this is
> > true.
> > 
> > Semantically it amounts to the same thing, but this gives us more
> > flexibility: the library doesn't have to be compiled with contracts
> > on/off, the contracts are in the library API, and the user tells the
> > compiler whether or not to wrap the contract code around each call
> > to the library.
> > 
> > (Yes this bloats the code everywhere a DbC function is called, but
> > this is supposed to be done in non-release builds only anyway, so I
> > don't think that matters so much.)
> 
> It wouldn't work unless the source were available, because otherwise
> you just have the function signature.
[...]

In my mind, contract code belongs in the function signature, because
they document how the function expects to be called, and what it
guarantees in return. It doesn't seem to make sense to me that contracts
would be hidden from the user of the library. Sorta defeats the purpose,
since how is the user supposed to know what the function expects? Rely
on documentation, perhaps, but docs aren't as reliable as actual
contract code.


T

-- 
Без труда не выловишь и рыбку из пруда. 


Re: The Right Approach to Exceptions

2012-02-24 Thread Jonathan M Davis
On Friday, February 24, 2012 08:27:44 H. S. Teoh wrote:
> On Fri, Feb 24, 2012 at 07:57:13AM -0800, H. S. Teoh wrote:
> > > On Thursday, February 23, 2012 15:18:27 H. S. Teoh wrote:
> > > > In my book, a linked library shares equal status with the "main
> > > > program", therefore the definition of "user input" still sits at
> > > > the internal-to-program and external boundary.
> 
> [...]
> 
> > I wasn't trying to say that library code should always use DbC and
> > application code should always use defensive programming. I'm saying
> > that if it makes sense for a function to use DbC (or vice versa) then
> > it should use DbC regardless of whether it's in a library or not.
> 
> [...]
> 
> Argh, I just realized that my first post was so poorly worded it made no
> sense at all. My second post was what I meant to say. :)
> 
> What I was trying to express in the first post was that "user input"
> comes from a source external to the program, whether from a user typing
> at the keyboard, or from a file or network resource, and this data
> traverses program code paths until eventually they are converted into
> the internal form the program uses for further processing. Input
> sanitization should be done along this code path until the input is
> processed into program-internal form, at which point, DbC begins to take
> effect, the assumption being that after preprocessing by the input
> sanitization code, all data should be valid, and if not, it's a failure
> of the input processing code and represents a logic flaw in the program,
> therefore an assertion should be thrown.

Yes. In general, that's the core difference between assertions and exceptions. 
If an assertion fails, it's a bug in the code, whereas if an exception is 
thrown, then it may or may not be caused by a program bug (and is frequently 
caused by interacting with I/O - be it directly or indirectly).

But that does require a judgement call sometimes as to which approach is 
better in a particular situation, and if you're being utterly paranoid (which 
some programs probably need to be but most don't), then you could end up using 
exceptions where you'd normally use assertions simply because you want to 
_guarantee_ that the check is always done. But hopefully, that sort of thing 
would be kept to a minimum.

Regarldess, at the core, assertions are for verifying program correctness, and 
exceptions are for reporting error conditions caused by bad stuff happening 
during the normal operation of the program.

- Jonathan M Davis


Re: The Right Approach to Exceptions

2012-02-24 Thread Jonathan M Davis
On Friday, February 24, 2012 07:57:13 H. S. Teoh wrote:
> Actually, I wonder if it makes sense for the compiler to insert
> in-contract code in the *caller* instead of the callee. Conceptually
> speaking, an in-contract means "you have to fulfill these conditions
> before calling this function". So why not put the check in the caller?
> 
> Similarly, an out-contract means "this function's return value will
> satisfy these conditions" - so let the caller verify that this is true.
> 
> Semantically it amounts to the same thing, but this gives us more
> flexibility: the library doesn't have to be compiled with contracts
> on/off, the contracts are in the library API, and the user tells the
> compiler whether or not to wrap the contract code around each call to
> the library.
> 
> (Yes this bloats the code everywhere a DbC function is called, but this
> is supposed to be done in non-release builds only anyway, so I don't
> think that matters so much.)

It wouldn't work unless the source were available, because otherwise you just 
have the function signature. So, the result would be inconsistent, and in the 
case of non-templated functions built against a non-release version of the 
callee, you'd end up having the checks twice, because the callee would have to 
have them regardless (since it's compiled separately, and not every function 
calling it would necessarily have its source). For templated functions, it 
already depends on the caller whether the assertions are enabled, because it's 
instantiated when the module with the caller in it is built. It's an 
interesting idea though.

- Jonathan M Davis


Re: The Right Approach to Exceptions

2012-02-24 Thread H. S. Teoh
On Fri, Feb 24, 2012 at 07:57:13AM -0800, H. S. Teoh wrote:
> > On Thursday, February 23, 2012 15:18:27 H. S. Teoh wrote:
> > > In my book, a linked library shares equal status with the "main
> > > program", therefore the definition of "user input" still sits at
> > > the internal-to-program and external boundary.
[...]
> I wasn't trying to say that library code should always use DbC and
> application code should always use defensive programming. I'm saying
> that if it makes sense for a function to use DbC (or vice versa) then
> it should use DbC regardless of whether it's in a library or not.
[...]

Argh, I just realized that my first post was so poorly worded it made no
sense at all. My second post was what I meant to say. :)

What I was trying to express in the first post was that "user input"
comes from a source external to the program, whether from a user typing
at the keyboard, or from a file or network resource, and this data
traverses program code paths until eventually they are converted into
the internal form the program uses for further processing.  Input
sanitization should be done along this code path until the input is
processed into program-internal form, at which point, DbC begins to take
effect, the assumption being that after preprocessing by the input
sanitization code, all data should be valid, and if not, it's a failure
of the input processing code and represents a logic flaw in the program,
therefore an assertion should be thrown.


T

-- 
Those who don't understand Unix are condemned to reinvent it, poorly.


Re: The Right Approach to Exceptions

2012-02-24 Thread H. S. Teoh
On Thu, Feb 23, 2012 at 07:06:02PM -0500, Jonathan M Davis wrote:
> On Thursday, February 23, 2012 15:18:27 H. S. Teoh wrote:
> > On Thu, Feb 23, 2012 at 12:07:40PM -0800, Jonathan M Davis wrote:
> > In my book, a linked library shares equal status with the "main
> > program", therefore the definition of "user input" still sits at the
> > internal-to-program and external boundary.
> 
> Yes, "in your book." Some people will agree with you and some won't.
> It really depends on what the code is doing though IMHO. In some
> cases, one is better and in some cases, the other is better. But it
> _is_ important to remember that there's a big difference between
> linking against a library over which you have control and a 3rd party
> library.
> 
> And there are times when it just plain makes more sense to have a
> function which throws an exception on bad input regardless of whether
> it's an "internal" function or not. For instance, if you want to
> convert a string to something else (e.g. with SysTime's
> fromISOExtString or even just with std.conv.to), you need to actually
> verify that the string has a value which can be correctly converted.
> It's actually cheaper to have the function doing the conversion do the
> checking rather than have another function do a check first, and then
> have the converting function not check (save perhaps for an assertion
> outside of release mode), because then you'll be processing the string
> _twice_.

I guess this is a judgment call. Personally, I would consider arguments
to string conversion functions to be "user input" even though,
technically speaking, you could just be passing a byte array literal to
it, in which case it's just a case of bad input parameters.


> This is _not_ a cut-and-dried issue. Sometimes DbC makes more sense,
> and sometimes defensive programming does. You pick the one that works
> best for a given situation.
> 
> The whole thing is a gray area, and you're not going to get a
> consensus that a library should always use DbC on its functions or
> that it should always use defensive programming.

I wasn't trying to say that library code should always use DbC and
application code should always use defensive programming. I'm saying
that if it makes sense for a function to use DbC (or vice versa) then it
should use DbC regardless of whether it's in a library or not.  If I
were to write a string conversion function, for example, I wouldn't use
contracts to enforce the right encoding, regardless of whether it's in a
library or in application code. I would use exceptions, simply because
that's what makes sense in this case. Just because something is in the
library shouldn't change whether DbC or defensive programming is used.
It's the semantics that matter, not whether it's in a library.


[...]
> > No need to templatize anything, just ship two versions of the
> > library, one with DbC compiled in, one without. Let the user decide
> > which one to link in.
> 
> There _is_ a need to do that if the caller wants to control whether an
> assertion or an exception is used. There's also a need if you want to
> enable it in some places and not in others. However, the reality of
> the matter is that using a debug version of a library is as close as
> you're likely to get.  And I'm certainly not arguing that templatizing
> functions in this manner would be a good idea. I'm just pointing aut
> that there are issues with how DbC is currently implemented.
[...]

Actually, I wonder if it makes sense for the compiler to insert
in-contract code in the *caller* instead of the callee. Conceptually
speaking, an in-contract means "you have to fulfill these conditions
before calling this function". So why not put the check in the caller?

Similarly, an out-contract means "this function's return value will
satisfy these conditions" - so let the caller verify that this is true.

Semantically it amounts to the same thing, but this gives us more
flexibility: the library doesn't have to be compiled with contracts
on/off, the contracts are in the library API, and the user tells the
compiler whether or not to wrap the contract code around each call to
the library.

(Yes this bloats the code everywhere a DbC function is called, but this
is supposed to be done in non-release builds only anyway, so I don't
think that matters so much.)


T

-- 
Тише едешь, дальше будешь.


Re: The Right Approach to Exceptions

2012-02-24 Thread Regan Heath

On Thu, 23 Feb 2012 15:13:17 -, James Miller  wrote:

On 23 February 2012 05:09, Regan Heath  wrote:

On Tue, 21 Feb 2012 14:19:17 -, Andrei Alexandrescu
 wrote:


On 2/21/12 5:55 AM, Regan Heath wrote:


On Sun, 19 Feb 2012 23:04:59 -, Andrei Alexandrescu
 wrote:


On 2/19/12 4:00 PM, Nick Sabalausky wrote:




Seriously, how is this not *already* crystal-clear? I feel as if
every few
weeks you're just coming up with deliberately random shit to argue  
so

the
rest of us have to waste our time spelling out the obvious in  
insanely

pedantic detail.



It sometimes happened to me to be reach the hypothesis that my
interlocutor must be some idiot. Most often I was missing something.



I get the impression that you find "Devil's advocate" a useful tool  
for
generating debate and out of the box thinking.. there is something to  
be

said for that, but it's probably less annoying to some if you're clear
about that from the beginning. :p



Where did it seem I was playing devil's advocate? Thanks.



"Devil's Advocate" is perhaps not the right term, as you don't seem to  
ever
argue the opposite to what you believe.  But, it occasionally seems to  
me

that you imply ignorance on your part, in order to draw more information
from other posters on exactly what they think or are proposing.  So,  
some
get frustrated as they feel they have to explain "everything" to you  
(and
not just you, there have been times where - for whatever reason - it  
seems
that anything less than a description of every single minute detail  
results
in a miss understanding - no doubt partly due to the medium in which we  
are

communicating).


Regan

--
Using Opera's revolutionary email client: http://www.opera.com/mail/


I think that is technically called being facetious.


Doesn't seem quite right to me:
http://dictionary.reference.com/browse/facetious

R

--
Using Opera's revolutionary email client: http://www.opera.com/mail/


Re: The Right Approach to Exceptions

2012-02-23 Thread Jonathan M Davis
On Thursday, February 23, 2012 15:18:27 H. S. Teoh wrote:
> On Thu, Feb 23, 2012 at 12:07:40PM -0800, Jonathan M Davis wrote:
> In my book, a linked library shares equal status with the "main
> program", therefore the definition of "user input" still sits at the
> internal-to-program and external boundary.

Yes, "in your book." Some people will agree with you and some won't. It really 
depends on what the code is doing though IMHO. In some cases, one is better 
and in some cases, the other is better. But it _is_ important to remember that 
there's a big difference between linking against a library over which you have 
control and a 3rd party library.

And there are times when it just plain makes more sense to have a function 
which throws an exception on bad input regardless of whether it's an 
"internal" function or not. For instance, if you want to convert a string to 
something else (e.g. with SysTime's fromISOExtString or even just with 
std.conv.to), you need to actually verify that the string has a value which 
can be correctly converted. It's actually cheaper to have the function doing 
the conversion do the checking rather than have another function do a check 
first, and then have the converting function not check (save perhaps for an 
assertion outside of release mode), because then you'll be processing the 
string _twice_.

This is _not_ a cut-and-dried issue. Sometimes DbC makes more sense, and 
sometimes defensive programming does. You pick the one that works best for a 
given situation.

The whole thing is a gray area, and you're not going to get a consensus that a 
library should always use DbC on its functions or that it should always use 
defensive programming.

> > Arguably, the best thing would be if there was a way for the caller to
> > indicate whether it wanted the callee to have DbC enable and possibly
> > even indicate whether it wanted the callee to use DbC or defensive
> > programming. But there's no way to do that in D, and I'm not sure that
> > it could even be done with the C linking model - at least, there's no
> > way to it without templatizing everything and giving an argument to
> > the template indicating what you want, which obviously isn't a good
> > solution (and won't work at all in the case of virtual functions,
> > since they can't be templatized).

> No need to templatize anything, just ship two versions of the library,
> one with DbC compiled in, one without. Let the user decide which one to
> link in.

There _is_ a need to do that if the caller wants to control whether an 
assertion or an exception is used. There's also a need if you want to enable 
it in some places and not in others. However, the reality of the matter is 
that using a debug version of a library is as close as you're likely to get. 
And I'm certainly not arguing that templatizing functions in this manner would 
be a good idea. I'm just pointing aut that there are issues with how DbC is 
currently implemented.

And the primary problem with how DbC is implemented is the fact that its 
assertions test the caller, not the callee, but the assertions end up in the 
callee. So, the assertions are separated from the code that they're actually 
testing. It's the best that we can do at this point, but it does result in a 
weird situation where you end up using assertions to test _other_ people's 
code rather than your own.

- Jonathan M Davis


Re: The Right Approach to Exceptions

2012-02-23 Thread H. S. Teoh
On Thu, Feb 23, 2012 at 12:07:40PM -0800, Jonathan M Davis wrote:
> On Thursday, February 23, 2012 07:47:55 H. S. Teoh wrote:
[...]
> > The way I understand it, DbC is used for ensuring *program*
> > correctness (ensure that program logic does not get itself into a
> > bad state); defensive programming is for sanitizing *user input*
> > (ensure that no matter what the user does, the program doesn't get
> > into a bad state).
> > 
> > That's why DbC is compiled out in release mode -- the assumption is
> > that you have thoroughly tested your program logic and verified
> > there are no logic problems. Input sanitizing is never compiled out,
> > because you never know what users will do, so you always have to
> > check.
> > 
> > The two do somewhat overlap, of course. For example, failing to
> > sanitize user input may eventually lead to passing invalid arguments
> > to an internal function.
> 
> Exactly. But where things tend to blur is the concept of "user input."
> For instance, if you're using a 3rd party library, should it be
> asserting on the arguments that you pass it?

In my book, a linked library shares equal status with the "main
program", therefore the definition of "user input" still sits at the
internal-to-program and external boundary.


> Unless you compile it in non-release mode, it obviously won't, which
> could be an argument for using exceptions, but regardless of that,
> from the library's perspective, you're a user.

I believe the traditional way is to ship a debug or devel version of the
library which is linked when you compile in non-release mode (e.g.,
libc-dbg), and then in release mode the release mode library is linked
(libc proper). That way DbC will be enforced in non-release mode by the
library, and suppressed in the release mode binary.

If libraries only ship in release mode, then that sorta defeats the
point of DbC, which is to ensure program correctness before release and
not get in the way after. Now the library has to be paranoid and always
sanitize all inputs.


> If it used DbC, it would be putting assertions it in its own code to
> test _your_ code.  And since you're a user, it arguably should use
> exceptions to make sure that the arguments that it gets are correct.

No, the library should ship a development version with all contracts
compiled-in, so that contract violations will be enforced during
development & testing.

Sadly, this isn't often done in practice, which leads to the sad
situation where the program/library boundary has a lot of overhead,
because the library must be paranoid and always sanitize all inputs no
matter what.


[...]
> Arguably, the best thing would be if there was a way for the caller to
> indicate whether it wanted the callee to have DbC enable and possibly
> even indicate whether it wanted the callee to use DbC or defensive
> programming. But there's no way to do that in D, and I'm not sure that
> it could even be done with the C linking model - at least, there's no
> way to it without templatizing everything and giving an argument to
> the template indicating what you want, which obviously isn't a good
> solution (and won't work at all in the case of virtual functions,
> since they can't be templatized).
[...]

No need to templatize anything, just ship two versions of the library,
one with DbC compiled in, one without. Let the user decide which one to
link in.


T

-- 
Never trust an operating system you don't have source for! -- Martin Schulze


Re: The Right Approach to Exceptions

2012-02-23 Thread Jonathan M Davis
On Thursday, February 23, 2012 07:47:55 H. S. Teoh wrote:
> On Thu, Feb 23, 2012 at 02:57:43AM -0800, Jonathan M Davis wrote:
> [...]
> 
> > DbC tends to work better with internal stuff where you control both
> > the caller and the callee, whereas defensive programming works better
> > with public APIs.  But regardless, which is best to use depends on the
> > situtation and what you're goals are.
> 
> [...]
> 
> The way I understand it, DbC is used for ensuring *program* correctness
> (ensure that program logic does not get itself into a bad state);
> defensive programming is for sanitizing *user input* (ensure that no
> matter what the user does, the program doesn't get into a bad state).
> 
> That's why DbC is compiled out in release mode -- the assumption is that
> you have thoroughly tested your program logic and verified there are no
> logic problems. Input sanitizing is never compiled out, because you
> never know what users will do, so you always have to check.
> 
> The two do somewhat overlap, of course. For example, failing to sanitize
> user input may eventually lead to passing invalid arguments to an
> internal function.

Exactly. But where things tend to blur is the concept of "user input." For 
instance, if you're using a 3rd party library, should it be asserting on the 
arguments that you pass it? Unless you compile it in non-release mode, it 
obviously won't, which could be an argument for using exceptions, but 
regardless of that, from the library's perspective, you're a user. If it used 
DbC, it would be putting assertions it in its own code to test _your_ code. 
And since you're a user, it arguably should use exceptions to make sure that 
the arguments that it gets are correct. So, it all tends to get blurry, and 
the best decision varies from situation to situation. It's also part of what 
makes the concept of assertion vs exception harder for some folks (and as nice 
as enforce may be it, it blurs the line even further for many people IMHO, 
since it then makes exceptions the same as assertions syntactically).

Arguably, the best thing would be if there was a way for the caller to 
indicate whether it wanted the callee to have DbC enable and possibly even 
indicate whether it wanted the callee to use DbC or defensive programming. But 
there's no way to do that in D, and I'm not sure that it could even be done 
with the C linking model - at least, there's no way to it without templatizing 
everything and giving an argument to the template indicating what you want, 
which obviously isn't a good solution (and won't work at all in the case of 
virtual functions, since they can't be templatized).

- Jonathan M Davis


Re: The Right Approach to Exceptions

2012-02-23 Thread James Miller
On 23 February 2012 05:09, Regan Heath  wrote:
> On Tue, 21 Feb 2012 14:19:17 -, Andrei Alexandrescu
>  wrote:
>
>> On 2/21/12 5:55 AM, Regan Heath wrote:
>>>
>>> On Sun, 19 Feb 2012 23:04:59 -, Andrei Alexandrescu
>>>  wrote:
>>>
 On 2/19/12 4:00 PM, Nick Sabalausky wrote:
>>>
>>>
> Seriously, how is this not *already* crystal-clear? I feel as if
> every few
> weeks you're just coming up with deliberately random shit to argue so
> the
> rest of us have to waste our time spelling out the obvious in insanely
> pedantic detail.


 It sometimes happened to me to be reach the hypothesis that my
 interlocutor must be some idiot. Most often I was missing something.
>>>
>>>
>>> I get the impression that you find "Devil's advocate" a useful tool for
>>> generating debate and out of the box thinking.. there is something to be
>>> said for that, but it's probably less annoying to some if you're clear
>>> about that from the beginning. :p
>>
>>
>> Where did it seem I was playing devil's advocate? Thanks.
>
>
> "Devil's Advocate" is perhaps not the right term, as you don't seem to ever
> argue the opposite to what you believe.  But, it occasionally seems to me
> that you imply ignorance on your part, in order to draw more information
> from other posters on exactly what they think or are proposing.  So, some
> get frustrated as they feel they have to explain "everything" to you (and
> not just you, there have been times where - for whatever reason - it seems
> that anything less than a description of every single minute detail results
> in a miss understanding - no doubt partly due to the medium in which we are
> communicating).
>
>
> Regan
>
> --
> Using Opera's revolutionary email client: http://www.opera.com/mail/

I think that is technically called being facetious.

--
James Miller


Re: The Right Approach to Exceptions

2012-02-23 Thread H. S. Teoh
On Thu, Feb 23, 2012 at 02:57:43AM -0800, Jonathan M Davis wrote:
[...]
> DbC tends to work better with internal stuff where you control both
> the caller and the callee, whereas defensive programming works better
> with public APIs.  But regardless, which is best to use depends on the
> situtation and what you're goals are.
[...]

The way I understand it, DbC is used for ensuring *program* correctness
(ensure that program logic does not get itself into a bad state);
defensive programming is for sanitizing *user input* (ensure that no
matter what the user does, the program doesn't get into a bad state).

That's why DbC is compiled out in release mode -- the assumption is that
you have thoroughly tested your program logic and verified there are no
logic problems. Input sanitizing is never compiled out, because you
never know what users will do, so you always have to check.

The two do somewhat overlap, of course. For example, failing to sanitize
user input may eventually lead to passing invalid arguments to an
internal function.


T

-- 
Only boring people get bored. -- JM


Re: The Right Approach to Exceptions

2012-02-23 Thread Jonathan M Davis
On Wednesday, February 22, 2012 22:33:47 Jim Hewes wrote:
> On 2/21/2012 2:29 PM, Ali Çehreli wrote:
> > On 02/18/2012 09:09 PM, Jim Hewes wrote:
> >  > I think of exception handling as tied to contract programming.
> > 
> > I think your use of the word 'contract' is colliding with the contract
> > programming feature. What you describe later does not match with the
> > contract programming and I guess is the reason why Andrei is pointing
> > out two chapters from TDPL.
> > 
> > I will reread those chapters later today but I think Andrei is referring
> > to the distinction between assert() and std.exception.enforce().
> 
> Thanks. I assume the objection is about the bad parameters. In design by
> contract, a function should not be checking the input, correct? It
> assumes it's correct. But I was mostly thinking of the case when the
> functions are more of a public API and you can't trust the input. I did
> mention using assert for internal functions. But I guess if you are
> strict, you should never check input. I just shouldn't mention design by
> contract at all then. :)

In DbC, you use assertions to verify arguments, because it's up to the caller 
to ensure that the arguments are valid - otherwise it's breaking the contract. 
So, in release mode, there are no checks, and in non-release mode, your 
program gets killed if the contract is violated. It's considered a bug in the 
code if bad arguments are passed to the function.

In defensive programming, however, you use exceptions, which is much safer, 
because the function will never actually execute with bad arguments, but it's 
slower, because it's always checking, whereas assertions are compiled out in 
release mode. Also, it's not necessarily a bug when you pass an invalid 
argument to a function using defensive programming, because throwing an 
failure is part of its normal behavior (and won't be compiled out in release 
mode) and is recoverable, unlike an AssertError. Rather, the function is 
merely informing the caller that it was given bad arguments and lets the 
caller sort it out.

DbC tends to work better with internal stuff where you control both the caller 
and the callee, whereas defensive programming works better with public APIs. 
But regardless, which is best to use depends on the situtation and what you're 
goals are.

- Jonathan M Davis


Re: The Right Approach to Exceptions

2012-02-23 Thread Jim Hewes

On 2/21/2012 2:29 PM, Ali Çehreli wrote:

On 02/18/2012 09:09 PM, Jim Hewes wrote:

 > I think of exception handling as tied to contract programming.

I think your use of the word 'contract' is colliding with the contract
programming feature. What you describe later does not match with the
contract programming and I guess is the reason why Andrei is pointing
out two chapters from TDPL.

I will reread those chapters later today but I think Andrei is referring
to the distinction between assert() and std.exception.enforce().



Thanks. I assume the objection is about the bad parameters. In design by 
contract, a function should not be checking the input, correct? It 
assumes it's correct. But I was mostly thinking of the case when the 
functions are more of a public API and you can't trust the input. I did 
mention using assert for internal functions. But I guess if you are 
strict, you should never check input. I just shouldn't mention design by 
contract at all then. :)


Re: new std.variant (was Re: The Right Approach to Exceptions)

2012-02-23 Thread H. S. Teoh
On Thu, Feb 23, 2012 at 04:00:04AM +0100, Juan Manuel Cabo wrote:
[...]
> I'm sorry. I went over the top. I apollogize.

I apologize too, for some of the inflammatory things I said in the heat
of the moment in some of my replies to this thread.


> ..I won't post for a while.
> This thread is almost poping a vein in my neck..

Heh, several times I had to stop myself from hitting Send, take a deep
breath, clear my head, and go back to edit out my inflammatory remarks
before posting it.


> Passion can do that!
> I love D. Love all your good work guys!!!
[...]

Me too. D is the closest to what I've always wanted in an ideal
programming language. It's not perfect, and its implementation still has
a ways to go, but it already far surpasses all other languages that I've
looked at in terms of what I consider important in an ideal language.
I'm loving it so much I just can't convince myself to go back to C++
anymore in my personal projects, except for maintenance. (At work I just
grit my teeth and comfort myself that they're paying me for it, so I'll
tolerate C/C++. :P)


T

-- 
Без труда не выловишь и рыбку из пруда. 


Re: Towards a better conceptual model of exceptions (Was: Re: The Right Approach to Exceptions)

2012-02-22 Thread H. S. Teoh
On Thu, Feb 23, 2012 at 04:37:37AM +0100, Kapps wrote:
[...]
> The Condition approach you propose is fairly similar to what I was
> thinking of, as an approach to fixing Problems that occur (not
> necessarily exceptions). One of the fundamental problems I have with
> exceptions is that, despite intended to be recoverable as opposed to
> Errors, they are not recoverable. The operation still fails when an
> exception is thrown, and you have to restart all over again (even if
> it's something easily recoverable from).

Exactly!! As soon as the stack unwinds, you've just left behind the very
context that would have been the best position to fix problems from. In
a properly-written program, the caller of the function treats the
throwing function as a black box, so once the stack unwinds back to the
caller, there's no more possibility of repairing the problem. The best
you can do is to repeat the operation from scratch and hope it works
this time.

That's what I like so much about the Lisp approach. The high-level code
registers handlers that are qualified to make high-level decisions, and
the low-level code implements the low-level details of how to go about
fixing the problem.


> The other issue, is that exceptions are just poorly designed for
> multithreaded and event based applications. If you queue some work to
> be done, you likely won't even know if an exception is thrown. If you
> have a callback / event based approach, you have to do the equivalent
> of returning error codes (something exceptions were designed to
> avoid), except with passing in error codes instead. An example is .NET
> forcing all asynchronous operations to have a Begin and an End
> with the End rethrowing whatever problems occurred during the
> operation (even if they were easily solveable if the caller simply had
> immediate access to handle them).

I've thought about this too, though I don't currently know what's the
right way to implement it. For an event-based program, Conditions aren't
good enough, because high-level handlers are deregistered once the
registering function's scope exits. This is to prevent unrelated
functions from polluting each other's handler stack, with handlers from
one function catching stuff it shouldn't from other code.

But for an event-based program, you *need* persistent problem handlers.
The question is how to prevent persistent handlers from getting problem
notifications that they weren't designed to handle.

One idea, which I haven't really thought through yet, is to introduce
the concept of Domains. A Domain represents a particular operation
carried out by some sequence of events and their respective callbacks.
For instance, in a network app, a "Login" operation involves calling a
Login function in the communications layer, with a callback to notify
when the login process is completed. The Login function in turn calls
the Connection layer to connect to the remote server, with a callback to
inform the Login function when the server is connected. This callback in
turn calls the SendMsg function to initiate the authentication process,
with its respective callback, etc.. The sum total of all these
components: the Login function, its callback, the Connection layer, its
callback, etc.., comprise the Login domain.

Domains may be nested; for example, the Connection layer may call the
Socket layer and the Packet layer, all of which have their own set of
callbacks. This "initiate connection" process constitutes a subdomain of
the Login domain, and the "send packet" process constitutes a subdomain
of the "initiate connection" domain.

Each problem handler is then associated with a particular domain (and
its subdomains). Whenever a problem occurs, the problem handling system
identifies in which domain the problem happened, and searches up the
domain hierarchy (starting from the lowest-level domain, like "send
packet", up to "initiate connection" up to "Login") for problem
handlers. The problem handler then can take the necessary actions to
correct the problem.

The Condition system can (and probably should) be adapted to this model
too. For example if the "initiate connection" domain gets a timeout
error from the packet layer, the best place to attempt retry is right
there in the Connection layer. However, that's not the best place to
decide whether or not to retry; it may be the case that a higher-level
handler (i.e. handler higher up the domain hierarchy) is in a better
position to make this decision. So somehow they need to be able to
cooperate with each other.

The details of how this can work still need to be worked out.


> [...]
> (Unfortunately, unless you have a global problem handler for the
> entire transfer operation, this still suffers from the second issue
> about how those exceptions do not get carried up to the caller that
> starte the operation.)

I think with the concept of Domains, we can solve this. Problems that
are found in lower level domains bubble up the domain hierarchy un

Re: Towards a better conceptual model of exceptions (Was: Re: The Right Approach to Exceptions)

2012-02-22 Thread Kapps

On Wednesday, 22 February 2012 at 07:30:54 UTC, H. S. Teoh wrote:
I have an idea. What if handlers took *two* arguments, a 
Condition, and
a (possibly derived) Exception object? The raise system would 
then match
conditions to handlers by both the Condition type and derived 
Exception

type.
*snip*



The Condition approach you propose is fairly similar to what I 
was thinking of, as an approach to fixing Problems that occur 
(not necessarily exceptions). One of the fundamental problems I 
have with exceptions is that, despite intended to be recoverable 
as opposed to Errors, they are not recoverable. The operation 
still fails when an exception is thrown, and you have to restart 
all over again (even if it's something easily recoverable from). 
The other issue, is that exceptions are just poorly designed for 
multithreaded and event based applications. If you queue some 
work to be done, you likely won't even know if an exception is 
thrown. If you have a callback / event based approach, you have 
to do the equivalent of returning error codes (something 
exceptions were designed to avoid), except with passing in error 
codes instead. An example is .NET forcing all asynchronous 
operations to have a Begin and an End with the End 
rethrowing whatever problems occurred during the operation (even 
if they were easily solveable if the caller simply had immediate 
access to handle them).


The first one, is solveable with something similar to Conditions. 
A Problem would generally be a state issue. For example, you're 
writing a simple file transfer tool. The client authenticates 
with the server and gets a session ID. The server then starts 
transferring to the client, when suddenly the client loses 
connection for a moment (maybe they lost Wi-Fi signal, maybe 
someone unplugged their router momentarily, maybe they just had a 
bad route). With the exception based approach, the only way to 
notify this is to stop the entire operation, reconnect, 
reauthenticate, create a new session, negotiate what was being 
transfered, negotiate how far in the transfer completed, and 
other annoying things. Instead, we could register a handler to 
solve network problems. At the lowest level, the socket could 
have a handler that attempts to reconnect if any socket 
operations fail due to a connection error. Then, the transfer 
protocol could have a handler that, if the dependent socket 
reconnect handler is successful, notifies the server of the 
session key (and maybe the last packet it received) and carries 
on seamlessly without having to do a bunch of work. If the socket 
reconnect fails, the network problem handler reports a failure, 
and the transfer protocol handler does not get executed. If there 
is no transfer protocol handler, or there is one that basically 
says a solution is not possible, the same happens. Instead, that 
Problem is executed into an Exception, as the operation could not 
continue. (Unfortunately, unless you have a global problem 
handler for the entire transfer operation, this still suffers 
from the second issue about how those exceptions do not get 
carried up to the caller that starte the operation.)


The transfer approach might not be the greatest example, but it 
demonstrates something that (IMO) Exceptions are misused for, 
which affects the design of Exceptions themselves as it attempts 
to hammer a solution they're not great for in with them.


Re: new std.variant (was Re: The Right Approach to Exceptions)

2012-02-22 Thread Juan Manuel Cabo
On Thursday, 23 February 2012 at 02:12:20 UTC, Juan Manuel Cabo 
wrote:



If we are going to get ideallistic [..]


I'm sorry. I went over the top. I apollogize.

..I won't post for a while.
This thread is almost poping a vein in my neck..

Passion can do that!
I love D. Love all your good work guys!!!

--jm





Re: new std.variant (was Re: The Right Approach to Exceptions)

2012-02-22 Thread Jonathan M Davis
On Wednesday, February 22, 2012 20:07:37 Robert Jacques wrote:
> StringBuilder in .Net is implemented using lists and doesn't expose
> iteration nor indexing; why are we worrying about the indexing and
> container performance of D's appender?l

Because we're losing something that we currently have and which is potentially 
useful. Now, improving the performance of appending may outweigh that, but if 
we don't need to lose it, then we shouldn't.

>From the sounds of it though, we don't have much choice if we want to really 
improve Appender's appending performance.

- Jonathan M Davis


Re: new std.variant (was Re: The Right Approach to Exceptions)

2012-02-22 Thread Juan Manuel Cabo
On Thursday, 23 February 2012 at 01:57:49 UTC, Jonathan M Davis 
wrote:

The D equivalent would really be Array, not Appender.


Array!T in D is ref counted and more geared towards T being a
struct. And I had big trouble sorting it with sort!()() in D2.056,
so I made my own sort just to be able to use Array!(T).

I know that that situation will not remain forever (and I didn't
check if it was already fixed in D2.058).


I'm not sure that it's a great idea to use Appender as a
container - particularly when there are types
specifically intended to be used as containers. Appender is 
geared specifically  towards array building (like StringBuilder 
in Java, except generalized for all
arrays). If it's a container that you're looking for, then I 
really think that

you should use a container.

- Jonathan M Davis


If Appender supports the range interface, then it is a
container. Someone will use it that way, because in the real
world people take the things for what they are, not for
what they are named.

Appender can work with T classes well. Contains items.
It would be GC managed (as opposed to Array!T).
So it is a container. If it is not O(1) to access, it
should be said big in the ddoc though.

It is a recurrent trend in your posts, that you post just
because you have some ideallistic concern or opinion.

If we are going to get ideallistic, this is an extract
of a poem by Jorge Luis Borges (argentinian writer) that
illustrates my point:

  If (as the Greek states in the Cratilo)
  the name is the archetype of the thing,
  in the letters of rose it is the rose
  and all the Nile is in the word Nile

  Si como escribió el griego en el Crátilo,
  el nombre es arquetipo de la cosa,
  en el nombre de rosa está la rosa
  y todo el Nilo en la palabra Nilo.
  --Jorge Luis Borges

--jm













Re: new std.variant (was Re: The Right Approach to Exceptions)

2012-02-22 Thread Robert Jacques

On Wed, 22 Feb 2012 19:57:37 -0600, Jonathan M Davis  
wrote:


On Thursday, February 23, 2012 02:36:31 Juan Manuel Cabo wrote:

Yeah, but I don't care about the underlying array. I care
about multiple places referencing the same Appender. If I
from any place that references it, it appends to the same
appender. The Appender "array" has identity. Ranges do not:

int[] bla = [1,2,3];
int[] ble = bla;
ble ~= 4;
assert(bla.length == 3);

This is very easy to solve with appender.
This is what happens in Java:
ArrayList bla = new ArrayList();
bla.add(1);
ArrayList ble = bla;
ble.add(2);
//prints 2
System.out.println(Integer.toString(bla.size()));
//prints 2
System.out.println(Integer.toString(ble.size()));

(yikes, aint that verbose!)
The ArrayList has identity. It is a class, so that it
many variables reference the _same_ object.
(this can be accomplished with structs too though, but
not with ranges).


The D equivalent would really be Array, not Appender. I'm not sure that it's a
great idea to use Appender as a container - particularly when there are types
specifically intended to be used as containers. Appender is geared specifically
towards array building (like StringBuilder in Java, except generalized for all
arrays). If it's a container that you're looking for, then I really think that
you should use a container.

- Jonathan M Davis


StringBuilder in .Net is implemented using lists and doesn't expose iteration 
nor indexing; why are we worrying about the indexing and container performance 
of D's appender?


Re: new std.variant (was Re: The Right Approach to Exceptions)

2012-02-22 Thread Juan Manuel Cabo
On Thursday, 23 February 2012 at 01:36:32 UTC, Juan Manuel Cabo 
wrote:
On Thursday, 23 February 2012 at 00:51:38 UTC, Jonathan M Davis 
wrote:

[...]
If appender ends up with multiple arrays in it, then random 
access is no longer O(1) and is therefore unacceptable. As 
such, most sort algorithms wouldn't work with it.


If all I want is binary search on a big appender, then it
is O(k * n * log(n)), and that k right there doesn't
bother me. Also, binary search is absolutely not
cpu cache friendly to begin with.

Also, your bit about using appender to pass an array around 
wouldn't work either, because it wouldn't simply be wrapper

around an array anymore.

- Jonathan M Davis


Yeah, but I don't care about the underlying array. I care
about multiple places referencing the same Appender. If I
from any place that references it, it appends to the same
appender. The Appender "array" has identity. Ranges do not:

 int[] bla = [1,2,3];
 int[] ble = bla;
 ble ~= 4;
 assert(bla.length == 3);

This is very easy to solve with appender.
This is what happens in Java:
ArrayList bla = new ArrayList();
bla.add(1);
ArrayList ble = bla;
ble.add(2);
//prints 2
System.out.println(Integer.toString(bla.size()));
//prints 2
System.out.println(Integer.toString(ble.size()));

(yikes, aint that verbose!)
The ArrayList has identity. It is a class, so that it
many variables reference the _same_ object.
(this can be accomplished with structs too though, but
not with ranges).


I meant ref counted structs.






P.S. Please don't top post. Replies should go _after_ the 
preceding message.


Sorry, got it.

--jm





Re: new std.variant (was Re: The Right Approach to Exceptions)

2012-02-22 Thread Jonathan M Davis
On Thursday, February 23, 2012 02:36:31 Juan Manuel Cabo wrote:
> Yeah, but I don't care about the underlying array. I care
> about multiple places referencing the same Appender. If I
> from any place that references it, it appends to the same
> appender. The Appender "array" has identity. Ranges do not:
> 
> int[] bla = [1,2,3];
> int[] ble = bla;
> ble ~= 4;
> assert(bla.length == 3);
> 
> This is very easy to solve with appender.
> This is what happens in Java:
> ArrayList bla = new ArrayList();
> bla.add(1);
> ArrayList ble = bla;
> ble.add(2);
> //prints 2
> System.out.println(Integer.toString(bla.size()));
> //prints 2
> System.out.println(Integer.toString(ble.size()));
> 
> (yikes, aint that verbose!)
> The ArrayList has identity. It is a class, so that it
> many variables reference the _same_ object.
> (this can be accomplished with structs too though, but
> not with ranges).

The D equivalent would really be Array, not Appender. I'm not sure that it's a 
great idea to use Appender as a container - particularly when there are types 
specifically intended to be used as containers. Appender is geared specifically 
towards array building (like StringBuilder in Java, except generalized for all 
arrays). If it's a container that you're looking for, then I really think that 
you should use a container.

- Jonathan M Davis


Re: new std.variant (was Re: The Right Approach to Exceptions)

2012-02-22 Thread Juan Manuel Cabo
On Thursday, 23 February 2012 at 00:51:38 UTC, Jonathan M Davis 
wrote:
P.S. Please don't top post. Replies should go _after_ the 
preceding message.


P.S: You are right though, that it wouldn't be O(1) anymore
and it should be said big in the documentation that it is
amortized.

--jm




Re: new std.variant (was Re: The Right Approach to Exceptions)

2012-02-22 Thread Juan Manuel Cabo
On Thursday, 23 February 2012 at 01:36:32 UTC, Juan Manuel Cabo 
wrote:

If all I want is binary search on a big appender, then it
is O(k * n * log(n)), and that k right there doesn't
bother me.


(Where binary search is of course O(log(n))
and accessing individual elements with the proposed
Appender is O(N / (4080/T.sizeof)), so k == 4080/T.sizeof)

--jm




Re: new std.variant (was Re: The Right Approach to Exceptions)

2012-02-22 Thread Juan Manuel Cabo
On Thursday, 23 February 2012 at 00:51:38 UTC, Jonathan M Davis 
wrote:

[...]
If appender ends up with multiple arrays in it, then random 
access is no longer O(1) and is therefore unacceptable. As 
such, most sort algorithms wouldn't work with it.


If all I want is binary search on a big appender, then it
is O(k * n * log(n)), and that k right there doesn't
bother me. Also, binary search is absolutely not
cpu cache friendly to begin with.

Also, your bit about using appender to pass an array around 
wouldn't work either, because it wouldn't simply be wrapper

around an array anymore.

- Jonathan M Davis


Yeah, but I don't care about the underlying array. I care
about multiple places referencing the same Appender. If I
from any place that references it, it appends to the same
appender. The Appender "array" has identity. Ranges do not:

 int[] bla = [1,2,3];
 int[] ble = bla;
 ble ~= 4;
 assert(bla.length == 3);

This is very easy to solve with appender.
This is what happens in Java:
ArrayList bla = new ArrayList();
bla.add(1);
ArrayList ble = bla;
ble.add(2);
//prints 2
System.out.println(Integer.toString(bla.size()));
//prints 2
System.out.println(Integer.toString(ble.size()));

(yikes, aint that verbose!)
The ArrayList has identity. It is a class, so that it
many variables reference the _same_ object.
(this can be accomplished with structs too though, but
not with ranges).




P.S. Please don't top post. Replies should go _after_ the 
preceding message.


Sorry, got it.

--jm




Re: new std.variant (was Re: The Right Approach to Exceptions)

2012-02-22 Thread Robert Jacques

On Wed, 22 Feb 2012 18:51:27 -0600, Jonathan M Davis  
wrote:

On Thursday, February 23, 2012 01:38:05 Juan Manuel Cabo wrote:

(And not talking about some cheesy insertion sort!!)

If you build an array once and for all, and all you want
is to do binary search on it later, it doesn't make sense to
allocate that big contiguous .data. I'd rather leave it
as an appender.

--jm


On Wednesday, 22 February 2012 at 23:22:35 UTC, Juan Manuel Cabo

wrote:
>> No, because the array doesn't actually exist until appender
>> makes copy.
>
> Will one be able to use the sort!()() algorithm directly on
> your appender,
> that is, without accessing/creating the underlying array?


If appender ends up with multiple arrays in it, then random access is no
longer O(1) and is therefore unacceptable. As such, most sort algorithms
wouldn't work with it.

Also, your bit about using appender to pass an array around wouldn't work
either, because it wouldn't simply be wrapper around an array anymore.

- Jonathan M Davis


P.S. Please don't top post. Replies should go _after_ the preceding message.


Well, a VList (which is a list of arrays) is has O(1) amortized indexing. 
Actual performance of my implementation would be O(N / (4080/T.sizeof)), which 
isn't so bad. And anything under a page of ram would be O(1).


Re: new std.variant (was Re: The Right Approach to Exceptions)

2012-02-22 Thread H. S. Teoh
On Wed, Feb 22, 2012 at 07:51:27PM -0500, Jonathan M Davis wrote:
[...]
> P.S. Please don't top post. Replies should go _after_ the preceding message.

Answer: Because it breaks the normal flow of conversation.

Question: Why is it bad to top-post?


T

-- 
Why waste time learning, when ignorance is instantaneous? -- Hobbes, from 
Calvin & Hobbes


Re: new std.variant (was Re: The Right Approach to Exceptions)

2012-02-22 Thread Jonathan M Davis
On Thursday, February 23, 2012 01:38:05 Juan Manuel Cabo wrote:
> (And not talking about some cheesy insertion sort!!)
> 
> If you build an array once and for all, and all you want
> is to do binary search on it later, it doesn't make sense to
> allocate that big contiguous .data. I'd rather leave it
> as an appender.
> 
> --jm
> 
> 
> On Wednesday, 22 February 2012 at 23:22:35 UTC, Juan Manuel Cabo
> 
> wrote:
> >> No, because the array doesn't actually exist until appender
> >> makes copy.
> > 
> > Will one be able to use the sort!()() algorithm directly on
> > your appender,
> > that is, without accessing/creating the underlying array?

If appender ends up with multiple arrays in it, then random access is no 
longer O(1) and is therefore unacceptable. As such, most sort algorithms 
wouldn't work with it.

Also, your bit about using appender to pass an array around wouldn't work 
either, because it wouldn't simply be wrapper around an array anymore.

- Jonathan M Davis


P.S. Please don't top post. Replies should go _after_ the preceding message.


Re: new std.variant (was Re: The Right Approach to Exceptions)

2012-02-22 Thread Juan Manuel Cabo

(And not talking about some cheesy insertion sort!!)

If you build an array once and for all, and all you want
is to do binary search on it later, it doesn't make sense to
allocate that big contiguous .data. I'd rather leave it
as an appender.

--jm


On Wednesday, 22 February 2012 at 23:22:35 UTC, Juan Manuel Cabo 
wrote:
No, because the array doesn't actually exist until appender 
makes copy.


Will one be able to use the sort!()() algorithm directly on 
your appender,

that is, without accessing/creating the underlying array?

--jm





Re: new std.variant (was Re: The Right Approach to Exceptions)

2012-02-22 Thread Juan Manuel Cabo
On Wednesday, 22 February 2012 at 20:59:15 UTC, Jonathan M Davis 
wrote:
speed [...] is really its whole point of existance. I don't 
know why else you'd ever use appender.

[...]

- Jonathan M Davis


A use case is to give identity to a built-in array.

Consider this:

 class MyClass {
 private MyData[] theData;

 public @property MyData[] data() {
 return theData;
 }
 ...
 }


 MyClass m = new MyClass();
 m.data ~= new MyData();
 //Nothing got appended:
 assert(m.data.length == 0);

For the 95% of the use cases, that is the desired
behaviour. You don't want anyone appending to
your private array. If you wanted to, you would
have defined MyClass.append(myData).

  But there are a few cases where you want to
give identity to the array, and let anyone who
has a "handle" to it, to be able to append it.
(another case is while porting code from languages
that don't represent arrays as ranges, and return
them as getters).

--jm






Re: new std.variant (was Re: The Right Approach to Exceptions)

2012-02-22 Thread Juan Manuel Cabo
No, because the array doesn't actually exist until appender 
makes copy.


Will one be able to use the sort!()() algorithm directly on your 
appender,

that is, without accessing/creating the underlying array?

--jm




Re: new std.variant (was Re: The Right Approach to Exceptions)

2012-02-22 Thread Robert Jacques
On Wed, 22 Feb 2012 14:17:09 -0600, Jonathan M Davis   
wrote:

On Wednesday, February 22, 2012 14:12:07 Jonathan M Davis wrote:

On Wednesday, February 22, 2012 12:16:43 Robert Jacques wrote:
> There's a big difference between sealed and not accessible. .data's  
API
> requires exposing an array, and there's no way to do this without  
leaking
> memory like a sieve in one way or another. However, if all you need  
is to
> iterate the contents, that's easy to do. I'm currently adding  
backwards

> iteration. Even indexing is fairly efficient (not yet implemented),
> particularly relative indexing (i.e. n from back or front).
>
> I haven't seen too many use cases yet where accessing the underlying  
array

> is important, nor has it come up on bugzilla. I've found one case in
> Phobos where appender was used as a stack. What's your example? What
> features does it have to support and how efficient does it have to be?

It's can be useful to just get at the underlying array and pass it to
functions which are going to use it but not alter it (or at least not  
append
to it). Iterating over it doesn't give you an array. And since  
appender's

entire purpose is simply to make appending to an array more efficient,
making it impossible to treat it as one until you're done appending is
overly restrictive IMHO. Yes, if you leak references to the underlying
data, you're asking for trouble, but that doesn't mean that it can't be
used without leaking memory.

Unfortunately, I don't have any code snippets with me at the moment, so  
I
can't give any concrete examples of usage, but any situation where you  
want
to be able to operate on the array while building it needs the ability  
to

get at the underlying array. Yes, in most cases, you're probably simply
appending to the array, but at least once in a while, you need to  
operate

on an array while building it.


Also, wouldn't it be less efficient if you _had_ to copy the array once  
you were
done with the appender? That would seem to go against what appender is  
trying

to do.

- Jonathan M Davis


No, because the array doesn't actually exist until appender makes copy.  
Internally, appender is using a list of arrays to store the data (similar  
to a VList and string builders from other languages). So it's little o(2N)  
for both memory and speed; the current appender is much worse than that.  
In terms of actual performance, on a clean machine I'm substantially  
faster for N < 4k (thanks to a free list), about the same for things of a  
few k in size, and then as things get bigger the current appender tanks.


Re: new std.variant (was Re: The Right Approach to Exceptions)

2012-02-22 Thread Jonathan M Davis
On Wednesday, February 22, 2012 14:24:49 Robert Jacques wrote:
> I view appender's purpose as array building, which is slightly different
>  from simply speeding up array appending. Simply put, an array is a
> terrible data structure for building arrays. But, I can appreciate the
> need for mutation and if a particular array building algorithm can't be
> performed on appender, then appender has failed. Would exposing a
> bidirectional array be sufficient for your usages? A random access range?

Well, as long as you don't have access to the actual array, you're going to 
lose something. There are functions that you just won't be able to use because 
they take an array. However, a range with essentially the same properties as a 
range (bidirectional, random access, etc.) would cover a large number of the 
functions that you might want to call. And if you need to hide the array for 
speed for efficiency for some reason - especially if it results in a large 
increase in speed - then that could at least partially outweigh the cost of 
losing the array (especially if Appender has an API to at least use it as a 
range). But I'd definitely be against hiding it simply for safety, since speed 
is really its whole point of existance. I don't know why else you'd ever use 
appender.

So, I don't really like the idea of losing access to the underlying array, but 
if you can provide at least make it so that you could use it with range-based 
functions, and the changes provides a sigificant speed improvement, then the 
need for speed arguably outweighs the loss of being able to use the internal 
array directly.

- Jonathan M Davis


Re: The Right Approach to Exceptions

2012-02-22 Thread deadalnix

Le 22/02/2012 18:50, H. S. Teoh a écrit :

On Wed, Feb 22, 2012 at 11:53:39AM +0100, deadalnix wrote:
[...]

Additionnaly, I would mention that the transient isn't a caracteristic
of the Exception, but of the recovery strategy.


Technically correct. Though I'm playing with the idea of making recovery
strategies a property of an exception - since a recovery strategy is
meaningless without an associated exception (or problem). I need to
think this through a bit more, though, as to how to correctly implement
this.



I did though about this. This isn't the right way. Recovery strategy 
doesn't have any meaning at the catch point, so we shouldn't make it a 
property of Exception.


And sometime you don't care about the Exception. If you try to connect 
something that is know to fail for exemple, you really don't want to 
know what went wrong. You just want to try again with some backoff.


I do think you made a point with the handler getting an Exception and a 
recovery stratgy as parameter, but it is still unclear where all this 
goes to me.


Re: new std.variant (was Re: The Right Approach to Exceptions)

2012-02-22 Thread Robert Jacques
On Wed, 22 Feb 2012 13:12:07 -0600, Jonathan M Davis   
wrote:

On Wednesday, February 22, 2012 12:16:43 Robert Jacques wrote:

There's a big difference between sealed and not accessible. .data's API
requires exposing an array, and there's no way to do this without  
leaking
memory like a sieve in one way or another. However, if all you need is  
to

iterate the contents, that's easy to do. I'm currently adding backwards
iteration. Even indexing is fairly efficient (not yet implemented),
particularly relative indexing (i.e. n from back or front).

I haven't seen too many use cases yet where accessing the underlying  
array

is important, nor has it come up on bugzilla. I've found one case in
Phobos where appender was used as a stack. What's your example? What
features does it have to support and how efficient does it have to be?


It's can be useful to just get at the underlying array and pass it to
functions which are going to use it but not alter it (or at least not  
append

to it). Iterating over it doesn't give you an array. And since appender's
entire purpose is simply to make appending to an array more efficient,  
making it

impossible to treat it as one until you're done appending is overly
restrictive IMHO. Yes, if you leak references to the underlying data,  
you're

asking for trouble, but that doesn't mean that it can't be used without
leaking memory.

Unfortunately, I don't have any code snippets with me at the moment, so I
can't give any concrete examples of usage, but any situation where you  
want to
be able to operate on the array while building it needs the ability to  
get at
the underlying array. Yes, in most cases, you're probably simply  
appending to
the array, but at least once in a while, you need to operate on an array  
while

building it.

- Jonathan M Davis


I view appender's purpose as array building, which is slightly different  
from simply speeding up array appending. Simply put, an array is a  
terrible data structure for building arrays. But, I can appreciate the  
need for mutation and if a particular array building algorithm can't be  
performed on appender, then appender has failed. Would exposing a  
bidirectional array be sufficient for your usages? A random access range?


Re: new std.variant (was Re: The Right Approach to Exceptions)

2012-02-22 Thread Jonathan M Davis
On Wednesday, February 22, 2012 14:12:07 Jonathan M Davis wrote:
> On Wednesday, February 22, 2012 12:16:43 Robert Jacques wrote:
> > There's a big difference between sealed and not accessible. .data's API
> > requires exposing an array, and there's no way to do this without leaking
> > memory like a sieve in one way or another. However, if all you need is to
> > iterate the contents, that's easy to do. I'm currently adding backwards
> > iteration. Even indexing is fairly efficient (not yet implemented),
> > particularly relative indexing (i.e. n from back or front).
> > 
> > I haven't seen too many use cases yet where accessing the underlying array
> > is important, nor has it come up on bugzilla. I've found one case in
> > Phobos where appender was used as a stack. What's your example? What
> > features does it have to support and how efficient does it have to be?
> 
> It's can be useful to just get at the underlying array and pass it to
> functions which are going to use it but not alter it (or at least not append
> to it). Iterating over it doesn't give you an array. And since appender's
> entire purpose is simply to make appending to an array more efficient,
> making it impossible to treat it as one until you're done appending is
> overly restrictive IMHO. Yes, if you leak references to the underlying
> data, you're asking for trouble, but that doesn't mean that it can't be
> used without leaking memory.
> 
> Unfortunately, I don't have any code snippets with me at the moment, so I
> can't give any concrete examples of usage, but any situation where you want
> to be able to operate on the array while building it needs the ability to
> get at the underlying array. Yes, in most cases, you're probably simply
> appending to the array, but at least once in a while, you need to operate
> on an array while building it.

Also, wouldn't it be less efficient if you _had_ to copy the array once you 
were 
done with the appender? That would seem to go against what appender is trying 
to do.

- Jonathan M Davis


Re: The Right Approach to Exceptions

2012-02-22 Thread Jacob Carlborg

On 2012-02-22 15:01, Andrei Alexandrescu wrote:

On 2/22/12 1:22 AM, Jacob Carlborg wrote:

Now I'm completely lost. According to what I've read this is thread this
is exactly what you want to do, put the formatting inside the exceptions.


No, just have exceptions inform an external formatter.

Andrei



Ok, I see.

--
/Jacob Carlborg


Re: new std.variant (was Re: The Right Approach to Exceptions)

2012-02-22 Thread Jonathan M Davis
On Wednesday, February 22, 2012 12:16:43 Robert Jacques wrote:
> There's a big difference between sealed and not accessible. .data's API
> requires exposing an array, and there's no way to do this without leaking
> memory like a sieve in one way or another. However, if all you need is to
> iterate the contents, that's easy to do. I'm currently adding backwards
> iteration. Even indexing is fairly efficient (not yet implemented),
> particularly relative indexing (i.e. n from back or front).
> 
> I haven't seen too many use cases yet where accessing the underlying array
> is important, nor has it come up on bugzilla. I've found one case in
> Phobos where appender was used as a stack. What's your example? What
> features does it have to support and how efficient does it have to be?

It's can be useful to just get at the underlying array and pass it to 
functions which are going to use it but not alter it (or at least not append 
to it). Iterating over it doesn't give you an array. And since appender's 
entire purpose is simply to make appending to an array more efficient, making 
it 
impossible to treat it as one until you're done appending is overly 
restrictive IMHO. Yes, if you leak references to the underlying data, you're 
asking for trouble, but that doesn't mean that it can't be used without 
leaking memory.

Unfortunately, I don't have any code snippets with me at the moment, so I 
can't give any concrete examples of usage, but any situation where you want to 
be able to operate on the array while building it needs the ability to get at 
the underlying array. Yes, in most cases, you're probably simply appending to 
the array, but at least once in a while, you need to operate on an array while 
building it.

- Jonathan M Davis


Re: new std.variant (was Re: The Right Approach to Exceptions)

2012-02-22 Thread Robert Jacques
On Wed, 22 Feb 2012 11:33:57 -0600, Jonathan M Davis   
wrote:

On Wednesday, February 22, 2012 08:19:38 Robert Jacques wrote:

To Variant? Yes, definitely. To Appender? I don't think so. There is an
slight change in API behavior necessitated by performance  
considerations,

but I don't think it warrants a review by the community at large.
Specifically, appender's internal data structure is now sealed, which  
means

that .data must always make a copy. My preference would be to deprecate
.data in favor of a .dup/.idup pair. It'll break a bunch of code (which  
I
don't like), but it will make sure no one is calling .data twice in a  
row,

resulting in a silent performance problem.


I've definitely written code that needed to get at data while appending.  
I
would consider it to be a huge downside to not be able to efficiently  
get at the
current state of the array within the appender. Why on earth would you  
seal

it?

- Jonathan M Davis


There's a big difference between sealed and not accessible. .data's API  
requires exposing an array, and there's no way to do this without leaking  
memory like a sieve in one way or another. However, if all you need is to  
iterate the contents, that's easy to do. I'm currently adding backwards  
iteration. Even indexing is fairly efficient (not yet implemented),  
particularly relative indexing (i.e. n from back or front).


I haven't seen too many use cases yet where accessing the underlying array  
is important, nor has it come up on bugzilla. I've found one case in  
Phobos where appender was used as a stack. What's your example? What  
features does it have to support and how efficient does it have to be?


Re: Towards a better conceptual model of exceptions (Was: Re: The Right Approach to Exceptions)

2012-02-22 Thread H. S. Teoh
On Wed, Feb 22, 2012 at 11:14:11AM +0100, deadalnix wrote:
> Le 22/02/2012 08:32, H. S. Teoh a écrit :
[...]
> >I have an idea. What if handlers took *two* arguments, a Condition,
> >and a (possibly derived) Exception object? The raise system would
> >then match conditions to handlers by both the Condition type and
> >derived Exception type. The handler then has access to the particular
> >derived object that it wants to handle without needing to downcast.
> >So it sorta simulates a catch(DerivedException).
> >
> Well I have no idea how to implement that. Let's put some thinking
> into this, it certainly an option. However, this require to create
> the Exception, even if in many cases, it will not be usefull.

I was thinking about using TypeInfo to match stuff up at runtime. But I
need to work through the details first to see if it's actually
implementable. This seems to be an area where language support would
help a lot.

Though it's better if we can do it without language support, so that we
can get this thing up and running and test it with real-life usage
before making language changes that may not benefit in the long run. :)


> About the name, it matters, but isn't the big issue here. When we have
> something working well, we could think about names. As the concept may
> be changed again, naming isn't that important.

Names are just identifiers, sure, but it does help to have a useful name
to help us think about the problem from a useful angle. For example if
we renamed Exception to Bug, the mechanics of the it (try, throw, catch)
would still work the same, but we would be likely to use it in the wrong
ways because "Bug" doesn't help us think about it in a useful way.


T

-- 
Why ask rhetorical questions? -- JC


Re: The Right Approach to Exceptions

2012-02-22 Thread H. S. Teoh
On Wed, Feb 22, 2012 at 11:53:39AM +0100, deadalnix wrote:
[...]
> Additionnaly, I would mention that the transient isn't a caracteristic
> of the Exception, but of the recovery strategy.

Technically correct. Though I'm playing with the idea of making recovery
strategies a property of an exception - since a recovery strategy is
meaningless without an associated exception (or problem). I need to
think this through a bit more, though, as to how to correctly implement
this.


T

-- 
The volume of a pizza of thickness a and radius z can be described by
the following formula: pi zz a. -- Wouter Verhelst


Re: new std.variant (was Re: The Right Approach to Exceptions)

2012-02-22 Thread Jonathan M Davis
On Wednesday, February 22, 2012 08:19:38 Robert Jacques wrote:
> To Variant? Yes, definitely. To Appender? I don't think so. There is an
> slight change in API behavior necessitated by performance considerations,
> but I don't think it warrants a review by the community at large.
> Specifically, appender's internal data structure is now sealed, which means
> that .data must always make a copy. My preference would be to deprecate
> .data in favor of a .dup/.idup pair. It'll break a bunch of code (which I
> don't like), but it will make sure no one is calling .data twice in a row,
> resulting in a silent performance problem.

I've definitely written code that needed to get at data while appending. I 
would consider it to be a huge downside to not be able to efficiently get at 
the 
current state of the array within the appender. Why on earth would you seal 
it?

- Jonathan M Davis


Re: The Right Approach to Exceptions

2012-02-22 Thread Regan Heath
On Tue, 21 Feb 2012 14:19:17 -, Andrei Alexandrescu  
 wrote:



On 2/21/12 5:55 AM, Regan Heath wrote:

On Sun, 19 Feb 2012 23:04:59 -, Andrei Alexandrescu
 wrote:


On 2/19/12 4:00 PM, Nick Sabalausky wrote:



Seriously, how is this not *already* crystal-clear? I feel as if
every few
weeks you're just coming up with deliberately random shit to argue so
the
rest of us have to waste our time spelling out the obvious in insanely
pedantic detail.


It sometimes happened to me to be reach the hypothesis that my
interlocutor must be some idiot. Most often I was missing something.


I get the impression that you find "Devil's advocate" a useful tool for
generating debate and out of the box thinking.. there is something to be
said for that, but it's probably less annoying to some if you're clear
about that from the beginning. :p


Where did it seem I was playing devil's advocate? Thanks.


"Devil's Advocate" is perhaps not the right term, as you don't seem to  
ever argue the opposite to what you believe.  But, it occasionally seems  
to me that you imply ignorance on your part, in order to draw more  
information from other posters on exactly what they think or are  
proposing.  So, some get frustrated as they feel they have to explain  
"everything" to you (and not just you, there have been times where - for  
whatever reason - it seems that anything less than a description of every  
single minute detail results in a miss understanding - no doubt partly due  
to the medium in which we are communicating).


Regan

--
Using Opera's revolutionary email client: http://www.opera.com/mail/


Re: The Right Approach to Exceptions

2012-02-22 Thread deadalnix

Le 22/02/2012 16:33, H. S. Teoh a écrit :

On Wed, Feb 22, 2012 at 11:18:13AM +0100, deadalnix wrote:

Le 21/02/2012 20:01, H. S. Teoh a écrit :

[...]

Does RTTI handle template methods?



I'm not aware of any language that have both template and RTTI. So I'm
clueless about this.


Doesn't C++ have RTTI?



You could argue that, but IMO, it look more like a stub than a real 
functionnality of the language.


Re: The Right Approach to Exceptions

2012-02-22 Thread H. S. Teoh
On Wed, Feb 22, 2012 at 11:18:13AM +0100, deadalnix wrote:
> Le 21/02/2012 20:01, H. S. Teoh a écrit :
[...]
> >Does RTTI handle template methods?
> >
> 
> I'm not aware of any language that have both template and RTTI. So I'm
> clueless about this.

Doesn't C++ have RTTI?


T

-- 
"The number you have dialed is imaginary. Please rotate your phone 90 degrees 
and try again."


Re: The Right Approach to Exceptions

2012-02-22 Thread Jose Armando Garcia
On Tue, Feb 21, 2012 at 6:32 PM, deadalnix  wrote:
> Le 21/02/2012 00:23, Andrei Alexandrescu a écrit :
>
>> On 2/20/12 4:44 PM, Juan Manuel Cabo wrote:
>>>
>>> HAhaha, it sometimes feel as though people are afraid that the
>>> Variant[string]
>>> idea is to never use plain old variables and never use exception
>>> subclasses. :-)
>>>
>>> On the contrary, the idea is so that plain old variables and exception
>>> subclasses
>>> can be created for the right reasons, and to remove cases where they need
>>> to be created for the wrong reasons.
>>
>>
>> Yah, I think there's a lot of confusion and consequent apprehension
>> regarding this. Thanks for attempting to clarify things.
>>
>> Andrei
>>
>
> So it doesn't help. Dulb subclasses of Exceptions are done mostly to be able
> to catch them. To avoid useless subclasses, we need a more precise way to
> catch Exception than the type only.
>

Agreed. I would love it if catch worked using a structural/pattern
matching mechanism. Something like template's if clause or like Scala
pattern matching...

-Jose
> This Variant[string] doesn't help.


Re: new std.variant (was Re: The Right Approach to Exceptions)

2012-02-22 Thread Robert Jacques

On Tue, 21 Feb 2012 21:51:34 -0600, Andrei Alexandrescu 
 wrote:

On 2/21/12 6:11 PM, Robert Jacques wrote:

On Tue, 21 Feb 2012 09:12:57 -0600, Adam D. Ruppe
 wrote:


On Tuesday, 21 February 2012 at 02:33:15 UTC, Robert Jacques
wrote:

Nope. See
(https://jshare.johnshopkins.edu/rjacque2/public_html/ )



Any luck in getting the required patches into phobos?

I'd love to see this full thing in there for the next release.

It rox.


I'm in the process of cleaning up the required patches and submitting
them. The first one's already in
(https://github.com/sandford/phobos/commit/8b845d2a50bc20993afed7306f3d84921e4ac54b).
I've almost got appender ready.


Are the changes substantial enough to put them through the review process?

Andrei


To Variant? Yes, definitely. To Appender? I don't think so. There is an slight 
change in API behavior necessitated by performance considerations, but I don't 
think it warrants a review by the community at large. Specifically, appender's 
internal data structure is now sealed, which means that .data must always make 
a copy. My preference would be to deprecate .data in favor of a .dup/.idup 
pair. It'll break a bunch of code (which I don't like), but it will make sure 
no one is calling .data twice in a row, resulting in a silent performance 
problem.


Re: The Right Approach to Exceptions

2012-02-22 Thread Andrei Alexandrescu

On 2/22/12 1:22 AM, Jacob Carlborg wrote:

Now I'm completely lost. According to what I've read this is thread this
is exactly what you want to do, put the formatting inside the exceptions.


No, just have exceptions inform an external formatter.

Andrei



Re: The Right Approach to Exceptions

2012-02-22 Thread deadalnix

Le 22/02/2012 06:47, H. S. Teoh a écrit :

On Tue, Feb 21, 2012 at 07:43:32PM -0500, Jonathan M Davis wrote:

On Tuesday, February 21, 2012 14:15:03 Andrei Alexandrescu wrote:

I thought I was pushing the generics angle, and OO people explained
it to me that that was wrong.


I've changed my mind. Now I'm trying to see if the generics angle has
some possibilities. Maybe, maybe not, but we'll never know without
experimenting with it. I think your is_transient idea can be expanded
upon.

The way exceptions are currently implemented, they only carry
information, not behaviour, as Jonathan said rightly.  The try/catch
mechanism essentially reduces to "I've hit a problem I don't know how to
solve, here's a description of it". There's no behaviour in there. The
throwing code has already given up.  It's up to the catcher to interpret
the description of the problem and figure out how to recover. To recover
well, the catcher must know the intimate details of the problem well. So
you have the situation of a specific catcher catching a specific
Exception subclass. This is not an ideal situation, because now
high-level code needs to know the specifics of low-level errors.

With your is_transient idea, though, this begins to change. Now we're no
longer just describing the problem.  When is_transient=1, it means the
thrower is suggesting that perhaps retrying would help. Of course, it's
up to the catcher whether or not to follow through with this suggestion,
but it's one step up from "here's a description of the problem, figure
out the solution yourself". But now the catcher doesn't necessarily have
to know the specifics of the low-level problem. It knows at least one
strategy that might fix the problem, regardless of what the problem is:
retry the operation. This is good, because the low-level code, which
knows the problem best, can offer a useful suggestion (retry). The
high-level code can just take the suggestion or not; it no longer needs
to know low-level details.

But why stop there? Since the low-level code knows all the dirty details
about the problem, it's in the best position to offer meaningful
recovery suggestions. It just has to communicate these possible recovery
strategies to the high-level code, and let the high-level code decide
what to do. The high-level code doesn't need to know how to implement
these strategies -- it's not in the best position to know that anyway.
It just knows, here's a list of recovery strategies, I can go ahead with
one of them, or just call it quits and unwind the stack. The low-level
code is what implements each strategy.

Of course, in order for the high-level code to meaningfully choose
between alternative strategies, the strategies themselves must be
generic concepts; otherwise we're still tying high-level code to
low-level details. So we need to identify generic categories of
exceptions for which this kind of generic recovery is meaningful --
which is what I've done in another post.

I won't repeat the details here, but I just want to say that I think
this angle merits some investigation. It allows us to factor out
exceptions which can be resolved by commonly used recovery strategies so
that we don't have to keep writing tons and tons of exception-specific
recovery code everywhere. Some specific code is still needed, no doubt,
there's always special cases that need specific handling.  But if enough
exceptions can be adequately dealt with generically, then we don't need
to write specific code for them. We can simply reuse generic recovery
solutions.


T



100% Agree.

Additionnaly, I would mention that the transient isn't a caracteristic 
of the Exception, but of the recovery strategy.


Re: The Right Approach to Exceptions

2012-02-22 Thread deadalnix

Le 21/02/2012 20:01, H. S. Teoh a écrit :

On Tue, Feb 21, 2012 at 07:57:37PM +0100, deadalnix wrote:

Le 21/02/2012 03:33, Robert Jacques a écrit :

[...]

Aren't __traits and opDispatch fun?


opDispatch is nice, but rather incomplete. It doesn't handle template
methods for example.


Does RTTI handle template methods?



I'm not aware of any language that have both template and RTTI. So I'm 
clueless about this.


Re: Towards a better conceptual model of exceptions (Was: Re: The Right Approach to Exceptions)

2012-02-22 Thread deadalnix

Le 22/02/2012 08:32, H. S. Teoh a écrit :

On Tue, Feb 21, 2012 at 08:56:03PM +0100, deadalnix wrote:

Le 21/02/2012 20:00, H. S. Teoh a écrit :

[...]

You're right, that would be unnecessary duplication, especially since
an unhandled Condition becomes a thrown Exception anyway, and it's a
very bad idea to duplicate the entire Exception hierarchy in
Condition.

Only thing is, then the handler will have to downcast the Exception
to get to the useful info. This may lead to messy code. But it's
something we should investigate.



Yes, I'm aware of the problem and it is real. I have no good solution
for it now.


I have an idea. What if handlers took *two* arguments, a Condition, and
a (possibly derived) Exception object? The raise system would then match
conditions to handlers by both the Condition type and derived Exception
type. The handler then has access to the particular derived object that
it wants to handle without needing to downcast. So it sorta simulates a
catch(DerivedException).

Well I have no idea how to implement that. Let's put some thinking into 
this, it certainly an option. However, this require to create the 
Exception, even if in many cases, it will not be usefull.


About the name, it matters, but isn't the big issue here. When we have 
something working well, we could think about names. As the concept may 
be changed again, naming isn't that important.


Re: The Right Approach to Exceptions

2012-02-22 Thread Jacob Carlborg

On 2012-02-22 08:33, Jonathan M Davis wrote:

On Wednesday, February 22, 2012 08:22:21 Jacob Carlborg wrote:

Now I'm completely lost. According to what I've read this is thread this
is exactly what you want to do, put the formatting inside the exceptions.


No. He wants to provide a way for an external function to generically generate
strings according to the format that you want when you generate the string.
So, some function would take a formatting string of some kind and then read
the corresponding values form the Variant[string] in Exception and generate a
string according to that format string. How exactly that works, I don't
understand (something about a string template language), but that's the idea.

So, while you could still use toString, there would be a way to generate
strings formatted the way that _you_ want rather than how toString would do it
- and to do it in a generic manner.

As long as this doesn't mean using Variant[string] as the way to inject all of
the extra data into exceptions and we still use an exception hierarchy with
the appropriate data members in derived exceptions, then I don't really see
that as a problem. The problem is if we then also get rid of the hierarchy
and/or try and put all of the data is the Variant[string] and only in the
Variant[string].


I agree.


From the sounds of it, we have _some_ agreement to have an exception hierarchy

with data members in derived classes where appropriate but to add the
Variant[string] bit to Exception to enable the passing of other data that you
might want but is not in the exception type normally as well as enable the
fancy string formatting stuff that Andrei wants. But this thread is so long and
complicated that I think that many of us are just confused.

- Jonathan M Davis


Ok, I see.

--
/Jacob Carlborg


Re: The Right Approach to Exceptions

2012-02-21 Thread Jonathan M Davis
On Wednesday, February 22, 2012 08:22:21 Jacob Carlborg wrote:
> Now I'm completely lost. According to what I've read this is thread this
> is exactly what you want to do, put the formatting inside the exceptions.

No. He wants to provide a way for an external function to generically generate 
strings according to the format that you want when you generate the string. 
So, some function would take a formatting string of some kind and then read 
the corresponding values form the Variant[string] in Exception and generate a 
string according to that format string. How exactly that works, I don't 
understand (something about a string template language), but that's the idea.

So, while you could still use toString, there would be a way to generate 
strings formatted the way that _you_ want rather than how toString would do it 
- and to do it in a generic manner.

As long as this doesn't mean using Variant[string] as the way to inject all of 
the extra data into exceptions and we still use an exception hierarchy with 
the appropriate data members in derived exceptions, then I don't really see 
that as a problem. The problem is if we then also get rid of the hierarchy 
and/or try and put all of the data is the Variant[string] and only in the 
Variant[string].

>From the sounds of it, we have _some_ agreement to have an exception hierarchy 
with data members in derived classes where appropriate but to add the 
Variant[string] bit to Exception to enable the passing of other data that you 
might want but is not in the exception type normally as well as enable the 
fancy string formatting stuff that Andrei wants. But this thread is so long and 
complicated that I think that many of us are just confused.

- Jonathan M Davis


Re: Towards a better conceptual model of exceptions (Was: Re: The Right Approach to Exceptions)

2012-02-21 Thread H. S. Teoh
On Tue, Feb 21, 2012 at 08:56:03PM +0100, deadalnix wrote:
> Le 21/02/2012 20:00, H. S. Teoh a écrit :
[...]
> >You're right, that would be unnecessary duplication, especially since
> >an unhandled Condition becomes a thrown Exception anyway, and it's a
> >very bad idea to duplicate the entire Exception hierarchy in
> >Condition.
> >
> >Only thing is, then the handler will have to downcast the Exception
> >to get to the useful info. This may lead to messy code. But it's
> >something we should investigate.
> >
> 
> Yes, I'm aware of the problem and it is real. I have no good solution
> for it now.

I have an idea. What if handlers took *two* arguments, a Condition, and
a (possibly derived) Exception object? The raise system would then match
conditions to handlers by both the Condition type and derived Exception
type. The handler then has access to the particular derived object that
it wants to handle without needing to downcast. So it sorta simulates a
catch(DerivedException).

It's a bit harder to implement, though... we'll need to store TypeInfo
for each handler and match it up with the Condition being raised. But
I'm assuming this isn't too much more than what the runtime is already
doing with Exceptions anyway, so performance-wise it should be
acceptable.

And while we're at it, I think we should just stop pretending this is
still the same system as Lisp, and just rename Condition to
RecoveryStrategy or RecoveryOpts or something along those lines, since
that's what it is.


[...]
> >To maximize usability and minimize redundancy and bloat, I'm thinking
> >we should define categories based on what recovery actions are
> >*actually available*, rather than what actions are *potentially*
> >available. So to that end, it's not really categorization per se, but
> >more of a way of describing what recovery strategies are actually
> >available.
> >
> 
> Yes indeed. Exception should provide data about what the problem IS,
> condition on what are option to recover.

Yeah, I think it makes sense to rename Condition to RecoveryStrategy or
RecoveryOpts. The recovery choices aren't a "condition"; the Exception
object is the condition, the choices are recovery strategies.


[...]
> >Each operation should have a single condition with a well-defined set
> >of recovery methods, not some arbitrary combination of multiple
> >conditions.
> >
> >What do you think?
> >
> 
> It is exactly what lead me to create template for such cases.
> Additionnaly, thoses templates should include a way to generate the
> Exception, but I ran into a strangeness of D when exprimenting with
> and did had time to come up with something working for that. The
> condition handling and the actual operation must be separated to avoid
> code duplication and ensure separation of concerns.

Yep.


> The current design allow to create new Conditions, in the lib but also
> in user code.
> 
> If we can find a way to handle properly the Exception crazy casting
> problem we have here a very nice way to handle errors.

Would it be possible to add that second argument to handlers? I think
that will solve the problem. But it will take a bit more effort to
implement.


T

-- 
"How are you doing?" "Doing what?"


Re: The Right Approach to Exceptions

2012-02-21 Thread Jacob Carlborg

On 2012-02-21 22:08, Andrei Alexandrescu wrote:

On 2/21/12 2:42 PM, Jacob Carlborg wrote:

On 2012-02-21 21:27, Andrei Alexandrescu wrote:

On 2/21/12 2:26 PM, Jacob Carlborg wrote:

As I said, it seems you want to push up implementation details specific
to a given subclass to the base class even though it shouldn't be
pushed
up.


I explained that doing so allows for proper formatting of error
messages. So it should pushed up.

Andrei


Well, I don't think that is the right approach. As many others have
explained, error messages are only a small part of exception handling.


I agree. Also, one interface function is only a small part of a class
hierarchy.


If you do want to have a generic way of getting an error message out of
an exception, what's wrong with toString? Or a new method that formats
the error messages. No need to push up the instance variables to the
base class.


This has been answered in the long thread. In brief, toString loses too
much information and putting formatting inside exceptions is not the
right place.


Andrei


Now I'm completely lost. According to what I've read this is thread this 
is exactly what you want to do, put the formatting inside the exceptions.


--
/Jacob Carlborg


Re: The Right Approach to Exceptions

2012-02-21 Thread H. S. Teoh
On Tue, Feb 21, 2012 at 07:43:32PM -0500, Jonathan M Davis wrote:
> On Tuesday, February 21, 2012 14:15:03 Andrei Alexandrescu wrote:
> > I thought I was pushing the generics angle, and OO people explained
> > it to me that that was wrong.

I've changed my mind. Now I'm trying to see if the generics angle has
some possibilities. Maybe, maybe not, but we'll never know without
experimenting with it. I think your is_transient idea can be expanded
upon.

The way exceptions are currently implemented, they only carry
information, not behaviour, as Jonathan said rightly.  The try/catch
mechanism essentially reduces to "I've hit a problem I don't know how to
solve, here's a description of it". There's no behaviour in there. The
throwing code has already given up.  It's up to the catcher to interpret
the description of the problem and figure out how to recover. To recover
well, the catcher must know the intimate details of the problem well. So
you have the situation of a specific catcher catching a specific
Exception subclass. This is not an ideal situation, because now
high-level code needs to know the specifics of low-level errors.

With your is_transient idea, though, this begins to change. Now we're no
longer just describing the problem.  When is_transient=1, it means the
thrower is suggesting that perhaps retrying would help. Of course, it's
up to the catcher whether or not to follow through with this suggestion,
but it's one step up from "here's a description of the problem, figure
out the solution yourself". But now the catcher doesn't necessarily have
to know the specifics of the low-level problem. It knows at least one
strategy that might fix the problem, regardless of what the problem is:
retry the operation. This is good, because the low-level code, which
knows the problem best, can offer a useful suggestion (retry). The
high-level code can just take the suggestion or not; it no longer needs
to know low-level details.

But why stop there? Since the low-level code knows all the dirty details
about the problem, it's in the best position to offer meaningful
recovery suggestions. It just has to communicate these possible recovery
strategies to the high-level code, and let the high-level code decide
what to do. The high-level code doesn't need to know how to implement
these strategies -- it's not in the best position to know that anyway.
It just knows, here's a list of recovery strategies, I can go ahead with
one of them, or just call it quits and unwind the stack. The low-level
code is what implements each strategy.

Of course, in order for the high-level code to meaningfully choose
between alternative strategies, the strategies themselves must be
generic concepts; otherwise we're still tying high-level code to
low-level details. So we need to identify generic categories of
exceptions for which this kind of generic recovery is meaningful --
which is what I've done in another post.

I won't repeat the details here, but I just want to say that I think
this angle merits some investigation. It allows us to factor out
exceptions which can be resolved by commonly used recovery strategies so
that we don't have to keep writing tons and tons of exception-specific
recovery code everywhere. Some specific code is still needed, no doubt,
there's always special cases that need specific handling.  But if enough
exceptions can be adequately dealt with generically, then we don't need
to write specific code for them. We can simply reuse generic recovery
solutions.


T

-- 
Real Programmers use "cat > a.out".


Re: new std.variant (was Re: The Right Approach to Exceptions)

2012-02-21 Thread Andrei Alexandrescu

On 2/21/12 6:11 PM, Robert Jacques wrote:

On Tue, 21 Feb 2012 09:12:57 -0600, Adam D. Ruppe
 wrote:


On Tuesday, 21 February 2012 at 02:33:15 UTC, Robert Jacques
wrote:

Nope. See
(https://jshare.johnshopkins.edu/rjacque2/public_html/ )



Any luck in getting the required patches into phobos?

I'd love to see this full thing in there for the next release.

It rox.


I'm in the process of cleaning up the required patches and submitting
them. The first one's already in
(https://github.com/sandford/phobos/commit/8b845d2a50bc20993afed7306f3d84921e4ac54b).
I've almost got appender ready.


Are the changes substantial enough to put them through the review process?

Andrei


Re: The Right Approach to Exceptions

2012-02-21 Thread Jonathan M Davis
On Tuesday, February 21, 2012 14:15:03 Andrei Alexandrescu wrote:
> I thought I was pushing the generics angle, and OO people explained it 
> to me that that was wrong.

You were just talking about applying OO policy to exceptions, which just 
doesn't make sense for most things, because they're just not polymorphic in 
terms of how they're handled.

> I'm sorry, I was unable to derive information from this post. It's a
> string of assertion without any backing.

Just look at exceptions. You catch them by type and then use that specific 
type. You don't generally operate on them via polymorphic functions. Derived 
classes usually hold more data but do not change the behavior of any existing 
functions. Rather, you look at the type of the exception and the values of the 
member variables in that concrete type to decide how to handle the exception 
based on that information. That's not particularly OO or polymorphic at all.

Yes, you can catch more generic exceptions instead of the concrete ones, but 
that doesn't generally mean that you end up calling virtual functions on the 
base class. Rather, it means that your code cares less about what exactly went 
wrong. You're still not likely to be calling a virtual function on the 
exception type, because exceptions carry information, not behavior.

The one major exception to this is toString. Having generic error message 
generating capabilities is useful. And not even that needs to be polymorphic. 
With Exception, the return value of toString is generated from the msg 
property which was passed in via its constructor. And toString isn't something 
that you normally override with Exception. Rather, it's the msg argument which 
changes. At minimum, if you do override toString, you need to call the base 
class' toString or you'll lose the stack trace. It's just not really designed 
with overriding in mind.

I really don't see how anyone can make much of an argument for exceptions 
being OO beyond the fact that they're objects given that handling them means 
doing the complete opposite of typical OO. With exceptions, it's how they're 
handled that changes from type to type, not their internal behavior, whereas 
OO focuses on changing the behavior of functions in derived classes. And with 
exceptions, you use the concrete types and not an abstract interface, whereas 
in OO, the idea is to use an abstract interface.

So, I don't see much point in trying to force OO principles on exceptions. 
Trying to treat them more generically with regard to generating error messages 
makes some sense, but that's about it. And that still isn't particularly OO - 
especially when the proposed solution is to use Variant[string] rather than to 
do something with toString. But if you want to change the message formatting 
at the catch point (just like all of the other exception behavior is generally 
done at the catch point), you can't do it with toString (at least, not without 
changing the internal state of the exception before calling toString). OO just 
doesn't fit.

- Jonathan M Davis


Re: new std.variant (was Re: The Right Approach to Exceptions)

2012-02-21 Thread Robert Jacques

On Tue, 21 Feb 2012 09:12:57 -0600, Adam D. Ruppe  
wrote:


On Tuesday, 21 February 2012 at 02:33:15 UTC, Robert Jacques
wrote:

Nope. See
(https://jshare.johnshopkins.edu/rjacque2/public_html/ )



Any luck in getting the required patches into phobos?

I'd love to see this full thing in there for the next release.

It rox.


I'm in the process of cleaning up the required patches and submitting them. The 
first one's already in 
(https://github.com/sandford/phobos/commit/8b845d2a50bc20993afed7306f3d84921e4ac54b).
 I've almost got appender ready.


Re: The Right Approach to Exceptions

2012-02-21 Thread Martin Nowak
On Tue, 21 Feb 2012 23:29:32 +0100, H. S. Teoh   
wrote:



On Tue, Feb 21, 2012 at 03:15:19PM -0600, Andrei Alexandrescu wrote:
[...]

A more debatable aspect of exceptions is the first-match rule in
catch blocks. All of OOP goes with best match, except here. But then
all code is together so the effect is small.


Does it make sense to make it best-match? Or is that too risky since
everyone expects it to be first-match?


Catch statements that are not in best-match order are a compile time  
error, g++ does that as a warning too.


Re: Towards a better conceptual model of exceptions (Was: Re: The Right Approach to Exceptions)

2012-02-21 Thread H. S. Teoh
On Tue, Feb 21, 2012 at 07:15:29PM +0100, Artur Skawina wrote:
> On 02/21/12 17:56, H. S. Teoh wrote:
[...]
> I don't think something like this can reliably work - handling unknown
> error conditions in code not expecting them is not a good idea.

I'm not proposing we do this for *every* error. Only for those for which
it makes sense. And the code *does* have to expect what it's handling: a
particular category of problems, a category for which it makes sense to
handle recovery in a generic way. I'm *not* proposing this:

try {...}
catch(Exception e) {// catch everything
// generic code that magically handles everything
}

If we could do that, this thread would've been over after 3 posts. :)


> After all, if the new error is of a similar nature as another one it
> could have been mapped to that one, or handled internally.

Which is what I'm trying to do with the categorization.

Internal handling, of course, can and should be done in those cases
where it's crystal clear how one should proceed. I'm addressing the case
where it can't be handled internally (else why would it throw an
exception / raise a condition in the first place?)


> Note that with my scheme the delegates can eg call another delegate
> provided in the exception from the lower level code - so things like
> that are possible. It's just that i don't think it's a good idea for
> low level code to use the exception mechanism to ask "Should I retry
> this operation?". The code either knows what to do (whether retrying
> makes sense) or could be provided with a predefined policy.

The delegate is the predefined policy. Except that it's much more
flexible than a global on/off setting. It has access to the registering
function's scope, based on which it can make decisions based on the
large-scale state of the program, which the low-level code doesn't (and
shouldn't) know about. Multiple delegates can be registered for the same
condition, and the most relevant one (the closest to the problem locus)
takes priority. Each delegate can implement its own policy on how to
deal with problems. The higher-level delegates only see problems that
the lower-level delegates decide to pass on.

In this way, lower-level delegates filter out stuff that they can handle
on their own, only deferring to the higher-level delegates when they
don't know how to proceed.  Your higher-level handler won't be handling
all sorts of trivial low-level problems, only the major issues that the
lower-level handlers can't already handle.


> If retrying occurs often during normal operation then throwing an
> exception every time is probably not the best way to handle this.

In fact, some experimental code that deadalnix & I are playing with
currently implements retry without try/catch, except when we need to
unwind the stack. So no performance hit there.


> And if it's a rare event - this kind of error handling adds too much
> overhead - programmer-wise, hence more bugs in the rarely executed
> parts of the program and probably java-style
> catch-everything-just-to-silence- -the-compiler situations, which are
> then never properly fixed...
[...]

We're currently playing with using templates to generate the boilerplate
stuff, so all you need to do is to write the template name and wrap a
block around the code to be retried. I think that's acceptable overhead
-- it's no worse than writing "try ... catch", and arguably far more
powerful.


T

-- 
Trying to define yourself is like trying to bite your own teeth. -- Alan Watts


Re: The Right Approach to Exceptions

2012-02-21 Thread deadalnix

Le 21/02/2012 22:15, Andrei Alexandrescu a écrit :

What if instead of catching by class, we catch by attribute matching?
So instead of writing:

try { ... }
catch(SomeExceptionType e) { ... }
catch(SomeOtherExceptionType e) { ... }
catch(YetAnotherSillyException e) { ... }

we write:

try { ... }
catch(e: exists!e.filename&& e.failedOp is File.open) {
// something
}
catch(e: e.is_transient&& e.num_retries< 5) {
// something else
}
// And why should we even need an exception object in the first
// place?
catch(time()>= oldtime+5000) {
// This thing's been running for way too long, time to
// do something drastic
}

Flamesuit on! ;-)


The only problem I see here is ascribing e a type.



That is why I proposed here : 
http://forum.dlang.org/thread/jhos0l$102t$1...@digitalmars.com?page=25#post-jhtus4:2416tp:241:40digitalmars.com 
some alternative syntax. To make it short :


try {
// Stuff . . .
} catch(Exception e) if(e.transient == false) {
// Handling . . .
}

For the longer explanation, see the link.


Re: The Right Approach to Exceptions

2012-02-21 Thread Ali Çehreli

On 02/18/2012 09:09 PM, Jim Hewes wrote:

> I think of exception handling as tied to contract programming.

I think your use of the word 'contract' is colliding with the contract 
programming feature. What you describe later does not match with the 
contract programming and I guess is the reason why Andrei is pointing 
out two chapters from TDPL.


I will reread those chapters later today but I think Andrei is referring 
to the distinction between assert() and std.exception.enforce().


> A
> function has a specific job that it's supposed to do. If for any reason
> it cannot do that job successfully, an exception should be thrown.

Agreed. That is how we have been using exceptions in C++ successfully at 
where I work. It makes everything simple.


> That
> can include even bad parameters

Yes, enforce() in D is great for that. I think Andrei agrees.

> (although if you have bad parameters to
> internal functions I'd think that is a design bug and could be handled
> by asserts).

Yes. Contract programming uses asserts().

> If what you mean is that exceptions should not be used to return
> information when the function is successful, I agree. But it can be used
> to return extra details about errors when a function fails.

Agreed. Again, this is how we have been using exceptions. We have the 
equivalents of detailed exception types that Jonathan M Davis has been 
mentioning. Come to think of it, less than a dozen.


> Not every
> exception coming out of a function was generated by that function. It
> may have come from several levels below that.

Of course. It is very useful.

> Maybe you can handle the
> former because it is more immediate but not the latter. So without
> exception types how would you know the difference between them? You
> could use error codes and switch on them but it defeats one of the main
> purposes of exception handling. So I think if there are exceptions
> generated from further away, it's an argument _for_ exception types
> rather than against it.

Agreed.

> For example, just have one BadParameter exception and then store
> information about which parameter is bad and why in the exception.

Agreed.

I would like to add that exceptions are thrown by code that doesn't know 
how the exception will be useful. For that reason, especially a library 
function must provide as much information as possible. Going with the 
same examples in this thread, FileException is not a good exception to 
throw when the actual problem is a FilePermissionException or a 
FileNotFoundException.


Ali



Re: The Right Approach to Exceptions

2012-02-21 Thread H. S. Teoh
On Tue, Feb 21, 2012 at 03:15:19PM -0600, Andrei Alexandrescu wrote:
[...]
> A more debatable aspect of exceptions is the first-match rule in
> catch blocks. All of OOP goes with best match, except here. But then
> all code is together so the effect is small.

Does it make sense to make it best-match? Or is that too risky since
everyone expects it to be first-match?


[...]
> >So I'm going to throw (har har) this very crazy and wild idea out
> >there and let's see if it's viable:
> >
> >What if instead of catching by class, we catch by attribute matching?
> >So instead of writing:
> >
> > try { ... }
> > catch(SomeExceptionType e) { ... }
> > catch(SomeOtherExceptionType e) { ... }
> > catch(YetAnotherSillyException e) { ... }
> >
> >we write:
> >
> > try { ... }
> > catch(e: exists!e.filename&&  e.failedOp is File.open) {
> > // something
> > }
> > catch(e: e.is_transient&&  e.num_retries<  5) {
> > // something else
> > }
> > // And why should we even need an exception object in the first
> > // place?
> > catch(time()>= oldtime+5000) {
> > // This thing's been running for way too long, time to
> > // do something drastic
> > }
> >
> >Flamesuit on! ;-)
> 
> The only problem I see here is ascribing e a type.
[...]

True, for this to work in its full generality would require duck-typing
(the catch block can use any property it tests for, regardless of type).
Which D doesn't have.

So it looks like we're back to catch conditions, that has come up a few
times in this thread:

try { ... }
catch(Exception e: e.is_transient && ... || ...) {
// or whatever the latest proposed syntax is, the idea
// is the same.
}
catch(Exception e: !e.is_transient && ...) {
// the previous block doesn't catch if conditions fail,
// so we can still get Exception here.
}

This does start to look like it might make sense to switch to best-match
instead of first-match for catch clauses.


T

-- 
Your inconsistency is the only consistent thing about you! -- KD


Re: Towards a better conceptual model of exceptions (Was: Re: The Right Approach to Exceptions)

2012-02-21 Thread H. S. Teoh
On Tue, Feb 21, 2012 at 02:40:30PM -0500, Jonathan M Davis wrote:
> On Tuesday, February 21, 2012 00:15:48 H. S. Teoh wrote:
> > TRANSITIVITY
> 
> I still contend that this useless, because you need to know what went wrong 
> to 
> know whether you actually want to retry anything. And just because the 
> particular operation that threw could be retried again doesn't mean that the 
> code that catches the exception can retry the function that _it_ called which 
> resulted in an exception somewhere down the call stack. And often, you don't 
> even know which function it was that was called within the try block. So, I 
> don't see how transivity really matters. If you know what what wrong - which 
> the type of the exception will tell you - then _that_ is what helps your code 
> make a useful decision as to what to do, not a transivity property.

The point of my little exercise was not to try to solve *every* case of
exception handling, but to isolate those cases for which generic
handling *does* make sense. I don't pretend that my categories cover
*every* case. They obviously don't, as you point out.  For those cases
where it doesn't make sense, you still catch the specific exception and
do your specific recovery, as before.

What I'm trying to do is to evaluate the possibility/feasibility of an
error recovery system where you *don't* have to know what the specific
error is, in order to do something useful with it. What are the errors
that *can* be recovered this way, if there are such errors.

This is why transitivity is useful, because it allows you to not have to
worry about what the specific problem is, and yet still be able to do
something meaningful, because the problem doesn't change in nature as
you move up the call stack. Obviously, non-transitive errors have to be
handled on a case-by-case basis; I'm not negating that at all.

The importance of transitivity to generic handling can be seen as
follows:

Suppose X calls Y and Y calls Z. Z encounters a problem of some sort,
let's say for instance it's an input error. So from Y's point of view,
the problem can be corrected if it passes different input to Z.  But
from X's point of view, this is not necessarily true: X's input to Y may
have nothing to do with Y's input to Z. So trying to generically handle
Z's problem at X's level makes no sense. The problem is not transitive,
so no generic handling is possible. You have to catch by type and
recover by type. End of story.

But suppose Z encounters a transitive problem. Say for instance it's a
component failure: one of the functions that Z calls has failed. Well,
by extension, that also means Z itself has failed. Now, from Y's point
of view, it can attempt to recover by calling W in lieu of Z, if there
exists a W that performs an equivalent function to Z. But since the
problem is transitive, we can go up to X's level. From X's point of
view, it knows nothing about Z, but it *does* know that Y has
encountered a component failure (since component failure is transitive).
Viewed from X's perspective, Y is the problem (it doesn't know about Z).
So if there's an alternative to Y that performs an equivalent function,
X can call that in lieu of Y.

So you see, even though X has absolute no idea what kind of problem Z
encountered, or even that such a thing as Z exists, it *can* make a
meaningful effort to recover from the problem that Z encountered. That's
what I'm trying to get at: generic handling.

Now, to avoid misunderstandings, I'm *not* saying that at every level of
the call stack there needs to be a recovery mechanism, such that Y has
an alternative to Z and X has an alternative to Y, and so on, all the
way up the stack. That would be foolish, since you'll be wasting time
writing alternative versions of everything.

How this approach benefits real-life programs is that you can insert
recovery mechanisms at strategic points in the call chain, so that when
problems occur lower down the stack, you can handle it at those points
without needing to unwind the stack all the way to the top. Obviously,
the try-catch mechanism already does this; but the difference is that
those strategic points may not be high enough up the call chain to be
able to make a decision about *which* recovery approach to take. They
know how to implement the recovery, but they don't know if they should
try, or just give up. So this is here is where the high-level delegates
come in. *They* know how to decide whether to attempt recovery or just
give up, but they *don't* know how to implement recovery, because the
lower-level code is a black box to them.

By tying the two together via the "Lispian system", you have the
possibility of making decisions high up the call stack, and yet still be
able to effect low-level recovery strategies.

To go back to the earlier example: if Z fails, then from X's point of
view Y has also failed, since it doesn't know what Z is. However, if X
registers a component failure handler and then calls Y, and Y is capa

Re: The Right Approach to Exceptions

2012-02-21 Thread Andrei Alexandrescu

On 2/21/12 2:47 PM, H. S. Teoh wrote:

On Tue, Feb 21, 2012 at 09:32:35PM +0100, deadalnix wrote:
[...]

So it doesn't help. Dulb subclasses of Exceptions are done mostly to
be able to catch them. To avoid useless subclasses, we need a more
precise way to catch Exception than the type only.

[...]

This is a good point.

Has anybody even considered, *why* does catch match by type? Is there a
good reason for that? Or is it just inherited from the rah rah days of
OOP where "everything is an object", so since exception is part of
everything, exception is an object, therefore we can just catch by
object type?


I think a reason is that exceptions should be able to transport an 
arbitrary amount of information. That means heterogeneous types at 
least. Then, as you discussed in the beginning of the thread, placing 
types in a hierarchy allows client code to catch several exceptions 
sharing a common super type, theory that was well understood.


A more debatable aspect of exceptions is the first-match rule in catch 
blocks. All of OOP goes with best match, except here. But then all code 
is together so the effect is small.



From all the heated debate in this thread, it's clear that exceptions

don't map very well to a class hierarchy, at least not without points of
contention and what amounts to workarounds and hacks.

So I'm going to throw (har har) this very crazy and wild idea out there
and let's see if it's viable:

What if instead of catching by class, we catch by attribute matching?
So instead of writing:

try { ... }
catch(SomeExceptionType e) { ... }
catch(SomeOtherExceptionType e) { ... }
catch(YetAnotherSillyException e) { ... }

we write:

try { ... }
catch(e: exists!e.filename&&  e.failedOp is File.open) {
// something
}
catch(e: e.is_transient&&  e.num_retries<  5) {
// something else
}
// And why should we even need an exception object in the first
// place?
catch(time()>= oldtime+5000) {
// This thing's been running for way too long, time to
// do something drastic
}

Flamesuit on! ;-)


The only problem I see here is ascribing e a type.


Andrei


Re: The Right Approach to Exceptions

2012-02-21 Thread Andrei Alexandrescu

On 2/21/12 2:42 PM, Jacob Carlborg wrote:

On 2012-02-21 21:27, Andrei Alexandrescu wrote:

On 2/21/12 2:26 PM, Jacob Carlborg wrote:

As I said, it seems you want to push up implementation details specific
to a given subclass to the base class even though it shouldn't be pushed
up.


I explained that doing so allows for proper formatting of error
messages. So it should pushed up.

Andrei


Well, I don't think that is the right approach. As many others have
explained, error messages are only a small part of exception handling.


I agree. Also, one interface function is only a small part of a class 
hierarchy.



If you do want to have a generic way of getting an error message out of
an exception, what's wrong with toString? Or a new method that formats
the error messages. No need to push up the instance variables to the
base class.


This has been answered in the long thread. In brief, toString loses too 
much information and putting formatting inside exceptions is not the 
right place.



Andrei




Re: The Right Approach to Exceptions

2012-02-21 Thread deadalnix

Le 21/02/2012 01:38, Andrei Alexandrescu a écrit :

On 2/20/12 6:25 PM, H. S. Teoh wrote:

On Mon, Feb 20, 2012 at 05:15:17PM -0600, Andrei Alexandrescu wrote:
Formatting should use class reflection. We already discussed that, and
we already agreed that was the superior approach.


Jose's argument convinced me otherwise. I retract my agreement.


When you're catching a specific exception, you're catching it with the
view that it will contain precisely information X, Y, Z that you need to
recover from the problem. If you don't need to catch something, then
don't put the catch block there.


That's extremely rare in my experience, and only present in toy examples
that contain a ton of "..." magic.



I think you experience here is biased by C++ .


Re: The Right Approach to Exceptions

2012-02-21 Thread H. S. Teoh
On Tue, Feb 21, 2012 at 09:32:35PM +0100, deadalnix wrote:
[...]
> So it doesn't help. Dulb subclasses of Exceptions are done mostly to
> be able to catch them. To avoid useless subclasses, we need a more
> precise way to catch Exception than the type only.
[...]

This is a good point.

Has anybody even considered, *why* does catch match by type? Is there a
good reason for that? Or is it just inherited from the rah rah days of
OOP where "everything is an object", so since exception is part of
everything, exception is an object, therefore we can just catch by
object type?

>From all the heated debate in this thread, it's clear that exceptions
don't map very well to a class hierarchy, at least not without points of
contention and what amounts to workarounds and hacks.

So I'm going to throw (har har) this very crazy and wild idea out there
and let's see if it's viable:

What if instead of catching by class, we catch by attribute matching?
So instead of writing:

try { ... }
catch(SomeExceptionType e) { ... }
catch(SomeOtherExceptionType e) { ... }
catch(YetAnotherSillyException e) { ... }

we write:

try { ... }
catch(e: exists!e.filename && e.failedOp is File.open) {
// something
}
catch(e: e.is_transient && e.num_retries < 5) {
// something else
}
// And why should we even need an exception object in the first
// place?
catch(time() >= oldtime+5000) {
// This thing's been running for way too long, time to
// do something drastic
}

Flamesuit on! ;-)


T

-- 
Famous last words: I *think* this will work...


Re: The Right Approach to Exceptions

2012-02-21 Thread Jacob Carlborg

On 2012-02-21 21:27, Andrei Alexandrescu wrote:

On 2/21/12 2:26 PM, Jacob Carlborg wrote:

As I said, it seems you want to push up implementation details specific
to a given subclass to the base class even though it shouldn't be pushed
up.


I explained that doing so allows for proper formatting of error
messages. So it should pushed up.

Andrei


Well, I don't think that is the right approach. As many others have 
explained, error messages are only a small part of exception handling.


If you do want to have a generic way of getting an error message out of 
an exception, what's wrong with toString? Or a new method that formats 
the error messages. No need to push up the instance variables to the 
base class.


--
/Jacob Carlborg


Re: The Right Approach to Exceptions

2012-02-21 Thread deadalnix

Le 21/02/2012 00:23, Andrei Alexandrescu a écrit :

On 2/20/12 4:44 PM, Juan Manuel Cabo wrote:

HAhaha, it sometimes feel as though people are afraid that the
Variant[string]
idea is to never use plain old variables and never use exception
subclasses. :-)

On the contrary, the idea is so that plain old variables and exception
subclasses
can be created for the right reasons, and to remove cases where they need
to be created for the wrong reasons.


Yah, I think there's a lot of confusion and consequent apprehension
regarding this. Thanks for attempting to clarify things.

Andrei



So it doesn't help. Dulb subclasses of Exceptions are done mostly to be 
able to catch them. To avoid useless subclasses, we need a more precise 
way to catch Exception than the type only.


This Variant[string] doesn't help.


Re: The Right Approach to Exceptions

2012-02-21 Thread Andrei Alexandrescu

On 2/21/12 2:26 PM, Jacob Carlborg wrote:

As I said, it seems you want to push up implementation details specific
to a given subclass to the base class even though it shouldn't be pushed
up.


I explained that doing so allows for proper formatting of error 
messages. So it should pushed up.


Andrei


Re: The Right Approach to Exceptions

2012-02-21 Thread Jacob Carlborg

On 2012-02-21 21:06, Andrei Alexandrescu wrote:

On 2/21/12 12:03 PM, Jacob Carlborg wrote:

On 2012-02-21 17:57, Andrei Alexandrescu wrote:

On 2/21/12 10:50 AM, Juan Manuel Cabo wrote:

I thought that an alternative to Variant[string] would be to have some
virtual
functions overrideable (getExceptionData(string dataName) or
something).
but they would all have to return Object or Variant, so it's the same
thing.


Exactly. By and large, I think in the fire of the debate too many people
in this thread have forgotten to apply a simple OO design principle:
push policy up and implementation down. Any good primitive pushed up the
exception hierarchy is a huge win, and any design that advocates
reliance on concrete types is admitting defeat.

Andrei


That because you can't (shouldn't) push up implementations specific to a
given subclass. Why don't we only have one class, Object, and add a
Variant[string] there.

Do you see how stupid that is.


I think I do. It's also fair to ask you if you are sure you understood
my point.

Andrei


As I said, it seems you want to push up implementation details specific 
to a given subclass to the base class even though it shouldn't be pushed up.


--
/Jacob Carlborg


Re: The Right Approach to Exceptions

2012-02-21 Thread Andrei Alexandrescu

On 2/21/12 1:17 PM, Jonathan M Davis wrote:

On Tuesday, February 21, 2012 10:57:20 Andrei Alexandrescu wrote:

On 2/21/12 10:50 AM, Juan Manuel Cabo wrote:

I thought that an alternative to Variant[string] would be to have some
virtual functions overrideable (getExceptionData(string dataName) or
something). but they would all have to return Object or Variant, so it's
the same thing.

Exactly. By and large, I think in the fire of the debate too many people
in this thread have forgotten to apply a simple OO design principle:
push policy up and implementation down. Any good primitive pushed up the
exception hierarchy is a huge win, and any design that advocates
reliance on concrete types is admitting defeat.


Exceptions do _not_ lend themselves to polymorphism. Having them in a type
hierarchy is useful. It allows you to deal with them at varying levels of
abstractions. But ultimately, you deal with the concrete types, _not_ an
abstract interface. In that sense, they're not OO _at all_.


Well this is just a series of assertions that conveys no information.


Adding a Variant[string] property to allow adding on additional information if
a particular application finds it useful may be a good thing to do. But it
should be an _add on_, not the core design.


Again, just an assertion.


Aside from printing strings,
trying to deal with exceptions generically just does not make sense.


Assertion.


At best,
you might care about a common exception rather than a more specific one in
particular case (e.g. catching IOException rather than FileException). But if
you're trying to actually handle the exception in any real way rather than
just print out a message, you need the concrete type, not an abstract
interface.


Assertion.


I think that you're pushing the OO angle too hard onto exceptions.


I thought I was pushing the generics angle, and OO people explained it 
to me that that was wrong.



They're not
completely separated from it, but they really aren't classic OO and shouldn't
be treated as such. If anything, they're inverted, because you frequently try
and deal with as concrete a type as possible rather than as abstract a type as
possible. The hierarchy aspect is really the only truly OO aspect of
exceptions IMHO. For the most part, polymorphism just doesn't enter into it.
And Exception really already declares the few functions where it does.


I'm sorry, I was unable to derive information from this post. It's a 
string of assertion without any backing.



Andrei


Re: The Right Approach to Exceptions

2012-02-21 Thread Andrei Alexandrescu

On 2/21/12 12:03 PM, Jacob Carlborg wrote:

On 2012-02-21 17:57, Andrei Alexandrescu wrote:

On 2/21/12 10:50 AM, Juan Manuel Cabo wrote:

I thought that an alternative to Variant[string] would be to have some
virtual
functions overrideable (getExceptionData(string dataName) or something).
but they would all have to return Object or Variant, so it's the same
thing.


Exactly. By and large, I think in the fire of the debate too many people
in this thread have forgotten to apply a simple OO design principle:
push policy up and implementation down. Any good primitive pushed up the
exception hierarchy is a huge win, and any design that advocates
reliance on concrete types is admitting defeat.

Andrei


That because you can't (shouldn't) push up implementations specific to a
given subclass. Why don't we only have one class, Object, and add a
Variant[string] there.

Do you see how stupid that is.


I think I do. It's also fair to ask you if you are sure you understood 
my point.


Andrei




Re: The Right Approach to Exceptions

2012-02-21 Thread Jacob Carlborg

On 2012-02-21 19:33, Juan Manuel Cabo wrote:

That because you can't (shouldn't) push up implementations specific to a given 
subclass. Why don't we only have one
class, Object, and add a Variant[string] there.

Do you see how stupid that is.


As stupid as any database API which returns result items as Variant[string] or
string[string], but it works. (the sad part is that one has to rely a bit on
convention, but convention can be standardized (string constants) and measures
taken when deviated so that it is done gracefuly).


That's because we are limited by the database API. If you created a new 
database from scratch, completely written in D, perhaps even object 
oriented, you could return the correct object form the beginning.



Do you have an alternative solution that allows to extend an exception
object with extra information, while keeping it the same class?


No, but that's what subclasses are used for.


So if one removes the bad reasons to create new Exception types, then the
ones that DO get created are solid, standard, reusable, and can withstand
the test of time. Because they would be open for extension but closed for
source code modification.


--
/Jacob Carlborg


Re: Towards a better conceptual model of exceptions (Was: Re: The Right Approach to Exceptions)

2012-02-21 Thread Jacob Carlborg

On 2012-02-21 09:15, H. S. Teoh wrote:

All of this heated debate has led me to reconsider our whole concept of
exceptions. It seems that we're squabbling over little details in
existing paradigms. But what of the big picture? What *is* an exception
anyway? We all know the textbook definition, but clearly something is
missing since we can't seem to agree how it should be implemented.

 ...


I think I like the idea in general.

--
/Jacob Carlborg


Re: Towards a better conceptual model of exceptions (Was: Re: The Right Approach to Exceptions)

2012-02-21 Thread deadalnix

Le 21/02/2012 20:00, H. S. Teoh a écrit :

On Tue, Feb 21, 2012 at 06:24:01PM +0100, deadalnix wrote:

Le 21/02/2012 18:10, H. S. Teoh a écrit :

[...]

True, and there's nothing to stop you from digging into the details
of the raised Condition if you want to. I did consider implementing
Conditions as some kind of class hierarchy, so that the generic
categories are at the top, underneath Condition, then actual specific
Conditions can extend them. If your handler knows of a specific
Condition, then you can access any pertinent additional info, and
make decisions based on that.

But what I wanted to know was, can you still do something meaningful
even if you knew nothing beyond the top-level generic categories of
Conditions? That way, your handler will still work with new
Conditions that you've never seen before.

[...]

And here come an idea of mine :

If the Condition has a way to provide the Exception associated. So you
can get the hierarchy from the Exception, and you don't need to creat
two hierachy of classes just for the bloat.


You're right, that would be unnecessary duplication, especially since an
unhandled Condition becomes a thrown Exception anyway, and it's a very
bad idea to duplicate the entire Exception hierarchy in Condition.

Only thing is, then the handler will have to downcast the Exception to
get to the useful info. This may lead to messy code. But it's something
we should investigate.



Yes, I'm aware of the problem and it is real. I have no good solution 
for it now.





You'll fond attached some sketching code. What do you think of it ?


Is this the same code you attached in an earlier post? I did look over
it, sorry, didn't have time to respond earlier. I like the idea of
wrapping retryable code in the runTransient template, that way we
minimize the amount of boilerplate needed to actually use this feature.

Oh wait, you've added some new stuff. Let's see...

Hmm, I like the idea of providing default handlers for some
commonly-encountered situations. Reduces the amount of boilerplate.  And
there's always the option of manually defining a handler if you need to.
+1.


I was considering more last night how to implement this system. I think
I change my mind about having a common Condition base class. The whole
idea is that every major category would be defined by what kinds of
actions are available, so they are quite distinct from each other. I
don't want to reinvent another hierarchy to represent problems; we
already have an Exception hierarchy for that. So the different
Conditions need to be qualitatively different.

To maximize usability and minimize redundancy and bloat, I'm thinking we
should define categories based on what recovery actions are *actually
available*, rather than what actions are *potentially* available. So to
that end, it's not really categorization per se, but more of a way of
describing what recovery strategies are actually available.



Yes indeed. Exception should provide data about what the problem IS, 
condition on what are option to recover.



In other words, an input error where you can recover by skipping the bad
data is qualitatively different from an input error where skipping
doesn't fix the problem. These two should be treated as distinct
Conditions. Basically, I want to guarantee that for some Condition c,
action A will *always* be available to the handler. Then the handler
won't need excessive checking (if A1 is available but A2 is not, then
use A1; else if A1 is not available but A2 is available, ... etc.). It
can count on all options being actually present.



Agreed.


Back to your code. We can implement this idea by defining a template for
each of the possible conditions. So code in runTransient always raises a
transient condition if it fails, runReroutable always raises a
reroutable condition if it fails (reroutable = failing component X can
be replaced by component Y). I don't know if this is too inflexible, but
trying to mix multiple conditions into a single operation seems to turn
the code into spaghetti:

int x, y, z;
retry1:
doSomething(x, y, z);
retry2:
if (problem1)
raise(condition1);
else if (problem2)
raise(condition2);
...
handleCondition(condition1):
if (recoveryAction1) {
fiddleWith(x);
goto retry1;
} else if (recoveryAction2) {
doSomethingElse(x,y,z);
goto retry2;
}
handleCondition(condition2):
if (recoveryAction3) {
fiddleWith(y);
goto retry1;
} else if (recoveryAction4) {
fiddleWith(z);
doSomethingElse(x,y,z);
goto retry2;
}

So I don't think this is the right way to go.

Re: The Right Approach to Exceptions

2012-02-21 Thread Jonathan M Davis
On Tuesday, February 21, 2012 08:15:29 Andrei Alexandrescu wrote:
> On 2/21/12 4:40 AM, Vincent wrote:
> > On Saturday, 18 February 2012 at 18:52:05 UTC, Andrei Alexandrescu wrote:
> >> From experience I humbly submit that catching by type is most of the
> >> time useless.
> > 
> > Completely disagree. Types allow to control place for "catch". Say, some
> > deeply nested function catches its own exceptions, while outer function
> > catches the rest - exceptions higher in hierarchy. But to have benefit
> > you have to create exceptions hierarchy - this is the main point.
> 
> As the next hundreds of messages discuss, matters are not all that
> simple :o).

It probably should be though. I think that there's a lot of overthinking going 
on here.

We need to have our exceptions organized into a hierarchy so that you can 
catch them based on what went wrong. That is the main thing that we're missing 
IMHO.

Adding extra capabilities along side that is fine as long as they make sense. 
Adding a Variant[string] property for the purpose of putting non-standard 
information in your exceptions which don't merit a new exception type makes 
some sense. But if it makes sense to catch an exception based on that 
information, then it probably merits a new type. Adding improved string 
formatting cabilities also makes some sense.

But I don't think that we need to drastically overhaul how exceptions work, 
and a lot of this discussion is straying into left field.

- Jonathan M Davis


Re: Towards a better conceptual model of exceptions (Was: Re: The Right Approach to Exceptions)

2012-02-21 Thread Jonathan M Davis
On Tuesday, February 21, 2012 00:15:48 H. S. Teoh wrote:
> TRANSITIVITY

I still contend that this useless, because you need to know what went wrong to 
know whether you actually want to retry anything. And just because the 
particular operation that threw could be retried again doesn't mean that the 
code that catches the exception can retry the function that _it_ called which 
resulted in an exception somewhere down the call stack. And often, you don't 
even know which function it was that was called within the try block. So, I 
don't see how transivity really matters. If you know what what wrong - which 
the type of the exception will tell you - then _that_ is what helps your code 
make a useful decision as to what to do, not a transivity property.

So, yes. A particular exception type is generally transitive or not, but you 
know that by the nature of the type and the problem that it represents. I 
don't see how focusing on transivity is useful.

> The try-catch mechanism is not adequate to implement all the recovery
> actions described above. As I've said before when discussing what I
> called the Lispian model, some of these recovery actions need to happen
> *in the context where the exception was thrown*. Once the stack unwinds,
> it may not be possible to recover anymore, because the execution context
> of the original code is gone.

> This is where the Lispian model really shines. To summarize:

try-catch and Exceptions are built into the language. Exceptions are part of 
not only the standard library but the runtime. They're not perfect, but they 
_work_. I really think that this whole thing is being way overthought and 
blown out of proportion.

Using another recovery mechanism in your programs (built on top of exceptions 
or otherwise) is fine, but I really don't see a need to seriously alter how 
error handling is done in the language as a whole. We're _way_ past the point 
where it makes sense to completely redesign how exceptions work. And I 
_really_ don't think that it's a good idea to change anything major about how 
error handling is done in the standard library without a lot of real world 
experience with whatever you want to replace it with. And we don't have that.

So, I see no problem with you experimenting, but I don't think that we should 
be drastically changing how the standard library functions with regards to 
exceptions. We would definitely gain something by cleaning up how they're 
organized, and maybe adding additional capabilites to improve their printing 
abilities like Andrei wants to do would be of some value, but we don't need a 
complete redesign, just some tweaks.

- Jonathan M Davis


Re: The Right Approach to Exceptions

2012-02-21 Thread Jonathan M Davis
On Tuesday, February 21, 2012 10:57:20 Andrei Alexandrescu wrote:
> On 2/21/12 10:50 AM, Juan Manuel Cabo wrote:
> > I thought that an alternative to Variant[string] would be to have some
> > virtual functions overrideable (getExceptionData(string dataName) or
> > something). but they would all have to return Object or Variant, so it's
> > the same thing.
> Exactly. By and large, I think in the fire of the debate too many people
> in this thread have forgotten to apply a simple OO design principle:
> push policy up and implementation down. Any good primitive pushed up the
> exception hierarchy is a huge win, and any design that advocates
> reliance on concrete types is admitting defeat.

Exceptions do _not_ lend themselves to polymorphism. Having them in a type 
hierarchy is useful. It allows you to deal with them at varying levels of 
abstractions. But ultimately, you deal with the concrete types, _not_ an 
abstract interface. In that sense, they're not OO _at all_.

Adding a Variant[string] property to allow adding on additional information if 
a particular application finds it useful may be a good thing to do. But it 
should be an _add on_, not the core design. Aside from printing strings, 
trying to deal with exceptions generically just does not make sense. At best, 
you might care about a common exception rather than a more specific one in 
particular case (e.g. catching IOException rather than FileException). But if 
you're trying to actually handle the exception in any real way rather than 
just print out a message, you need the concrete type, not an abstract 
interface.

I think that you're pushing the OO angle too hard onto exceptions. They're not 
completely separated from it, but they really aren't classic OO and shouldn't 
be treated as such. If anything, they're inverted, because you frequently try 
and deal with as concrete a type as possible rather than as abstract a type as 
possible. The hierarchy aspect is really the only truly OO aspect of 
exceptions IMHO. For the most part, polymorphism just doesn't enter into it. 
And Exception really already declares the few functions where it does.

- Jonathan M Davis


Re: The Right Approach to Exceptions

2012-02-21 Thread H. S. Teoh
On Tue, Feb 21, 2012 at 07:57:37PM +0100, deadalnix wrote:
> Le 21/02/2012 03:33, Robert Jacques a écrit :
[...]
> >Aren't __traits and opDispatch fun?
> 
> opDispatch is nice, but rather incomplete. It doesn't handle template
> methods for example.

Does RTTI handle template methods?


T

-- 
Answer: Because it breaks the logical sequence of discussion. / Question: Why 
is top posting bad?


Re: Towards a better conceptual model of exceptions (Was: Re: The Right Approach to Exceptions)

2012-02-21 Thread H. S. Teoh
On Tue, Feb 21, 2012 at 06:24:01PM +0100, deadalnix wrote:
> Le 21/02/2012 18:10, H. S. Teoh a écrit :
[...]
> >True, and there's nothing to stop you from digging into the details
> >of the raised Condition if you want to. I did consider implementing
> >Conditions as some kind of class hierarchy, so that the generic
> >categories are at the top, underneath Condition, then actual specific
> >Conditions can extend them. If your handler knows of a specific
> >Condition, then you can access any pertinent additional info, and
> >make decisions based on that.
> >
> >But what I wanted to know was, can you still do something meaningful
> >even if you knew nothing beyond the top-level generic categories of
> >Conditions? That way, your handler will still work with new
> >Conditions that you've never seen before.
[...]
> And here come an idea of mine :
> 
> If the Condition has a way to provide the Exception associated. So you
> can get the hierarchy from the Exception, and you don't need to creat
> two hierachy of classes just for the bloat.

You're right, that would be unnecessary duplication, especially since an
unhandled Condition becomes a thrown Exception anyway, and it's a very
bad idea to duplicate the entire Exception hierarchy in Condition.

Only thing is, then the handler will have to downcast the Exception to
get to the useful info. This may lead to messy code. But it's something
we should investigate.


> You'll fond attached some sketching code. What do you think of it ?

Is this the same code you attached in an earlier post? I did look over
it, sorry, didn't have time to respond earlier. I like the idea of
wrapping retryable code in the runTransient template, that way we
minimize the amount of boilerplate needed to actually use this feature.

Oh wait, you've added some new stuff. Let's see...

Hmm, I like the idea of providing default handlers for some
commonly-encountered situations. Reduces the amount of boilerplate.  And
there's always the option of manually defining a handler if you need to.
+1.


I was considering more last night how to implement this system. I think
I change my mind about having a common Condition base class. The whole
idea is that every major category would be defined by what kinds of
actions are available, so they are quite distinct from each other. I
don't want to reinvent another hierarchy to represent problems; we
already have an Exception hierarchy for that. So the different
Conditions need to be qualitatively different.

To maximize usability and minimize redundancy and bloat, I'm thinking we
should define categories based on what recovery actions are *actually
available*, rather than what actions are *potentially* available. So to
that end, it's not really categorization per se, but more of a way of
describing what recovery strategies are actually available.

In other words, an input error where you can recover by skipping the bad
data is qualitatively different from an input error where skipping
doesn't fix the problem. These two should be treated as distinct
Conditions. Basically, I want to guarantee that for some Condition c,
action A will *always* be available to the handler. Then the handler
won't need excessive checking (if A1 is available but A2 is not, then
use A1; else if A1 is not available but A2 is available, ... etc.). It
can count on all options being actually present.

Back to your code. We can implement this idea by defining a template for
each of the possible conditions. So code in runTransient always raises a
transient condition if it fails, runReroutable always raises a
reroutable condition if it fails (reroutable = failing component X can
be replaced by component Y). I don't know if this is too inflexible, but
trying to mix multiple conditions into a single operation seems to turn
the code into spaghetti:

int x, y, z;
retry1:
doSomething(x, y, z);
retry2:
if (problem1)
raise(condition1);
else if (problem2)
raise(condition2);
...
handleCondition(condition1):
if (recoveryAction1) {
fiddleWith(x);
goto retry1;
} else if (recoveryAction2) {
doSomethingElse(x,y,z);
goto retry2;
}
handleCondition(condition2):
if (recoveryAction3) {
fiddleWith(y);
goto retry1;
} else if (recoveryAction4) {
fiddleWith(z);
doSomethingElse(x,y,z);
goto retry2;
}

So I don't think this is the right way to go. Each operation should have
a single condition with a well-defined set of recovery methods, not some
arbitrary combination of multiple conditions.

What do you think?


T

-- 
The day Microsoft makes something that doe

Re: The Right Approach to Exceptions

2012-02-21 Thread deadalnix

Le 21/02/2012 03:33, Robert Jacques a écrit :

On Mon, 20 Feb 2012 15:21:56 -0600, H. S. Teoh 
wrote:

On Mon, Feb 20, 2012 at 02:57:08PM -0600, Andrei Alexandrescu wrote:

On 2/20/12 1:45 PM, Jonathan M Davis wrote:
>On Monday, February 20, 2012 20:42:28 deadalnix wrote:
>>Le 20/02/2012 20:27, Jonathan M Davis a écrit :
>>>On Monday, February 20, 2012 11:15:08 H. S. Teoh wrote:
That's why I proposed to use runtime reflection to scan the
exception
object for applicable fields. Then you get the best of both
worlds: the
message formatter doesn't need to know what the fields are, and
you get
full compile-time type checking for catching code that directly
accesses
the fields.
>>>
>>>That would certainly be better.
>>>
>>>- Jonathan M Davis
>>
>>This is way better than Variant[string], but unimplemented ATM.
>
>Yes, but you can use compile-time constructs to generate it. And as you
>pointed out in another post, tupleof should do the trick.
Regardless, the
>point is that using reflection of some kind is a much better
solution than
>using variant.

Agreed. Ideally adding a sort of mixin to any class would enable it
for advanced run-time information:

class DRoxException : Exception
{
mixin(enableRTTI);
... normal implementation ...
}

[...]

Doesn't this need compiler/language support?


Nope. See (https://jshare.johnshopkins.edu/rjacque2/public_html/ )

Variant e = new MyException();
writeln( e.filename, e.line, e.column);

Aren't __traits and opDispatch fun?


opDispatch is nice, but rather incomplete. It doesn't handle template 
methods for example.


Re: The Right Approach to Exceptions

2012-02-21 Thread Juan Manuel Cabo
> That because you can't (shouldn't) push up implementations specific to a 
> given subclass. Why don't we only have one
> class, Object, and add a Variant[string] there.
>
> Do you see how stupid that is.

As stupid as any database API which returns result items as Variant[string] or
string[string], but it works. (the sad part is that one has to rely a bit on
convention, but convention can be standardized (string constants) and measures
taken when deviated so that it is done gracefuly).

Do you have an alternative solution that allows to extend an exception
object with extra information, while keeping it the same class?

So if one removes the bad reasons to create new Exception types, then the
ones that DO get created are solid, standard, reusable, and can withstand
the test of time. Because they would be open for extension but closed for
source code modification.

--jm


On 02/21/2012 03:03 PM, Jacob Carlborg wrote:
> On 2012-02-21 17:57, Andrei Alexandrescu wrote:
>> On 2/21/12 10:50 AM, Juan Manuel Cabo wrote:
>>> I thought that an alternative to Variant[string] would be to have some
>>> virtual
>>> functions overrideable (getExceptionData(string dataName) or something).
>>> but they would all have to return Object or Variant, so it's the same
>>> thing.
>>
>> Exactly. By and large, I think in the fire of the debate too many people
>> in this thread have forgotten to apply a simple OO design principle:
>> push policy up and implementation down. Any good primitive pushed up the
>> exception hierarchy is a huge win, and any design that advocates
>> reliance on concrete types is admitting defeat.
>>
>> Andrei
> 
> That because you can't (shouldn't) push up implementations specific to a 
> given subclass. Why don't we only have one
> class, Object, and add a Variant[string] there.
> 
> Do you see how stupid that is.
> 



  1   2   3   4   5   6   >