[EVALUATION] - E04 - Leadership! Google, Guido van Rossum, PSF

M

Martin P. Hellwig

Anton Vredegoor wrote:
Most people can survive (without damaging their souls so to speak) when
working for corruption themselves in this way, but sooner or later one
is asked to corrupt others (defending one's title during a promotion,
leading a community and so on). This is the crucial point where
corruption definitively occurs: where silence is not enough anymore to
keep one employed.

The human brain can withstand extreme amounts of conficting sensory
data and still maintain a sense of continuity and order while from all
sides things seem to fall apart. Such continuity is highly valuable
because it is the only thing that keeps chaos at arms length. The same
thing can be applied to whole societies (see for example the state
American politics is in now). So that is the reason these kind of
things normally are swept under the carpet.

However for *me* personally, because I am deserted by my government,
the university community, friends and family, and left to fend for
myself, there is no pressing need to keep up the facade so I can
finally see it for what it is.
Modern life has modern problem, well that isn't quite true, the problems
are actually the same since the moment organisms began to group to
enlarge their chance of survival.
The funny thing is that the problem, in its essentials, is still the
same, how to enlarge the chance of survival, this time within the group.
"En zie daar" (pardon my dutch) we have created hierarchy and within
that the problem of the individuals identity versus the groups identity,
mix it with diplomacy and "unreal" friendship (the ones that are based
on a persons status) and you got enough material to create Hollywood and
everything that comes from it. If you add "take one for the group" you
got yourself an action movie.

Now theory is all fine and dandy especially if its common sense but
where does it leave us? Well most of the time, screwed, for the lack of
a better word. But is it fair to negatively mark people because their
personal identity for whatever reason fitted better to the common group
identity? Sure it's bitter but don't be bitter about it, you know
probably more then anybody else where that bitterness leads to.

What we can do about it? I have no clue, I'll just use that frustration
to work on a personal project or making a song on my guitar, but YMMV
;-). Perhaps being happy for somebody's else's luck is the key to ones
own happiness, at least it works for me.

Now luckily where on the Internet where anybody can be anybody or not if
they like to, at least everybody got a chance (not an equal one but
still) to show what they are worth of. Perhaps you could too?

I wish you the best of wisdom and luck and I mean that most sincerely.
<cut rest>
 
I

Ilias Lazaridis

Alex said:
" * BS or MS in Computer Science or equivalent (PhD a plus). "

This referes to an _academic_ degree.

Ah, I see. It depends on the job; for example,
<http://www.google.com/support/jobs/bin/answer.py?answer=23641> phrases
the requirement in a more explicit way:

* BS in Computer Science or equivalent experience. [...]

<http://www.google.com/support/jobs/bin/answer.py?answer=23604> which is
very specific about requiring:

* A BS degree in mechanical, electrical or industrial technology.
Very few companies make an explicit statement about non-academic applicants.

It seems Google does not.

It seems we do, for some but not all of our job openings -- that "or
[...] - (comments, examples, elaborations)

I understand.

Ok, thus Google is flexible in this.

[sidenote: some jobs _require_ a degree by law]

So, I like Google again (in this context).

-

What about external independents ?

Does Google cooperate with them?

And how can one contact such a giant?
Thanks: if I have to get mis-spelled, I prefer the alternate
mis-spelling "Martel", which at least refers to a potable cognac!-)

hicc!

-

Python vs. jamLang follows:
[...] - (code, explanations)

thanks, I've changed this:

http://lazaridis.com/case/lang/python.html#simple_variable_access

this leads to a new limitation:

"#LIMITATION: large amount of repetitive code"
You can surely define a custom metaclass with methods that call
inspect.whatever, or directly perform whatever introspection you
require; it just seems strange to me to put this functionality in the
metaclass. At any rate, SOME code will need to execute to perform this
task, so "without code" cannot be accomplished. (Looking at your

I meant: reproduce the definition of the class (but without reproducing
the source-code of the class)

I have removed the "without code" remark, which was missleading.
evaluation of Ruby I note there's a LOT of code for this corresponding
case, indeed through inspect, despite the 'without code' specification).
[...] - (code-level elaborations, implementation suggestions)

ok
[...]
I assure you: the level is totally appropriate.

My point is that whether you call:

setattr(zap, zip, zop)

or

zap.__setattr__(zip, zop)

