Comment on PEP-0322: Reverse Iteration Methods

D

David Eppstein

"Raymond Hettinger said:
[Raymond]
[Steve Holden]
Hmm. My little brain is having difficulty imagining anything that won't.
What am I missing?

The PEP proposes adding a method only to a few objects
that don't have degenerate cases: list, str, unicode, xrange
and possibly tuple.

Speaking of which: str, int, etc., used to be functions, but they got
changed to be the initializers of types having those names. Why is
xrange still a function rather than the initializer of the xrange type?

The most visible difference of changing xrange to be the type of the
xrange objects would be more information shown in help(xrange), some of
which could be useful if we are talking about adding more methods to the
xrange objects.
 
S

Stephen Horne

Speaking of which: str, int, etc., used to be functions, but they got
changed to be the initializers of types having those names. Why is
xrange still a function rather than the initializer of the xrange type?

"""
Python 2.3 (#46, Jul 29 2003, 18:54:32) [MSC v.1200 32 bit (Intel)] on
win32
Type "help", "copyright", "credits" or "license" for more information.<type 'xrange'>
"""

Looks like a type rather than a function to me, and the help appears
to be very good. Enumerate also seems to be a type with an
initialiser.

Are you using an earlier version?
 
A

Alex Martelli

David Abrahams wrote:
...
I don't see why not.

The party line (not saying I necessarily agree, mind you -- just relating
it) is that tuples are meant as semantically heterogeneous containers
(semantically in that the items may happen to be the same type, but
their _meaning_ is disparate: e.g., consider the tuples used in modules
time or stat). So it doesn't really make sense to iterate on them, either;
it just happens to be possible because their items are accessed with []
with progressive naturals, and they have a len(). Associating _names_
to the indices makes more sense and indeed such pseudo-tuples are
starting to be supplied by the standard library (though not a general
mechanism for making your own, yet). Much like you can't iterate in
C++ on a std::pair<>, thus you "shouldn't" be able to iterate on a tuple.

"Frozen" versions of lists and dicts might make more sense -- and lose
only the MUTATING methods. I've seen Ruby experts claim that the
idea of freezing an object looks cool but in practice you end up almost
never using it -- they're speaking from experience, I guess, since in Ruby
they've had the possibility of freezing any object for ages. However, my
intuition (not supported by that much specific experience) makes me
yearn for such a feature...


Alex
 
D

David Abrahams

Alex Martelli said:
David Abrahams wrote:
...
I don't see why not.

The party line (not saying I necessarily agree, mind you -- just relating
it) is that tuples are meant as semantically heterogeneous containers
(semantically in that the items may happen to be the same type, but
their _meaning_ is disparate: e.g., consider the tuples used in modules
time or stat). So it doesn't really make sense to iterate on them, either;
it just happens to be possible because their items are accessed with []
with progressive naturals, and they have a len(). Associating _names_
to the indices makes more sense and indeed such pseudo-tuples are
starting to be supplied by the standard library (though not a general
mechanism for making your own, yet). Much like you can't iterate in
C++ on a std::pair<>, thus you "shouldn't" be able to iterate on a
tuple.

Well, (understanding that you don't nececessarily agree with the
above) you can in fact iterate on std::pair<T,T> with the usual C++
iterator protocol, and with a new mixed compile-time/runtime tuple
iterator protocol developed by Doug Gregor, iteration over
heterogeneous tuples is possible too. It certainly is desirable to be
able to do that; it's a real need that has come up in practice.
 
A

Alex Martelli

David Abrahams wrote:
...
Well, (understanding that you don't nececessarily agree with the
above) you can in fact iterate on std::pair<T,T> with the usual C++
iterator protocol,

You mean there's an std::pair::begin etc?! OK, I guess I'm even
rustier on standard C++ than I thought I was -- I could have SWORN
there wasn't. (Std chapter & verse pls? I plan to win bets based
on this tidbit...!-). So I guess the lack of those in gcc is a
breach of the Standard on gcc's part...?
and with a new mixed compile-time/runtime tuple
iterator protocol developed by Doug Gregor, iteration over
heterogeneous tuples is possible too. It certainly is desirable to be
able to do that; it's a real need that has come up in practice.

So, given an arbitrary struct, with fields (generally) of different types,
you can iterate field by field? That's basically what the "heterogeneous
tuple" is supposed to be equivalent to, in the "party line" I was relating
(although the names to go with the fields are only present in a FEW such
tuples, such as those returned by modules time and stat, as of now; more
general tuples still haven't grown the ability to access fields by name,
in Python).

My partial dissent comes from feeling the need for "frozen/immutable
lists" and the fact that tuples are often used as such (including goofy
immutable representations of dicts, e.g. via tuple(thedict.iteritems()),
and the like). If tuples cannot be thought of as immutable lists, then
(I think) we need "immutable/hashable/frozen" lists by other means.
(I need to say "I think" because, as I quoted, Ruby experts claim that
the "anyobject.freeze" feature they do have isn't actually as useful
as it SEEMS it should be -- though I'm not sure I understand why, yet).


Alex
 
S

Stephen Horne

David Abrahams wrote:
...

You mean there's an std::pair::begin etc?! OK, I guess I'm even
rustier on standard C++ than I thought I was -- I could have SWORN
there wasn't. (Std chapter & verse pls? I plan to win bets based
on this tidbit...!-). So I guess the lack of those in gcc is a
breach of the Standard on gcc's part...?

Somehow I think David is mistaken here - I cannot believe that
dereferencing an iterator returns a different datatype depending on
which item it happens to point to at runtime in statically typed C++,
and without that ability to dereference the iterator (1) I cannot see
the point of iterating through a pair, and (2) the 'iterator' would
not be a true iterator as C++ iterators have to comply with one of a
set of standard protocols (forward, bidirectional, random etc) which
all include subscripting.

Of course you can iterate through a container that holds std::pair
objects - you do that every time you iterate through an std::map - but
that isn't the same thing.
 
S

Stephen Horne

set of standard protocols (forward, bidirectional, random etc) which
all include subscripting.

Oops - I've got subscripting on the mind just now. I meant
dereferencing of course.
 
D

David Abrahams

Alex Martelli said:
David Abrahams wrote:
...

You mean there's an std::pair::begin etc?!

No, that's an entirely different question. I can build an iterator
which iterates over pair said:
OK, I guess I'm even rustier on standard C++ than I thought I was --
I could have SWORN there wasn't. (Std chapter & verse pls? I plan
to win bets based on this tidbit...!-). So I guess the lack of
those in gcc is a breach of the Standard on gcc's part...?

No, nothing like that.
So, given an arbitrary struct, with fields (generally) of different
types, you can iterate field by field?

Ah, no. A tuple in C++ is a different thing, and much more like a
Python tuple: http://www.boost.org/libs/tuple
That's basically what the "heterogeneous tuple" is supposed to be
equivalent to, in the "party line" I was relating

Well, that's just nutty. We can make a much better analogy to a
struct in Python with the usual class hacks. But even then, we can
arrange to iterate the attributes.
(although the names to go with the fields are only present in a FEW
such tuples, such as those returned by modules time and stat,

Whaa?? Are they subclassing tuple? Nope, time.struct_time is not
even a tuple. It's just the struct hack with a tuple-like repr()
( said:
as of now; more general tuples still haven't grown the ability to
access fields by name, in Python).

My partial dissent comes from feeling the need for "frozen/immutable
lists" and the fact that tuples are often used as such (including goofy
immutable representations of dicts, e.g. via tuple(thedict.iteritems()),
and the like). If tuples cannot be thought of as immutable lists, then
(I think) we need "immutable/hashable/frozen" lists by other means.