is a syntax trifle. The semantics are the same (setattr is defined to
[...] - (elaborations)

I've understood this.

[...]
I'd put it the other way 'round, since assigning to Object.meta is the
simpler and most common approach (for an attribute name that's fixed).
setattr is generally used only when the attribute name is computed at
runtime (a variable or expression), because in that case there is no
more direct syntax for the task. getattr, in addition, has another use
case: you can provide a "default value" so that, if the attribute is
absent in the object, you get the default value rather than having to
field an exception (AttributeError). IOW, instead of:

try:
mymeta = Object.meta
except AttributeError:
mymeta = "No metainfo available"

you can code the simpler:

mymeta = getattr(Object, 'meta', "No metainfo available")

This is a reasonably frequent use case, since nobody particularly likes
using four "atomic actions" (try, etc) to express the unitary concept
"give me the 'meta' attribute, IF available, otherwise a default".

I understand now.
Correct. In particular, Python has a concept of IMMUTABLE objects:
objects which, once created, cannot be altered in any way. In
particular, you cannot add attributes (or change existing ones, etc) in
such "immutable", aka "constant", objects.

=> Python: immutable objects
If any language you evaluate lacks the concept of immutable objects, be
sure to note the reciprocal "LIMITATION: cannot define immutable,
constant objects".

yes, you are right.

=> {New Requirement: ability to declare objects as mutable/immutable.}
Python could do better (since some objects are mutable and still don't
support attribute-setting -- function objects for example used to be
that way, though that limitation was removed a few versions ago), but
giving up the important concept of "constant"/"immutable" objects would
not be right, so (given the inherent contradiction between having
something immutable and being allowed to add stuff to it) I dearly hope
it will never be possible to alter EVERY object.

Again looking at your evaluation of Ruby, it seems this point is
incorrect there: in Ruby, it's possible to ``freeze'' an object, making
it constant/immutable; once you've done so, you can't then "add
arbitrary" [[or non-arbitrary, for that matter!-)]] "attributes" to it.

I estimate that there is a "unfreeze" operation, too - which would lead
to flexibity.
If you consider it a limitation that "unchangeable objects cannot be
changed" (which, to me, is a mere DEFINITION of "unchangeable"!-), you
should record the alleged limitation explicitly in both cases.

I think I've understood your elaborations.

[...]
You mean something like...:

Talker.meta = "Class meta information"
john.meta = "Instance meta information"
Talker.sayHello.meta = 'method meta information"

You can't do that on an integer, because an integer is immutable; you
ok

can't do it on 'Talker.name', because there IS no such thing as
'Talker.name' (if that's a limitation, it's also a limitation that you
can't do it on 'rumpelstiltskin.meta' either, and the reason is just the
same: there IS no such thing as rumpelstiltskin in this code).
ok

There's no need to define this 'meta' attribute anywhere, it just
springs into existence when you assign to it.

?

"assign to it" with:

setattr(Talker, 'meta', "Class meta information")

but _not_ with this:

Talker.meta = "Class meta information"

correct?
Once you've set these, you can "print Talker.meta" etc etc.
ok


Hope I've done that in the few cases where I understood what your
intentions were, but there must be more in which I didn't, such as the
strange "without code" specification.

clarified above.
You're welcome.

Alex

..
 
D

Duncan Booth

Alex said:
It IS true that in Python you cannot set arbitrary attributes on
arbitrary objects. The workaround is to use a dict, indexed by the id
of the object you want to "set arbitrary attributes on"; this has the
helpful consequence that separate namespaces are used, so your arbitrary
setting of metadata cannot interfere with the `true' attributes of the
object in question.
That's a horrible suggestion (using id's, not the bit about separate
namespaces). If you use the id then attributes will persist beyond the
lifetime of the object and may suddenly reappear on other unrelated objects
later.

A better suggestion here would be to use weak references. Unfortunately,
not every Python object can be the target of a weak reference, so there is
a limitation here preventing a useful implementation for many builtin
types. I can't actually think of a use case for what Ilias wants, and if
there isn't a use case it isn't a big problem, but if anyone can come up
with a usecase please say.

BTW, I don't know Ruby enough to understand the example at
http://lazaridis.com/case/lang/ruby/base.html:

class Object
def meta # adds variable "meta" to all objects in the system
end

Talker.meta = "Class meta information"
john.meta = "Instance meta information"
1234.meta = 'any Instance meta information"

puts Talker.meta
puts john.meta
puts 1234.meta # an integer object

With the above code what would 'puts someexpressionresultingin1234.meta'
output? i.e. is the attribute being set on all integers with the value
1234, or just on a specific instance of an integer.

I don't know if the question even makes sense for Ruby, but it obviously
needs to be answered before similar code could be implemented for Python.

Anyway, subject to the restriction that it doesn't work for int, list,
tuple, etc. here is some code which lets you assign attributes the way I
think Ilias wants. Unlike the Ruby code it doesn't just dump them all in
the same namespace as other attributes, instead you have to create one or
more meta namespaces which then don't interfere at all with other
attributes on the objects, but which in other ways work just like
attributes (e.g. for the purposes of inheritance you can set an attribute
or a method on a base class and it works fine in instances of derived
classes.)

BTW, If anyone does actually want to use this, the attribute lookup code is
incomplete: completing it is left as a exercise.

------------- metaspace.py -------------------
from weakref import WeakKeyDictionary

class _Metanamespacewrapper(object):
def __init__(self, namespace, target):
self.__dict__['_namespace'] = namespace
self.__dict__['_target'] = target
d = namespace.d
if target not in d:
d[target] = {}
self.__dict__['_dict'] = d[target]

def __getattribute__(self, name):
if name.startswith('_'):
return object.__getattribute__(self,name)

if name in self._dict:
return self._dict[name]

t = type(self._target)
for klass in (t,)+t.__mro__:
try:
d = self._namespace.d[klass]
v = d[name]
except KeyError:
continue
break
else:
raise AttributeError, "meta namespace has no attribute '%s' on
object '%r'" % (name, self._target)

if hasattr(v, '__get__'):
return v.__get__(self._target)
return v

def __setattr__(self, name, value):
self._dict[name] = value

def __delattr__(self, name):
del self._dict[name]

class Metanamespace(object):
def __init__(self):
self.d = WeakKeyDictionary()

def __call__(self, target):
return _Metanamespacewrapper(self, target)

meta = Metanamespace()

# Example of use...

class Talker(object):
def sayHello(self):
print "Hello world"

john = Talker()

# Check simple access to attributes
meta(Talker).name = 'test'
print meta(Talker).name, meta(john).name

meta(john).name = 'a name'
john.name = 'real attribute' # Does not interfere with meta namespace
print meta(john).name, john.name

meta(object).arg = 5
print "arg=", meta(john).arg
meta(john).arg = 2
print "arg=", meta(john).arg
del meta(john).arg
print "arg=", meta(john).arg

def fn1(self, arg):
print "fn1", self, arg
def fn2(self, arg):
print "fn2", self, arg

# Check that methods work properly
meta(object).fn = fn1
meta(john).fn(1)
meta(Talker).fn = fn2
meta(john).fn(2)

-------------------------------------------------
The output is:

test test
a name real attribute
arg= 5
arg= 2
arg= 5
fn1 <__main__.Talker object at 0x009D9670> 1
fn2 <__main__.Talker object at 0x009D9670> 2
 
I

Ilias Lazaridis

Alex said:
No insider information is necessary, the job requirements make it
absolutely clear (at least to me) that Google is a company with an
elitist culture,

Absolutely yes, in terms of who we want to work at Google: we DO want
GREAT people. And we don't keep this a secret, either: right up there
at <http://www.google.com/jobs/>, we say "our strategy is simple: we
hire great people". Rather than hiring a LOT of people, we prefer to be
extremely, obsessively selective, and try to hire ONLY a few people,
ones who we can convince ourselves do deserve that adjective, "great".

This does mean that we definitely tend err on the side of caution, and
FAIL to hire some people who are also great, just because we can't
determine with sufficient certainty that they indeed are -- I've seen
this happen more than once, and deeply regret it (for both Google and
the person), but I have no idea how we could do better without relaxing
our extremely elitist standards (we do debate these issues internally
all of the time, trying to do better, but have found no magic wand yet).
[...]
students), but I've met many people with advanced degrees from even the
best/most elitist universities, such as Stanford or MIT, where it sure
looked to me as if the university's attempts to only graduate the very
best have definitely failed.
[...]
Requiring a certain title for a job is mostly a desperate attempt to
reduce the huge amount of work and effort it takes to hire great people,
whittling down the number of resumes to be considered divided by the
number of hires from the high thousands to the low hundreds. If there
were available infinite resources for the job of hiring/selection, we
could easily interview, say, 6000 candidates for a post, giving each a
week or so of concentrated attention to probe their abilities; alas,
this would require about 120 person-years from our people for the
selection process. So, if nobody at Google did ANYTHING BUT interview
candidates, given that we have a bit over 5000 employees now, we could
hire in the course of 2006 another 40 or so, without doing anything
else. (The numbers are all off the top of my head, but I think they may
be roughly the right orders of magnitude).

This is just impractical: we need to hire many more than 40, AND cannot
afford to have all existing employees do nothing but select new ones.
So, we need to shrink the ratio drastically, on both factors: say 10
instead of 40 hours of selection per candidate, and 50 rather than 6000
candidates being considered per post. So we perform selection in
stages, and most candidates out of those many thousands-per-job are
"weeded out" at the very first stage, e.g. by failing to meet specific
qualifications.

I wish that, as you say, "titles" were indeed strong indications of
excellence. Unfortunately, they aren't, but in some cases they're
better than nothing. Many of our job descriptions, as I pointed out in
another post on this thread, say "BS or equivalent experience" or words
to that effect; if you can show the "or equivalent", and can get past
the first hurdle, then that title is the least of the issues. For
example, if we advertised a job requiring "PhD or equivalent", and among
the candidates were Bill Gates, Larry Page, and Sergey Brin, none of
whom has obtained a PhD to the best of my knowledge, they would surely
be able to display the "or equivalent" based on their accomplishments
and experience, and thus get past that first hurdle.
[...]

-

TAG.google.evolution.talent.detection

..
 
A

Alex Martelli

Ilias Lazaridis said:
Ok, thus Google is flexible in this.

[sidenote: some jobs _require_ a degree by law]

Or some even more stringent qualification, such as the state's Bar exam
for lawyers -- you may not be able to sit for that exam w/o the
appropriate degree, but the degree by itself is not enough, you still
have to pass the exam. It is that way for Engineers in Italy (I passed
my State Exam in the early '80s), although you only need the certificate
for some specific professional undertakings (e.g. design a ship, or a
large building, or technically supervise building operations beyond a
certain size -- not to write software or to design chips).

Personally, I agree with the theory, first expressed by Adam Smith, that
such barriers to entry are mostly useful to grant practitioners of a
certain profession the "scarcity value" that lets them charge higher
prices, although of course they're always presented as "good for
society". Note that in Europe in the Middle Ages you needed strict
qualifications of that kind for just about anything -- you could not
make hats unless you belonged to the Hatters' Guild, etc; most of those
restrictions have since been lifted, but a few groups (doctors, lawyers,
accountants, ...) have managed to keep them in place.
What about external independents ?
Does Google cooperate with them?

Hardly ever... I, too, was at first trying to see if I could establish
some kind of consulting relationship with Google, in order to keep my
existing freelance practice alive as well, but in the end I found no way
to do so. I believe Google's external consultants are very few -- e.g.,
some lawyers (Google employs some, but also has some external ones on
retainer), the members of our Board of Directors, Hal Varian.
And how can one contact such a giant?

I believe that starting at <http://www.google.com/about.html> and
navigating from it will show you all the various avenues of contact
depending on your purpose for the contact; except that, for questions
about the Google Foundation, it might be better to start at
<http://google.org/> instead. Since the number of people wanting to
establish such contact for all sort of purposes is very large, and only
a few of Google's 5000 employees spend their time dealing with such
contact attempts, I have unfortunately heard of many cases in which such
attempts prove unfruitful.

http://lazaridis.com/case/lang/python.html#simple_variable_access

this leads to a new limitation:

"#LIMITATION: large amount of repetitive code"

One normally does not define large numbers of identical accessors (there
would be no purpose served in so doing), so the "boilerplate"
(repetitive code) does not truly occur. If for whatever reason one DOES
want a bazillion identical accessors, a simple custom metaclass (if one
has many classes with such needs), or simpler code still (if just one or
two classes require such bundles of accessors), avoid the repetitions.

For example (untested code, but should work):

class Talker(object):
def __init__(self):
self._name = ''
self._age = 0
for attr in '_name _age'.split():
def getter(self, attr=attr): return getattr(self, attr)
def setter(self, value, attr=attr): return setattr(self, attr, value)
setattr(Talker, attr[1:], property(getter, setter))

This is probably not worth the bother for just 2 attributes, but it
trivially generalizes to a bazillion attributes, if that's what you
want. A custom metaclass could also more easily define an __init__
based simply on attribute names and default values desired (it's quite
possible this way, too, of course); alternative approaches include
wrapping the 'class' statement and the following loop in a factory
function which builds and returns the desired class (this gives roughly
the same power as a custom metaclass for this specialized task, though a
custom metaclass is more flexible and general); and the use of lexical
closures in preference to the simple getter and setter functions shown
here (or, factory functions for properties, embedding such closures).
E.g., change the loop to:

def makeprop(attr):
def getter(self): return getattr(self, attr)
def setter(self, value): return setattr(self, attr, value)
return property(getter, setter)
for attr in '_name _age'.split():
setattr(Talker, attr[1:], makeprop(attr))

Some would consider this more elegant (better factored).


The reason you don't see this kind of thing emphasized in Python
literature is that this style of programming is very rarely needed --
mostly when you're building a framework, or the Python modest equivalent
of a domain-specific minilanguage, or some kind of automated code
generator. For 99% of the normal, everyday application programming one
does in Python, this introspective metaprogramming would be overkill,
although that may not be obvious to one who does not know Python -- for
example, it remains true that the addition of all of those trivial
getters and setters by whatever means performs NOTHING useful -- the
class's functionality and interface are IDENTICAL to the one you'd
normally code, with directly accessible attributes and without all of
those boilerplate methods.

You'll find more examples of appropriate use of metaprogramming and
introspection in the 2nd edition of the Python Cookbook, though.
I meant: reproduce the definition of the class (but without reproducing
the source-code of the class)

I have removed the "without code" remark, which was missleading.

Aha! I see now, and it does make more sense. Yes, using inspect you
could surely emit for example skeletons for the various methods, with
e.g. a 'pass' in lieu of their code. However, since instance attributes
are determined by code that gets executed (in __init__, and maybe in
__new__ and even elsewhere), it's not really practical to find out what
attributes an instance would have without in fact creating such an
instance and introspecting on it. Would such instantiation be OK here?
In some cases instantiating a class might have externally visible
effects, say opening a network connection, or a database, etc, so you
might well want to forbid that for purely introspective purposes.
yes, you are right.

=> {New Requirement: ability to declare objects as mutable/immutable.} ...
I estimate that there is a "unfreeze" operation, too - which would lead
to flexibity.

Yes, but also mean that immutable objects are not really immutable, only
"immutable until further notice". For example, the immutability of
objects can be used to enhance the ability to reason about a program's
correctness... but such reasoning is not helpful if an 'immutable'
object isn't really immutable.

Perhaps rather than thinking in terms of ideals and limitations, in this
field you could just use a simple descriptive approach, since each
choice has some advantages. E.g., a language such as Haskell occupies
one (productive) extreme: EVERY object is immutable -- you never change
existing objects, but rather make new ones as needed; this makes the
language ideal to reason about program correctness, although it requires
a programming style very different from what's normal in other languages
(I believe Haskell and other functional-programming languages of its ilk
requires very deep abilities to reason mathematically -- I've been known
to describe that as "math or CS PhD", although of course that's merely
indicative!). At the other extreme, Ruby's very productive choice is to
allow freeze and unfreeze of everything (I believe -- but you should
double check with a Ruby expert) -- makes it a lost cause to use
immutability to reason about program correctness, but allows a wide
variety of programming styles.

Most languages try to strike a balance with SOME objects being mutable
and others not, which of course like all compromises tries to get some
advantages from each side but also inevitably gets some DIS-advantages
too. It's not necessarily easy to see all the implication of each such
choice, or trade-off, and yet most design IS about making trade-offs...
?

"assign to it" with:

setattr(Talker, 'meta', "Class meta information")

but _not_ with this:

Talker.meta = "Class meta information"

correct?

Nope: both forms have IDENTICAL semantics. They both work in just the
SAME way.

Try it out...!


Alex
 
A

Alex Martelli

Duncan Booth said:
That's a horrible suggestion (using id's, not the bit about separate
namespaces). If you use the id then attributes will persist beyond the
lifetime of the object and may suddenly reappear on other unrelated objects
later.

The second sentence is true, but does not imply the first: just add a
strong reference to the object you're imposing extra attributes on
(e.g., pile such objects into an auxiliary list).
A better suggestion here would be to use weak references. Unfortunately,
not every Python object can be the target of a weak reference, so there is
a limitation here preventing a useful implementation for many builtin

....which is why I didn't suggest that;-).
types. I can't actually think of a use case for what Ilias wants, and if
there isn't a use case it isn't a big problem, but if anyone can come up
with a usecase please say.

Many usecases of Lisp's property-lists might apply to Python just as
well, assuming one could associate properties/attributes to all objects
(as you can have property-lists anywhere in Lisp).

For example, you could associate to each of a lot of strings in some set
of data structures (but not necessarily all of them) the codec to be
used with that string. This is typical metainformation: a string of
bytes as such does not tell you how to make it back into Unicode, and in
the cases where you have obtained that metainformation it would be nice
to store it somewhere (falling back to heuristics if you ever need to
deal with strings for which you haven't yet obtained the metainfo).

Similarly, some numbers (but again not necessarily all of them, for
whatever complex datastructures set they're used in) might usefully be
associated with the unit of measure they're in (again falling back to
heuristics if needed).

In Python you just know you can't easily do that so you typically turn
the whole program around to use, not strings or numbers with optional
metainfo associated to each, but instances of more complicated datatypes
which carry the information and the optional metainformation. But if
you're used to the extremely handy idiom of just associating interesting
metainfo with any object whatsoever, that's quite a bother.

If you know that all you're decorating with propertylist is hashable,
you can use a simple dictionary -- but then one day you want to add some
metainformation to a file instance, and OUCH, that just fails... so
you're back to workarounds of various sorts, the most popular being no
doubt "superstructures" (subclasses or wrappers) holding the real data
and optional metainformation separately.

The ability of having attributes on functions is relatively recent in
Python and basically spoke to the same kind of needs (in that case,
there were also enough abuses of functions' docstrings to hold what was
in fact metainformation that nobody could really doubt the usecase for a
better approach;-).

With the above code what would 'puts someexpressionresultingin1234.meta'
output? i.e. is the attribute being set on all integers with the value
1234, or just on a specific instance of an integer.

Good question, presumably answerable with a small Ruby test (which
however I have no time to perform right now;-).


Alex
 
H

Hans Nowak

Duncan said:
BTW, I don't know Ruby enough to understand the example at
http://lazaridis.com/case/lang/ruby/base.html:

class Object
def meta # adds variable "meta" to all objects in the system
end

I don't think this is valid Ruby code, by the way... It should probably
be something like this:

class Object
attr_accessor :meta
end
Talker.meta = "Class meta information"
john.meta = "Instance meta information"
1234.meta = 'any Instance meta information"

puts Talker.meta
puts john.meta
puts 1234.meta # an integer object

With the above code what would 'puts someexpressionresultingin1234.meta'
output? i.e. is the attribute being set on all integers with the value
1234, or just on a specific instance of an integer.

At first glance, it seems the former is true:

irb(main):021:0> class Object
irb(main):022:1> attr_accessor :meta
irb(main):023:1> end
=> nil
irb(main):026:0> 1234.meta = "fred"
=> "fred"
irb(main):027:0> (1000+234).meta
=> "fred"
irb(main):028:0> x = 617
=> 617
irb(main):029:0> x *= 2
=> 1234
irb(main):031:0> x.meta
=> "fred"
irb(main):032:0> 3.meta
=> nil

However, inspecting the object_id (comparable to Python's id()) shows
that all these refer to the same object:

irb(main):035:0> 1234.object_id
=> 2469
irb(main):036:0> x.object_id
=> 2469
irb(main):041:0> y = 1000
=> 1000
irb(main):042:0> y.object_id
=> 2001
irb(main):043:0> y += 234
=> 1234
irb(main):044:0> y.object_id
=> 2469

I am not an expert on Ruby internals, but it looks like these integers
are cached. As with Python, I don't know if one can count on this
behavior to happen always.
 
D

Duncan Booth

Alex said:
The second sentence is true, but does not imply the first: just add a
strong reference to the object you're imposing extra attributes on
(e.g., pile such objects into an auxiliary list).
Except that would lead to fairly massive memory leaks, so isn't really
of practical use. I guess you could combine an auxiliary list with a
periodic scan of the list releasing those objects which are referenced
only from the list, which would reduce the problem to objects
participating in cycles. Combine that with the weak reference approach
wherever it works and you might be able to cover most situations.
Good question, presumably answerable with a small Ruby test (which
however I have no time to perform right now;-).

Hans Nowak seems to have partly answered that. It looks as though the id
of a Ruby integer is one more than twice the integer's value so I guess
that ruby packs the integer value in place of the object reference. A
quick search reveals:
A Fixnum holds Integer values that can be represented in a native
machine word (minus 1 bit). If any operation on a Fixnum exceeds this
range, the value is automatically converted to a Bignum.

Fixnum objects have immediate value. This means that when they are
assigned or passed as parameters, the actual object is passed, rather
than a reference to that object. Assignment does not alias Fixnum
objects. There is effectively only one Fixnum object instance for any
given integer value, so, for example, you cannot add a singleton
method to a Fixnum.

So it looks like the Ruby example will work as observed for integers which
fit in one bit smaller than a machine word and then work differently for
larger integers.
 
I

Ilias Lazaridis

Alex said:
Ilias Lazaridis <[email protected]> wrote:
[...] - google stuff
One normally does not define large numbers of identical accessors (there
[...] - (extensive elaboration)

possibly one can provide the code for something similar to the ruby
attr_accessor:

class Talker
def sayHello
puts "Hello world"
end

attr_accessor :name, :age

end

thus they can later be accessed this way

john.age = 19

print john.age

Aha! I see now, and it does make more sense. Yes, using inspect you
could surely emit for example skeletons for the various methods, with
e.g. a 'pass' in lieu of their code. However, since instance attributes
are determined by code that gets executed (in __init__, and maybe in
__new__ and even elsewhere), it's not really practical to find out what
attributes an instance would have without in fact creating such an
instance and introspecting on it. Would such instantiation be OK here?

If I understand you right, it would be ok.

The requirements are given by the template:

john.sayYourClassDefinition()

"john" is instantiated, when asked for his class definition.
In some cases instantiating a class might have externally visible
effects, say opening a network connection, or a database, etc, so you
might well want to forbid that for purely introspective purposes.

see above.
Yes, but also mean that immutable objects are not really immutable, only
"immutable until further notice". For example, the immutability of
[...] - (elaborations)

I understand now.

=> {New Requirement: ability to declare objects as immmutable or
mutable/immutable}
Nope: both forms have IDENTICAL semantics. They both work in just the
SAME way.

Try it out...!

But this means that "assignment of metadata" works fine.
...


class metainfo

thus if I make a typo, I create a new attribute?

..
 
X

Xavier Morel

Ilias said:
I estimate that there is a "unfreeze" operation, too - which would lead
to flexibity.
There is none, you have to make a copy of the object via the "dup"
(duplicate) method to get an unfrozen copy (note: clone yields an exact
copy, which means that it's still frozen).

Unfreezing an object is forbidden in Ruby.

Alex said:
> At the other extreme, Ruby's very productive choice is to
> allow freeze and unfreeze of everything (I believe -- but you should
> double check with a Ruby expert)
I'm no ruby expert, but I'm pretty sure there is no way to unfreeze a
frozen ruby object, you *have* to create a molten copy with the "dup"
method.

Ilias said:
>> Ilias Lazaridis <[email protected]> wrote:
> [...] - google stuff
>
>> One normally does not define large numbers of identical accessors (there
> [...] - (extensive elaboration)
>
> possibly one can provide the code for something similar to the ruby
> attr_accessor:
>
> class Talker
> def sayHello
> puts "Hello world"
> end
>
> attr_accessor :name, :age
>
> end
>
> thus they can later be accessed this way
>
> john.age = 19
>
> print john.age
>
There is no point, these exist because a ruby attribute can *never* be
accessed from outside the object, a Ruby attribute is always private
while a Python attribute is always public. This means that you *have to*
declare properties to have the ability to access an attribute of a Ruby
object, which lead to attr_accessor, attr_reader and attr_writer as
shortcut-declarations of basic properties.

The Pythonic equivalent of Ruby's attr_accessor is merely to do nothing,
because what the attr_accessor does is:

attr_accessor :something
generates
def something
@something
end
def something= value
@something = value
end

but not doing it would prevent any access to the "something" attribute.
(attr_reader only declares the getter method, making the attribute
read-only, and attr_writer only defines the setter, making the attribute
write-only)

One thing that is very important is that in Ruby you *never* deal with
member attributes from outside the object, only methods (messages to the
object).
In Python, you deal either with methods (messages) or attributes
(datas), but these attributes can be either "real" attributes (real
unchecked data) or properties, e.g. virtual attributes (that may
generate side-effects, sanity check on the data, or _may not map to any
existing unique data in the object_) and unless you really try to, you
don't have any way to distinguish a "real" attribute from a property
("virtual" attribute), and you don't care.
>
> thus if I make a typo, I create a new attribute?
>
Why yes of course, what were you expecting?
 
S

Steven D'Aprano

Or some even more stringent qualification, such as the state's Bar exam
for lawyers -- you may not be able to sit for that exam w/o the
appropriate degree, but the degree by itself is not enough, you still
have to pass the exam. It is that way for Engineers in Italy (I passed
my State Exam in the early '80s), although you only need the certificate
for some specific professional undertakings (e.g. design a ship, or a
large building, or technically supervise building operations beyond a
certain size -- not to write software or to design chips).

Personally, I agree with the theory, first expressed by Adam Smith, that
such barriers to entry are mostly useful to grant practitioners of a
certain profession the "scarcity value" that lets them charge higher
prices, although of course they're always presented as "good for
society". Note that in Europe in the Middle Ages you needed strict
qualifications of that kind for just about anything -- you could not
make hats unless you belonged to the Hatters' Guild, etc; most of those
restrictions have since been lifted, but a few groups (doctors, lawyers,
accountants, ...) have managed to keep them in place.

Let's not confuse the medieval guild system with today's system. Guilds
were more like clubs than professional bodies: it was who you knew, rather
than what you knew, that decided whether you got in. You were forbidden
from becoming (say) a hat maker unless the other hat makers allowed you to
join the guild. There was no independent, or even semi-independent, body
who decided what qualifications were needed to make hats. It was all about
who you knew -- if your uncle's best friend was a hat maker, you could be
apprenticed to a hat maker and join the guild, otherwise there was no exam
to sit that got you in, no matter how talented you were.

This system combined the worst of all outcomes: you got artificial
scarcity with the monopoly pricing that leads to, *plus* it failed to
enforce or even encourage minimum standards of skill and strategy.

By contrast, today's professional bodies like law, medicine etc. have
independent standards of skill that must be met. I don't wish to deny
that knowing the right people can help smooth the procedure of becoming
a doctor, lawyer, etc., but failing to have an uncle who is a lawyer is no
barrier to becoming a lawyer, provided you can pass the bar exam. That is
very different from the guild system.

In general, professional bodies like engineers, doctors, etc. do a
reasonable job of enforcing minimum standards of skill and quality.
Certainly there are a lot fewer building collapses in countries that
enforce building standards than countries that allow the free market to
decide.

Free market radicals like to sneer at "for the good of society" arguments,
but the problem with their reasoning is that they only consider the
monetary cost of hiring a professional, and not the other costs. Of course
anything that makes professionals scarce will increase the cost of hiring
that professional. But they fail to take into account the externalities
that come from increasing the numbers of under-qualified, shoddy
professionals.

The free market often works well for (say) enforcing minimum standards for
bread: anyone who can taste can recognise good bread from bad, and if you
buy bad bread from a baker today you simply will go to another baker
tomorrow. But dealing with accountants, lawyers, doctors etc. is very
different. Expert opinions are not like bread: only a fellow expert can
recognise good advice from bad advice. Most people buy bread at least once
a week, but might only get legal advice once or twice in their life. Under
these circumstances, Adam Smith's Invisible Hand is feeble indeed, and
shonky rip-off merchants and incompetents thrive, harming everyone.

That's not to say that skilled experts can't make a living -- in an
economy filled with snake-oil medical practitioners, good experts who
get a good reputation can charge a high premium. People who find a
good doctor or lawyer will recommend him to their friends. This squeezes
out the middle: new, but skilled, experts get lost in the sea of shonkies,
but the tiny minority that manage to get a reputation will attract
near-monopoly pricing. That leads to a two-tier system where only the rich
and powerful can afford good experts, be they doctors, lawyers, engineers
or accountants, and everyone else either goes without or are forced into a
lottery where the vast majority of experts they can afford are incompetent.

Another major difference between today's professional bodies and medieval
guilds is that the scarcity is not entirely (or even mostly) caused by
the professional body. It is the universities controlling prerequisite
degrees that gain more from the scarcity: within reason, the fewer places
they offer for (say) law degrees, the higher fees they can charge for
them. In my inexpert opinion, the cause of shortages of experts is more
the fault of the universities than of the professional bodies.
 
A

Alex Martelli

Ilias Lazaridis said:
One normally does not define large numbers of identical accessors (there
[...] - (extensive elaboration)

possibly one can provide the code for something similar to the ruby
attr_accessor:

class Talker
def sayHello
puts "Hello world"
end

attr_accessor :name, :age

end

thus they can later be accessed this way

john.age = 19

print john.age

Yes: the amount of code one needs to provide for this purpose is, NONE.

class Talker(object):
pass

You can now write

john = Talker()
john.age = 19
print john.age

just as you request. _No need to provide ANY code in the class_.

If you want an initial/default value for the .age parameter, you'll want
to add an __init__ setting it (otherwise, trying to print john.age
WITHOUT having previously set john.age will produce an AttributeError --
I wish all languages were so nice as to similarly let me know about
attempts to use uninitialized variables/attributes!-).

But still, you don't need any accessor methods -- setters and getters --
unless there's something "real" that you want code to perform upon any
setting and/or getting of an attribute (when there IS something special
of that kind, and only then, you code setter and/or getter and use
property, but then it won't be REPETITIVE [boilerplate] code, because
there WILL be something special in those methods you're coding...).

BTW, all I say about setting and getting an attribute also applies to
REMOVING an attribute, aka DELETING it. Dunno why nobody ever seems to
think about it, but it should be possible to have some attributes that
are optional, and thus to remove them if/when they're not needed any
more.

Let me give an example: say that anybody under 18 must have a legal
guardian, but there is no concept of legal guardian for anybody who is
18 or over; then, you might have:

class Person(object):
def __init__(self, name, parent):
# the person is born: age 0, given name, legal guardian is parent
self._age = 0
self.name = name
self.guardian = parent
def getAge(self):
return self._age
def setAge(self, age):
if hasattr(self, 'guardian') and age>=18: del self.guardian
self._age = age
age = property(getAge, setAge)

This is a good example of a case in which you need to run some code when
john.age is set, because if it's set to 18 or over you want to remove
john's attribute defining his legal guardian -- so, you use a property.
You don't need properties for the name and guardian attributes, because
so far at least we have not specified any code that needs to run when
those attributes are gotten, set, or deleted; if and when the specs
changes, you can change the definition of the class and NOT change any
client-code, because the client code still uses the attributes in
exactly the same way, e.g. "john.age += 1", whether a corresponding
property is defined, or not.

If I understand you right, it would be ok.

The requirements are given by the template:

john.sayYourClassDefinition()

"john" is instantiated, when asked for his class definition.

OK, I'll look into that in my copious spare time (unless somebody else
does the work first;-).

But this means that "assignment of metadata" works fine.

Yes, on mutable objects such as ordinary classes it does work fine. It
would not work on immutable objects such as strings or numbers.

thus if I make a typo, I create a new attribute?

Yep, just like, I believe, in Ruby; if you meant to assign to @zappo but
happen to mistakenly assign to @zippo instead, you've created a new
attribute. In Python, if you wish, you can check for such "oops"-level
errors by using tools such as pychecker or pyLint, which of course can
also check for other "oopses" besides "variable/attribute created once
but never used nor referenced". I've seen some people program their
editors to routinely run pychecker when saving files with a .py
extension, for example.

Personally, I don't generally bother with pychecker (even though its
most excellent author is my friend and colleague Neal Norwitz) because
compilers and lint tools just can't catch ALL of my "oopses" -- for
example, if I meant to write count+=1 but made a typo and wrote count-=1
instead, no checker/lint/compiler is gonna catch it for me. So, I have
learned that I need to write *UNIT-TESTS* for all of my code -- me and
another million programmers. Of course, unit tests, which are
indispensable anyway to catch the += vs -= typoes, as a side effect also
catch any typos such as zippo vs zappo. Very good expansions on these
fundamental ideas can be seen at
<http://www.mindview.net/WebLog/log-0025> and
<http://www.artima.com/weblogs/viewpost.jsp?thread=4639>, by excellent
authors Bruce Eckel and Robert Martin respectively (great experts of
such languages as Java and C++, but aficionados of Python, Ruby,
Smalltalk thanks to these considerations).


Alex
 
B

Brian van den Broek

Steven D'Aprano said unto the world upon 03/01/06 07:33 PM:
Let's not confuse the medieval guild system with today's system. Guilds
were more like clubs than professional bodies: it was who you knew, rather
than what you knew, that decided whether you got in. You were forbidden
from becoming (say) a hat maker unless the other hat makers allowed you to
join the guild. There was no independent, or even semi-independent, body
who decided what qualifications were needed to make hats. It was all about
who you knew -- if your uncle's best friend was a hat maker, you could be
apprenticed to a hat maker and join the guild, otherwise there was no exam
to sit that got you in, no matter how talented you were.

By contrast, today's professional bodies like law, medicine etc. have
independent standards of skill that must be met. I don't wish to deny
that knowing the right people can help smooth the procedure of becoming
a doctor, lawyer, etc., but failing to have an uncle who is a lawyer is no
barrier to becoming a lawyer, provided you can pass the bar exam. That is
very different from the guild system.

Another major difference between today's professional bodies and medieval
guilds is that the scarcity is not entirely (or even mostly) caused by
the professional body. It is the universities controlling prerequisite
degrees that gain more from the scarcity: within reason, the fewer places
they offer for (say) law degrees, the higher fees they can charge for
them. In my inexpert opinion, the cause of shortages of experts is more
the fault of the universities than of the professional bodies.

According to the 2000 US Census, in a population of 174,136,341 people
between 18 and 65
<http://factfinder.census.gov/servle...me=DEC_2000_SF1_U_DP1&-ds_name=DEC_2000_SF1_U>,
there were a total of 862,037 lawyers
<http://factfinder.census.gov/servle...me=DEC_2000_SF4_U_PCT205&-format=&-CONTEXT=dt>
among the employed people 16 years and older.

So, just shy of 1 out of every 200 working-aged people in the USA were
lawyers in 2000.

I'm inclined to agree with the claim that law schools don't have the
correct number of seats, but I think we might just differ on which way
the adjustment should go :)

(I do realize that US data isn't most pertinent to Steven, Alex or
myself -- au, it, ca -- but it is ready to hand. Shamefully, my
government wants to charge me for the occupation data, and Steven's
didn't yield free data before my patience and resolve wore out.)

Best,

Brian vdB
 
A

Aahz

[much stuff deleted that I mostly agree with to get at an interesting
chunk of disagreement]

By contrast, today's professional bodies like law, medicine etc. have
independent standards of skill that must be met. I don't wish to deny
that knowing the right people can help smooth the procedure of becoming
a doctor, lawyer, etc., but failing to have an uncle who is a lawyer is no
barrier to becoming a lawyer, provided you can pass the bar exam. That is
very different from the guild system.

Unfortunately, this isn't quite true. Medicine and law both require the
passing of an apprenticeship, so there's still some room for favoritism
and blackballing.
--
Aahz ([email protected]) <*> http://www.pythoncraft.com/

"Given that C++ has pointers and typecasts, it's really hard to have a
serious conversation about type safety with a C++ programmer and keep a
straight face. It's kind of like having a guy who juggles chainsaws
wearing body armor arguing with a guy who juggles rubber chickens wearing
a T-shirt about who's in more danger." --Roy Smith
 
D

David T

Steven D'Aprano said unto the world upon 03/01/06 07:33 PM:





So, just shy of 1 out of every 200 working-aged people in the USA were
lawyers in 2000.

I'm inclined to agree with the claim that law schools don't have the
correct number of seats, but I think we might just differ on which way
the adjustment should go :)

<snip>

My professional body (The American Chemical Society, no, I'm not a
professional programmer), takes the opinion that the more chemists
there are in the world the better. It keeps labor costs down.

That's what happens when the professional bodies are controlled by
large corporate interests. So, meanwhile, I sit at home and wait for
the "undersupply" of scientists to correct itself. I have visions of
starting my own manufacturing company, but it's hard to find capital.
There are way too many dead and dying small companies around Michigan
and way too many unemployed scientists and engineers, and lots of us
have great ideas for companies.

Fortunately, this gives me plenty of time to learn Python and write
code to simulate and optimize my manufacturing process!

:--David
 
M

Michael Sparks

....
Sorry to reply to the thread so late in the day, but I noticed (via
QOTW :-( ) that Anton got worked up at me suggesting that congratulating
someone with a new job was a nice idea (surprised me too - all the
Google employees I've met have been very nice people), read the
thread (got sad) and then saw this:
Who is "the man"? If Google were to hire you with no experience, would
you then have "worked for the man"?

If you want to understand the reference, the most fun recent
explanation I've seen is in "School of Rock". Jack Black's character
explains it far better than I ever could :)

Regards,


Michael.
--
(e-mail address removed), http://kamaelia.sourceforge.net/
British Broadcasting Corporation, Research and Development
Kingswood Warren, Surrey KT20 6NP

This message (and any attachments) may contain personal views
which are not the views of the BBC unless specifically stated.
 
I

Ilias Lazaridis

Alex said:
possibly one can provide the code for something similar to the ruby
attr_accessor:

class Talker
def sayHello
puts "Hello world"
end

attr_accessor :name, :age

end

thus they can later be accessed this way

john.age = 19

print john.age
[...]
Yes: the amount of code one needs to provide for this purpose is, NONE. [...]
just as you request. _No need to provide ANY code in the class_.
[...] - (elaborations on getter/setter need)

I've understood your elaborations.

I would need this python "attr_accessor", to showcase that python is
capable to do it (even if the usage seems irrational/redundant).
OK, I'll look into that in my copious spare time (unless somebody else
does the work first;-).

would be very nice!
"assign to it" with:

setattr(Talker, 'meta', "Class meta information") [...]
Talker.meta = "Class meta information"
[...]
But this means that "assignment of metadata" works fine.

Yes, on mutable objects such as ordinary classes it does work fine. It
would not work on immutable objects such as strings or numbers.
ok
thus if I make a typo, I create a new attribute?

Yep, just like, I believe, in Ruby; if you meant to assign to @zappo but
happen to mistakenly assign to @zippo instead, you've created a new
attribute. In Python, if you wish, you can check for such "oops"-level
[...] - (elaborations on code-verification / unit-testing)

Ok.

..
 
A

Anton Vredegoor

Michael said:
Sorry to reply to the thread so late in the day, but I noticed (via
QOTW :-( ) that Anton got worked up at me suggesting that congratulating
someone with a new job was a nice idea (surprised me too - all the
Google employees I've met have been very nice people), read the
thread (got sad) and then saw this:

Strange, I *did* get worked up at you some (long) time ago, probably
without you ever noticing, it was about your pythagorean proof about
anyone being able to learn kamelia quickly because you had some student
who had no problems whatsoever with it,

http://www.atopia.tk/anamnesis/kittleren.htm

while I could not find a clue what your software was supposed to do,
except being something very exciting (and I think I know python well
enough to not often have that problem), but I did not get worked up at
you because of you congratulating Guido. To make it more clear :
Congratulations Guido, with your new job!

However I still maintain that I was never able to meet these fine
people you speak about and which you seem to know because the cost
involved (a few hundred euro to visit pycon for example) was too high
compared to my food budget.

What I was trying to explain was that the world gets closer and closer
to the singularity (something I believe other people got worked up at
*me* about) and the world is now at the stage where everything gets
reinvented in about 5 years. This means that during the next 2.5 years
some phenomenon like Google changing internet history (which happened
in the last five years) will happen again (in a shorter time period).

Since we are going to reinvent ourselves faster and faster there is no
need to take old corrupt methods with us into the future. Some people
(maybe suffering from a wierd kind of conceptual static typing) equate
me talking about 'people having a backstabbing history' with implying
those exact people actually stab other people in the back literally.
'Having a backstabbing history' can also be explained as having been
into places where backstabbing was common, without implying any actual
backstabbing of these people themselves, except maybe the greater whole
they belonged to being instrumental to backstabbing, for example
mathemathics professors helping to crack codes which are used to
decipher or encode messages in a war situation.

I've already mentioned somewhere that I meant it figuratively and I
will further qualify it now by saying that Googles selection process
does them and the world a disservice by using some kind of elitist
criteria, or by hiring people who can prove that they -forced by market
mechanisms no doubt- have in the past collaborated with or worked for
companies that selected them on the basis of elitist criteria.

The elitist selection process suffers from the same problems that for
example IQ-tests have: Trying to scale something in one (or at least
too few) dimensions, where that something is very, very, very
multidimensional. It is insulting, inappropriate and denigrating when
applied to humans. Of course, irony will have people using elitist
selection processes being very personally offended by people pointing
out that it is unfair. Just like those who stole my money and job
opportunities now claim I'm a parasite or some wrathfull loser who
doesn't want to work.

In fact, although I still don't condone corruption, my vision has
cleared up a lot by not eating its fruits anymore. I am not jealous at
anyone still 'inside' and although I dream about trying to save people
from places where people get murdered randomly (the dreaded
backstabbing reference again), it gets less and less often as time goes
by. My current worry is about how I can survive for another 2.5 years
(the projected time period for the world to renew itself again) without
me jumping off a ledge because I dont want to be (and possibly live
forever without dying) with irrational people who don't deserve my
company or get offed by someone who can't stand me not joining
corruption.

It's possible some next generation of people don't want the current
population to survive because they are not advancing fast enough or
lack compassion with lower lifeforms. If anything, my post was about
trying to save the world from that fate by trying to induce that
compassion into this newsgroup. I think it would be beneficial for our
people working for google (and for google itself) if they freed
themselves of any remaining corruption so that world evolution will be
synchronized with social evolution and no unnecessary tensions will
occur.

Anton

'workout'
 
A

Alex Martelli

Ilias Lazaridis said:
...
I would need this python "attr_accessor", to showcase that python is
capable to do it (even if the usage seems irrational/redundant).

The code for it was in one of my previous posts, in more than one form,
but here it is again, more or less:

def add_silly_attr_accessor_to_a_class(klass, name):
def get(self, name): return getattr(self, '_'+name)
def set(self, name, value): return setattr(self, '_'+name, value)
setattr(klass, name, property(get, set))

to be used as in:

add_silly_attr_accessor_to_a_class(Talker, 'name')
add_silly_attr_accessor_to_a_class(Talker, 'age')

outside of the body of Talker. If you'd rather have it used INSIDE the
body of Talker, then:

def make_silly_attr_accessor(name):
def get(self, name): return getattr(self, '_'+name)
def set(self, name, value): return setattr(self, '_'+name, value)
return property(get, set)

to be used as in:

class Talker(object):
age = make_silly_attr_accessor('age')
name = make_silly_attr_accessor('name')

Finally, you could choose to use a decorator syntax instead:

def silly_attr_accessor_via_decorator(f):
name = '_'+f.__name__
def get(self, name): return getattr(self, '_'+name)
def set(self, name, value): return setattr(self, '_'+name, value)
return property(get, set)

to be used as in:

class Talker(object):
@silly_attr_accessor_via_decorator
def name(): pass
@silly_attr_accessor_via_decorator
def age(): pass

The latter is arguably a stretching of the concept of decorator, which
is meant to be a nice syntax for a higher-order-function (taking a
function as its argument and returning another function built by
modifying the argument one) -- here we're only using the *name* of the
"function" (name or age) and ignoring the object entirely (which is why
in the example use I'm defining the ``functions'' as empty, using the
no-op statement ``pass''). Still, some people believe there is mystical
and magical power in having special syntax for something rather than
using perfectly normal, general, and existing syntax for the purpose;
such syntax-obsessed people will no doubt be more impressed by seeing
the "special syntax" in use, than by ordinary, bread-and-butter
closures, properties and assignment statements...;-)


Alex
 
A

Alex Martelli

Brian van den Broek said:
(I do realize that US data isn't most pertinent to Steven, Alex or
myself -- au, it, ca -- but it is ready to hand. Shamefully, my

Actually, I've been living in the US for over 9 months now, and like all
immigrants I have more dealings with lawyers &c than the average Joe,
so, it IS quite pertinent to my daily life, thanks;-)


Alex
 

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

Forum statistics

Threads
473,780
Messages
2,569,608
Members
45,241
Latest member
Lisa1997

Latest Threads

Top