I agree completely.
(I need to say "I think" because, as I quoted, Ruby experts claim that
the "anyobject.freeze" feature they do have isn't actually as useful
as it SEEMS it should be -- though I'm not sure I understand why, yet).

Immutability is extremely useful whenever you have inplace operations
like +=, because it guarantees value stability for other references
to the same object.
 
D

David Abrahams

Stephen Horne said:
Somehow I think David is mistaken here - I cannot believe that
dereferencing an iterator returns a different datatype depending on
which item it happens to point to at runtime in statically typed
C++,

not said:
and without that ability to dereference the iterator (1) I cannot see
the point of iterating through a pair, and (2) the 'iterator' would
not be a true iterator as C++ iterators have to comply with one of a
set of standard protocols (forward, bidirectional, random etc) which
all include subscripting.

I'm pretty well familiar with those protocols - I've been working on
the C++ standards committee since 1997 and have written several
related proposals, c.f. http://tinyurl.com/ovpe.
Of course you can iterate through a container that holds std::pair
objects - you do that every time you iterate through an std::map - but
that isn't the same thing.

No, definitely not.
 
J

John Roth

David Abrahams said:
Ah, no. A tuple in C++ is a different thing, and much more like a
Python tuple: http://www.boost.org/libs/tuple


Well, that's just nutty. We can make a much better analogy to a
struct in Python with the usual class hacks. But even then, we can
arrange to iterate the attributes.


Whaa?? Are they subclassing tuple? Nope, time.struct_time is not
even a tuple. It's just the struct hack with a tuple-like repr()

(<type 'object'>,)

Not exactly. It's an object that can be subscripted like a
sequence, so it looks like a tuple for the most common
use cases. I haven't looked at the code, so I don't
know how far they went in putting in the remainder of
tuple behavior, though.

The subscripting and slicing is the key point here, though.
That's what makes it backward compatible. It's not an
extended tuple, and I doubt if it has most of the tuple
methods.

Frankly, I prefer to call it a data object, rather than
a "struct hack." Calling it a data object means that
it might grow other useful behaviors in the future. I'm
not sure about time, but I could appreciate methods
in stat that apply the access and modify dates to another
file in one operation.

And they most likely won't. It's easy enough to create
a data object that implements the basic part of the sequence
protocol.

John Roth
 
S

Stephen Horne

I'm pretty well familiar with those protocols - I've been working on
the C++ standards committee since 1997 and have written several
related proposals, c.f. http://tinyurl.com/ovpe.

OK - sorry for that.

I remain surprised that this degree of specialisation occurs, but it's
a case of live and learn I suppose.
 
S

Stephen Horne

No prob.


Sorry, what specialization?

Presumably template specialisation - such that the special case of
std::pair<T,T> picks up the iterating functionality that
std::pair<T,U> lacks (begin, end etc). That is what I thought you were
saying.

Or am I still getting this wrong?
 
D

David Abrahams

Stephen Horne said:
Presumably template specialisation - such that the special case of
std::pair<T,T> picks up the iterating functionality that
std::pair<T,U> lacks (begin, end etc). That is what I thought you were
saying.

Or am I still getting this wrong?

Yeah, slightly. You don't need a begin() member function in order to
make an iterator. The interface might look like:

std::for_each(pair_iterator<T>(my_pair), pair_iterator<T>(), f);

Decoupling is the way to go, man! :^)
 
S

Stephen Horne

Yeah, slightly. You don't need a begin() member function in order to
make an iterator. The interface might look like:

std::for_each(pair_iterator<T>(my_pair), pair_iterator<T>(), f);

Decoupling is the way to go, man! :^)

Ah - I get it! - std::pair doesn't exactly support iteration itself,
but a support class can be used to add that capability.

You can do this in any language. For instance, did you know that
Python classes supports iterating through the subset of their
attribute that have names beginning with "a", interleaved with
insults? Yes, all you need is to use this generator...

def A_Attrib_Gen (p_Instance) :
for i in dir (p_Instance) :
if i[0] = "a" :
yield i
yield "stupid stupid stupid"

Well, OK, maybe this is a little unfair - this generator isn't exactly
in the library, but with a little luck you see my point.

When you say "you can in fact iterate on std::pair<T,T> with the usual
C++ iterator protocol" it implies to me that std::pair<T,T> provides
the iterator protocol itself - not that some other class provides a
way to support iteration over the pair. After all, there is *always*
some way to support iteration of *anything*.

But maybe I'm just being overliteral.
 
D

David Abrahams

Stephen Horne said:
Ah - I get it! - std::pair doesn't exactly support iteration itself,
but a support class can be used to add that capability.

You can do this in any language.

Yes; I never implied otherwise.
For instance, did you know that Python classes supports iterating
through the subset of their attribute that have names beginning with
"a", interleaved with insults?

I never, anywhere, said "std::pair supports..."
Yes, all you need is to use this
generator...

def A_Attrib_Gen (p_Instance) :
for i in dir (p_Instance) :
if i[0] = "a" :
yield i
yield "stupid stupid stupid"

Well, OK, maybe this is a little unfair - this generator isn't exactly
in the library, but with a little luck you see my point.

Your sarcasm is briefly amusing, but unwarranted.
When you say "you can in fact iterate on std::pair<T,T> with the usual
C++ iterator protocol" it implies to me that std::pair<T,T> provides
the iterator protocol itself

Hey, it's not my fault you read what you want to see into what I
posted.
- not that some other class provides a
way to support iteration over the pair. After all, there is *always*
some way to support iteration of *anything*.

But maybe I'm just being overliteral.

No, you just don't know what "the usual iterator protocol" means. It
has to do with the protocol for using iterators, and begin()/end() are
nowhere in the iterator requirements. They're part of the container
requirements. There are many iterators that have nothing to do with
containers and have never been passed through a begin()/end().
Pointers to built-in arrays are one obvious example.

This whole discussion started out talking about what the Python
protocol for manipulating iterators should be. The fact that
iterators must themselves have an __iter__ method in Python may just
be making this more confusing than it needs to be.
 
S

Stephen Horne

It was my intention to explain the origins of my misunderstanding,
though certainly in a slightly less than serious way and with a big
dollop of its-not-all-my-fault-you-know. I tend to screw this kind of
thing up, and it seems I have done so again here.

Everything you have said in your last reply was at least 95% valid,
but there is a point I'd still like to make...
I never, anywhere, said "std::pair supports..." ....

Hey, it's not my fault you read what you want to see into what I
posted. ....

No, you just don't know what "the usual iterator protocol" means.

I never said, anywhere, that you said that "std::pair supports..." if
we are being that pedantic, but in the life of this thread neither of
us really has been that pedantic. I quoted your exact words for the
crucial point, which were...

"you can in fact iterate on std::pair<T,T> with the usual C++ iterator
protocol"

Pedantically speaking, this is imprecise language. Most significantly,
there is no such thing as "the usual C++ iterator protocol". An
iterator may implement any one of five different iterator protocols,
and these protocols have surprisingly little in common. All require an
operator++ and all require a dereference-style operator* and that is
it.

Actually, even the operator* isn't as common as it seems - input
iterators only support read access, output ones only write access - so
the practical commonality is limited to the operator++.

But then you weren't writing a standards document, and I wasn't
reading it as a standards document.

To most C++ programmers most of the time, the words "the usual C++
iterator protocol" don't have the pedantic meaning set by the C++
standard - they are not just about having an operator++. They have a
somewhat more pragmatic meaning, which includes the usual means of
obtaining iterators from containers. std::pair *is* a container in the
general sense of containing other objects, even though in the C++
standards document context it is not formally considered a container.

Of course there is room for misinterpretation in virtually any piece
of writing - criticism of your choice of words was certainly
intentional, but meant to be lighthearted.

But if you really believe that I "read what want to see into what
[you] posted" then I'm afraid you're wrong. I saw an implication which
you never intended, but that was right at the start when I had no
reason to "want to see" anything in particular.

I do have a certain history in this kind of
minor-misunderstanding-gets-overblown storyline, as Alex Martelli I
think can confirm if he's still following the thread. Actually, he'll
probably say you should consider yourself lucky that it only got this
bad ;-)

Anyway, I'm in no doubt that I'm primarily (probably entirely)
responsible for the 'overblown' part especially with my previous post.
I appologise for that.

This whole discussion started out talking about what the Python
protocol for manipulating iterators should be. The fact that
iterators must themselves have an __iter__ method in Python may just
be making this more confusing than it needs to be.

We may think of '__iter__' as part of the iterator protocol but,
pedantically speaking, it is no more part of the Python iterator
protocol than 'begin' is part of the C++ iterator protocol.

Iterators don't have to have an '__iter__' method in Python. Iterators
only have to have a 'next' method. It is the iterable object that
implements '__iter__'. And, as with getting C++ iterators from 'begin'
etc, even that is a convention rather than a requirement.

Sometimes iterators do have an '__iter__' method, but that normally
just returns self - it's a quick-fix convenience for when iterators
end up in a context where an iterable object is expected.

I guess our mindsets aren't so different that we can't make similar
mistakes ;-)


BTW - there's no reason why an iterator can't have additional methods
beyond those required for the iterator protocol. So a container class
could, for instance, support the following...

for i in iter (container).range (begin, end).reverse () :
...

Simply by defining its iterator class as something like...

class Iter (object) :
def __init__ (self, p_Src) :
"Keep ref to container to iterate, plus other setup stuff."
self.Src = p_Src
...

def __iter__ (self) :
"Allow iterator to behave as iterable for convenience"
return self

def range (self, p_Begin, p_End) :
"Set restriction on range to be iterated"
...
return self

def reverse (self) :
"Set reverse-order iteration mode"
...
return self

def next (self) :
"Get next item"
...

Which reminds me of Paul Foleys post in "Thoughts on PEP284",
Message-ID: <[email protected]>, about Lisp-like
for-loops - though actually it's really just a minor variation of what
Raymond suggested right from the start.

Hmmm - possibly this suggestion should be made closer to the root.
 
S

Stephen Horne

Please comment on the new PEP for reverse iteration methods.
Basically, the idea looks like this:

I have one last suggestion to make on this. Instead of adding a method
to the container, possibly it should be added to the iterator.

If the iterator class was roughly equivalent to the following...

class Iter (object) :
def __init__ (self, p_Src) :
"Keep ref to container to iterate, plus other setup stuff."
self.Src = p_Src
...

def __iter__ (self) :
"Allow iterator to behave as iterable for convenience"
return self

def reverse (self) :
"Set reverse-order iteration mode"
...
return self

def next (self) :
"Get next item"
...

We could write...

for i in iter (seq).reverse () :

Possible advantages being...

1. The need for/existence of an iterator is made explicit by the
already familiar 'iter' call.

2. Because the 'reverse' method is applied to the iterator rather
than the container, the existing spelling can be used without
worries about iterating over strings (or user classes) that
already have a 'reverse' method.

3. It's an extensible approach (other 'iterator modifiers' could be
defined in much the same way, e.g. range restriction) yet at the
same time both simple and lightweight.

The xrange and enumerate classes would probably also adopt the
'reverse' spelling for consistency...

for i in xrange(10).reverse() :
...
 
D

David Abrahams

I wasn't even going to post this to the list, because it's so full of
static and other unpythonic stuff. But then, Stephen hides his email
address, and there is one real Python-related issue at the bottom,
so...

Stephen Horne said:
It was my intention to explain the origins of my misunderstanding,
though certainly in a slightly less than serious way and with a big
dollop of its-not-all-my-fault-you-know.

I didn't need to have an assignment of blame in order to find closure
with this thread. I was just trying to be helpful.
I tend to screw this kind of thing up, and it seems I have done so
again here.

Then I can't understand why you continue to charge about in the china
shop....
I never said, anywhere, that you said that "std::pair supports..." if
we are being that pedantic, but in the life of this thread neither of
us really has been that pedantic.

Seems like you've started.
I quoted your exact words for the crucial point, which were...

"you can in fact iterate on std::pair<T,T> with the usual C++ iterator
protocol"

Pedantically speaking, this is imprecise language.

No, there is a common subset of all iterator requirements and that's
what I was referring to.
Most significantly, there is no such thing as "the usual C++
iterator protocol". An iterator may implement any one of five
different iterator protocols, and these protocols have surprisingly
little in common. All require an operator++ and all require a
dereference-style operator* and that is it.

And that is the usual iterator protocol. I happen to know exactly
what those protocols have in common.
Actually, even the operator* isn't as common as it seems - input
iterators only support read access, output ones only write access

Actually it's subtler than that. You can have input/output iterators
which support random access which aren't random access iterators
because they don't support lvalue access. So what, though?
- so the practical commonality is limited to the operator++.

You need operator* regardless.
But then you weren't writing a standards document, and I wasn't
reading it as a standards document.

That's why I didn't understand why you were giving me such a hard
time. Because it's informal speech I'm supposed to do quadruple duty
to make sure you haven't misinterpreted me? I really was going out
of my way to explain this stuff to you politely.
To most C++ programmers most of the time, the words "the usual C++
iterator protocol" don't have the pedantic meaning set by the C++
standard - they are not just about having an operator++

And operator*.
They have a somewhat more pragmatic meaning, which includes the
usual means of obtaining iterators from containers. std::pair *is* a
container in the general sense of containing other objects, even
though in the C++ standards document context it is not formally
considered a container.

Then so is

struct { int x, y; };

Also, so is char[4], and unsigned long is a container of at least 32
bits.

This is getting ridiculous. It seems like you want to have a pedantic
debate about terms whose meaning is going to be defined by your
completely informal and subjective interpretation.
Of course there is room for misinterpretation in virtually any piece
of writing - criticism of your choice of words was certainly
intentional, but meant to be lighthearted.

But if you really believe that I "read what want to see into what
[you] posted" then I'm afraid you're wrong. I saw an implication which
you never intended, but that was right at the start when I had no
reason to "want to see" anything in particular.

I do have a certain history in this kind of
minor-misunderstanding-gets-overblown storyline, as Alex Martelli I
think can confirm if he's still following the thread. Actually,
he'll probably say you should consider yourself lucky that it only
got this bad ;-)

Anyway, I'm in no doubt that I'm primarily (probably entirely)
responsible for the 'overblown' part especially with my previous post.
I appologise for that.


Thanks, I think. This is somewhat of a backhanded apology, but I'll
take it. [If you had just stopped with the last message, I wouldn't
even feel it mattered].
We may think of '__iter__' as part of the iterator protocol but,
pedantically speaking, it is no more part of the Python iterator
protocol than 'begin' is part of the C++ iterator protocol.

Iterators don't have to have an '__iter__' method in Python. Iterators
only have to have a 'next' method. It is the iterable object that
implements '__iter__'. And, as with getting C++ iterators from 'begin'
etc, even that is a convention rather than a requirement.

Sorry,

http://www.python.org/doc/current/lib/typeiter.html#l2h-149

Read it twice, carefully.
Sometimes iterators do have an '__iter__' method, but that normally
just returns self
Always.

- it's a quick-fix convenience for when iterators
end up in a context where an iterable object is expected.

I guess our mindsets aren't so different that we can't make similar
mistakes ;-)

I'm afraid not. I never, ever make mistakes in the first place ;->

infallib-ly y'rs
 
S

Stephen Horne

That's why I didn't understand why you were giving me such a hard
time. Because it's informal speech I'm supposed to do quadruple duty
to make sure you haven't misinterpreted me? I really was going out
of my way to explain this stuff to you politely.

Nope - occasional misreadings are a fact of life and shouldn't be a
big deal. But when you said "I never, anywhere, said "std::pair
supports..."" that seemed very pedantic to me, as the issue wasn't
your precise words but the meaning behind them. And when you said
"Hey, it's not my fault you read what you want to see into what I
posted." you seemed to be claiming that there was no room for
reasonable misinterpretation in your original words and that I was
100% to blame for the misinterpretation.

The whole point of my pedanticism was to point out that your original
statement was informal and subjective and that, while that was
certainly wholy appropriate, there was room for accidental
misinterpretation. IIRC I was not even the first to misinterpret - I
simply replied to Alex Martelli who had already misinterpreted your
words the same way that I did. So being made a scapegoat seemed
unfair.

And now I seem to be whining about it far to much, but really I just
want this point to be understood - and I wish I had been clear about
it in my last post.
Then so is

struct { int x, y; };

Also, so is char[4], and unsigned long is a container of at least 32
bits.

Not in the pedantic 'C++ standard definition' sense of course, but the
C++ standard has taken an existing word and tied a more restrictive
meaning to it. In the wider world the original unrestricted meaning is
still valid.

To me both structs and char arrays are containers in the
less-restrictive sense. After all, if a C programmer talks about
containers what is he referring to? And in C# even a fixed-size array
is implemented using a library class.

An unsigned long, no - that is a single atomic item. But then again,
IIRC, in the pre-C++ days when I first learned programming, 'container
for a value' was one common way of explaining the concept of a
variable ;-)
Thanks, I think. This is somewhat of a backhanded apology, but I'll
take it. [If you had just stopped with the last message, I wouldn't
even feel it mattered].

I felt it mattered because I felt that I was being held soley and
entirely responsible for a mistake that I felt wasn't unreasonable.

I appologise for the misinterpretation because I did misinterpret, and
I particularly appologise for the overblowing because it arises out of
my oversensitivity to certain things you said - which I certainly have
no right to given the sarcastic tone of my earlier post, and in any
case I have no doubt your words resulted from frustration.

I do not, however, accept that I am enirely and soley to responsible
for the misinterpretation.

My persistence in sticking to this may seem bizarre and petty, but
there are reasons for my being bizarre and petty over such things.
Lets just call it 'desperate defending of what little self esteem I
have left' - and yes, it does tend to be counterproductive :-(

OK - you are right and I am feeling a right fool. Sorry.
I'm afraid not. I never, ever make mistakes in the first place ;->

infallib-ly y'rs

:)
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,774
Messages
2,569,598
Members
45,152
Latest member
LorettaGur
Top