Another newbie question

X

Xavier Morel

Mike said:
And you've once again missed the point. The reason you don't
manipulate the attributes directly is because it violates
encapsulation, and tightens the coupling between your class and the
classes it uses. It means you see the implementation details of the
classes you are using, meaning that if that changes, your class has to
be changed to match.
One of Python's greatnesses is that a property is, for all means an
purposes, a fully virtual instance attribute/member.

If you follow the KISS principle, as long as your (naive? probably)
implementation of the class has "real" attributes and their manipulation
is meaningful & makes sense from an external point of view, just leave
it at that. If you happen to change the implementation for whatever
reason and happen to remove the real attributes, just create virtual
attributes with a property and be done with it.

Wrapping everything just because you can and considering that
encapsulation is only truly done if you never happen to touch what's
under the hood (even without knowing it) is the Java Way, this is Python.

In Python, the interface of an object instance is always virtualized
because you can never know if you're manipulating "real" attributes or
property-spawned virtual attributes. _that_, in my opinion, is not
knowing about the implementation details.

While a Java object is mineral (set in stone) and trying to abstract
everything from the start (and generate 50 pages documentation for each
class to be sure that you didn't miss anything) kind of makes sense, a
Python object is an organic, morphing, living entity. Don't abstract
everything and over-engineer from the start just because you can and
because you'd do it in Java or C#, only abstract (from your point of
view) when you *have to*. And remember that "Java's Object Oriented
Programming" is not the only one worth using, even though some people
would like to make you believe it.
 
S

Steven D'Aprano

Given that we're talking about API design, then yes, you do. With only
partial examples, you'll only get partial conclusions.

I think you and I are coming at this problem from different directions.

To my mind, the Coordinate class was complete in potentia, and did not
need to be listed because I was not operating on a Coordinate instance as
a whole. If you go back and look at my example, I was explicit about
wanting to do something with a single ordinate, not a coordinate pair.
Listing the entire API for the Coordinate class would be a waste of time,
since I wasn't concerned about Coordinate.rotate(), Coordinate.reflect()
or any other method applying to a coordinate pair.

In particular,
you can get most of your meaningless methods out of a properly
designed Coordinate API. For example, add/sub_x/y_ord can all be
handled with move(delta_x = 0, delta_y = 0).

Here is my example again:

Then, somewhere in my application, I need twice the
value of the y ordinate. I would simply say:

value = 2*pt.y
[end quote]

I didn't say I wanted a coordinate pair where the y ordinate was double
that of the original coordinate pair. I wanted twice the y ordinate, which
is a single real number, not a coordinate pair.


[snip]
And you've once again missed the point. The reason you don't
manipulate the attributes directly is because it violates
encapsulation, and tightens the coupling between your class and the
classes it uses. It means you see the implementation details of the
classes you are using, meaning that if that changes, your class has to
be changed to match.

Yes. And this is a potential problem for some classes. The wise programmer
will recognise which classes have implementations likely to change, and
code defensively by using sufficient abstraction and encapsulation to
avoid later problems.

The not-so-wise programmer takes abstraction as an end itself, and
consequently spends more time and effort defending against events which
almost certainly will never happen than it would have taken to deal with
it if they did.

Do you lie awake at nights worrying that in Python 2.6 sys.stdout will be
renamed to sys.standard_output, and that it will no longer have a write()
method? According to the "law" of Demeter, you should, and the writers of
the sys module should have abstracted the fact that stdout is a file away
by providing a sys.write_to_stdout() function.

That is precisely the sort of behaviour which I maintain is unnecessary.


No, it requires you to actually *think* about your API, instead of
just allowing every class to poke around inside your implementation.

But I *want* other classes to poke around inside my implementation.
That's a virtue, not a vice. My API says:

"In addition to the full set of methods which operate on the coordinate as
a whole, you can operate on the individual ordinates via instance.x and
instance.y which are floats."

Your API says:

"In addition to the full set of methods which operate on the coordinate as
a whole, you can operate on the individual ordinates via methods add_x,
add_y, mult_x, mult_y, sub_x, sub_y, rsub_x, rsub_y, div_x, div_y, rdiv_x,
rdiv_y, exp_x, exp_y, rexp_x, rexp_y...; the APIs of these methods are: ... "

My class is written, tested and complete before you've even decided on
your API. And you don't even really get the benefit of abstraction: I have
two public attributes (x and y) that I can't change without breaking other
people's code, you've got sixteen-plus methods that you can't change
without breaking other people's code.

(It goes without saying that these are in addition to the full set of
methods which operate on the coordinate as a whole -- our classes are
identical for those.)

The end result is that your code is *less* abstract than mine: your code
has to specify everything about ordinates: they can be added, they can be
subtracted, they can be multiplied, they can be printed, and so on. That's
far more concrete and far less abstract than mine, which simply says
ordinates are floats, and leave the implementation of floats up to Python.
 
A

Antoon Pardon

On Sat, 10 Dec 2005 01:28:52 -0500, Mike Meyer wrote:

The not-so-wise programmer takes abstraction as an end itself, and
consequently spends more time and effort defending against events which
almost certainly will never happen than it would have taken to deal with
it if they did.

Do you lie awake at nights worrying that in Python 2.6 sys.stdout will be
renamed to sys.standard_output, and that it will no longer have a write()
method? According to the "law" of Demeter, you should, and the writers of
the sys module should have abstracted the fact that stdout is a file away
by providing a sys.write_to_stdout() function.

I find this a strange interpretation.

sys is a module, not an instance. Sure you can use the same notation
and there are similarities but I think the differences are more
important here.
That is precisely the sort of behaviour which I maintain is unnecessary.




But I *want* other classes to poke around inside my implementation.
That's a virtue, not a vice. My API says:

"In addition to the full set of methods which operate on the coordinate as
a whole, you can operate on the individual ordinates via instance.x and
instance.y which are floats."

Yikes. I would never do that. Doing so would tie my code unnecesary
close to yours and would make it too difficult to change to an
other class with a different implementation like one using tuples or
lists instead of a seperate x and y instances.
Your API says:

"In addition to the full set of methods which operate on the coordinate as
a whole, you can operate on the individual ordinates via methods add_x,
add_y, mult_x, mult_y, sub_x, sub_y, rsub_x, rsub_y, div_x, div_y, rdiv_x,
rdiv_y, exp_x, exp_y, rexp_x, rexp_y...; the APIs of these methods are: ... "

Who in heavens name would need those? Maybe there is no x or y because
the implementation uses a list or a tuple, maybe the implementation
uses polar coordinates because that is more usefull for the application
it was planned for.

Sure a way to unpack your coordinate in a number of individual
ordinate variables could be usefull for when you want to manipulate
such an individual number.
My class is written, tested and complete before you've even decided on
your API. And you don't even really get the benefit of abstraction: I have
two public attributes (x and y) that I can't change without breaking other
people's code, you've got sixteen-plus methods that you can't change
without breaking other people's code.

No he would have none.
 
A

Alex Martelli

Mike Meyer said:
Of course you can do such things. But it's a silly thing to do. That

I guess this is the crux of our disagreement -- much like, it seems to
me, your disagreement with Xavier and Steven on the other half of this
thread, as I'll try to explain in the following.
invariant should be written as x > 23 for the class bar is an instance

Let's, for definiteness, say that bar is an instance of class Bar. Now,
my point is that absolutely not all instances of Bar are constrained to
always have their x attribute >23 -- in general, their x's can vary all
over the place; rather, the constraint applies very specifically to this
one instance of Bar -- the one held by foo (an instance of Foo) as foo's
attribute bar.

Let's try to see if I can make a trivially simple use case. Say I'm
using a framework to model statical structures in civil engineering.
I have classes such as Truss, Beam, Pier, Column, Girder, and so forth.

So in a given structure (class Foo) I might have a certain instance of
Beam, attribute beam1 of instances of Foo, which carries a certain load
(dependent on the overall loads borne by each given instance of Foo),
and transfers it to an instance of Pier (attribute pier1 of instances of
Foo) and one of Girder (attribute girder1 ditto).

Each of these structural elements will of course be able to exhibit as
attributes all of its *individual* characteristics -- but the exact
manner of *relationship* between the elements depends on how they're
assembled in a given structure, and so it's properly the business of the
structure, not the elements.

So, one invariant that had better hold to ensure a certain instance foo
of Foo is not about to crash, may be, depending on how Foo's detailed
structual geometry is, something like:

foo.beam1.force_transferred_A <= foo.pier1.max_load_top AND
foo.beam1.force_transferred_B <= foo.girder1.max_load_A

The natural place to state this invariant is in class Foo, by expressing
'foo' as 'self' in Python (or omitting it in languages which imply such
a lookup, of course).

If I'm not allowed (because you think "it's silly"!) to express a class
invariant in terms of attributes of the attributes of an instance of
that class, I basically have to write tons of boilerplate, violating
encapsulation, to express what are really attributes of attributes of
foo "as if" they were attributes of foo directly, e.g.

def beam1_force_transferred_A(): return beam1.force_transferred_A

(or other syntax to the same purpose). After going through this
pointless (truly silly) exercise I can finally code the invariant as

self.beam1_force_transferred_A <= self.pier1_max_load_top AND

(etc). Changing a lot of dots into underscores -- what a way to waste
programmer time! And all to NO advantage, please note, since:
of. Invariants are intended to be used to check the state of the
class, not the state of arbitary other objects. Doing the latter
requires that you have to check the invariants of every object pretty
much every time anything changes.

....in the end the invariant DOES have to be checked when anything
relevant changes, anyway, with or without the silly extra indirection.

But besides the wasted work, there is a loss of conceptual integrity: I
don't WANT Foo to have to expose the internal details that beam1's
reference point A transfers the force to pier1's top, etc etc, elevating
all of these internal structural details to the dignity of attributes of
Foo. Foo should expose only its externally visible attributes: loads
and forces on all the relevant points, geometric details of the
exterior, and structural parameters that are relevant for operating
safety margins, for example.

The point is that the internal state of an object foo which composes
other objects (foo's attributes) beam1, pier1, etc, INCLUDES some
attributes of those other objects -- thus stating that the need to check
those attributes' relationships in Foo's class invariant is SILLY,
strikes me as totally out of place. If the state is only of internal
relevance, important e.g. in invariants but not to be externally
exposed, what I think of as very silly instead is a style which forces
me to build a lot of "pseudoattributes" of Foo (not to be exposed) by
mindless delegation to attributes of attributes.

Invariants are a tool. Used wisely, they make finding and fixing some
logic bugs much easier than it would be otherwise. Used unwisely, they
don't do anything but make the code bigger.

I disagree, most intensely and deeply, that any reference to an
attribute of an attribute of self in the body of an invariant is
necessarily "unwise".
Not all invariants, pre-conditions or post-conditions can be
expressed.

Not all can be sensibly CHECKED, but most definitely all can be
EXPRESSED. Another one of my long-standing contentions with Eiffel is
the inability to express invariants (and pre- and post- conditions)
because the compiler is unable to figure out a decent way to check them;
Z and the VDL, just to name very old design languages, show easy ways to
allow full expression. Of course, if a condition is of the form, say,
"all items of potentially infinite iterable X satisfy predicate P", it
may not be runtime-checkable -- big furry deal, I want to be able to
EXPRESS it anyway, because apart from runtime checking there are other
precious uses of such conditions (e.g., the compiler might be able to
DEDUCE from such a condition some important optimization, or the
compiletime proof of other assertions, when run in the appropriate
mode).

But that has little to do with the use case I covered here. Surely
you're not claiming that the structural invariants showing that foo
isn't about to crash *can't be expressed*, just because the obvious way
to express them violates your stylistic preferences (which I find
totally unjustified in this case) and the way that meets your style
preferences, without offering any advantages, requires lots of pesky
useless boilerplate?! That's not the DEFINITION of "can't"!-)
I think Eiffel is fairly pure. But practicality beats purity, so there
are places where it has to give in and deviate from it's
principles. Clearly, you don't agree with the underlying
philosoiphy. So don't use it.

I don't, but I also occasionally take the time to explain, as I've done
here, where it (or, in this case, a specific style you claim it requires
-- not using attributes of attributes in a class invariant -- I'd
appreciate URLs to where Meyers dictates this restriction, btw)
interferes with purity, practicality, or both.


Alex
 
M

Mike Meyer

Steven D'Aprano said:
In particular,
you can get most of your meaningless methods out of a properly
designed Coordinate API. For example, add/sub_x/y_ord can all be
handled with move(delta_x = 0, delta_y = 0).

Here is my example again:

Then, somewhere in my application, I need twice the
value of the y ordinate. I would simply say:

value = 2*pt.y
[end quote]

I didn't say I wanted a coordinate pair where the y ordinate was double
that of the original coordinate pair. I wanted twice the y ordinate, which
is a single real number, not a coordinate pair.

Here you're not manipulating the attribute to change the class -
you're just using the value of the attribute. That's what they're
there for.
Yes. And this is a potential problem for some classes. The wise programmer
will recognise which classes have implementations likely to change, and
code defensively by using sufficient abstraction and encapsulation to
avoid later problems.

Except only the omniscennt programmer can do that perfectly. The
experienced programmers knows that requiments change over the lifetime
of a project, including things that the customer swears on a stack of
holy books will never change.
Do you lie awake at nights worrying that in Python 2.6 sys.stdout will be
renamed to sys.standard_output, and that it will no longer have a write()
method? According to the "law" of Demeter, you should, and the writers of
the sys module should have abstracted the fact that stdout is a file away
by providing a sys.write_to_stdout() function.
That is precisely the sort of behaviour which I maintain is unnecessary.

And that's not the kind of behavior I'm talking about here, nor is it
the kind of behavior that the LoD is designed to help you with (those
are two different things).
But I *want* other classes to poke around inside my implementation.
That's a virtue, not a vice. My API says:

"In addition to the full set of methods which operate on the coordinate as
a whole, you can operate on the individual ordinates via instance.x and
instance.y which are floats."

That's an API which makes changing the object more difficult. It may
be the best API for the case at hand, but you should be aware of the
downsides.
Your API says:

Actually, this is *your* API.
"In addition to the full set of methods which operate on the coordinate as
a whole, you can operate on the individual ordinates via methods add_x,
add_y, mult_x, mult_y, sub_x, sub_y, rsub_x, rsub_y, div_x, div_y, rdiv_x,
rdiv_y, exp_x, exp_y, rexp_x, rexp_y...; the APIs of these methods are: ... "

That would be a piss-poor API design. Any designer who knows what they
are doing should be able to turn out a better API than that given a
reasonable set of real-world requirements.
My class is written, tested and complete before you've even decided on
your API. And you don't even really get the benefit of abstraction: I have
two public attributes (x and y) that I can't change without breaking other
people's code, you've got sixteen-plus methods that you can't change
without breaking other people's code.
The end result is that your code is *less* abstract than mine: your code
has to specify everything about ordinates: they can be added, they can be
subtracted, they can be multiplied, they can be printed, and so on. That's
far more concrete and far less abstract than mine, which simply says
ordinates are floats, and leave the implementation of floats up to Python.

Again, this is *your* API, not mine. You're forcing an ugly, obvious
API instead of assuming the designer has some smidgen of ability. I've
already pointed out one trivial way to deal with this, and there are
others.

<mike
 
M

Mike Meyer

I guess this is the crux of our disagreement -- much like, it seems to
me, your disagreement with Xavier and Steven on the other half of this
thread, as I'll try to explain in the following.
Let's, for definiteness, say that bar is an instance of class Bar. Now,
my point is that absolutely not all instances of Bar are constrained to
always have their x attribute >23 -- in general, their x's can vary all
over the place; rather, the constraint applies very specifically to this
one instance of Bar -- the one held by foo (an instance of Foo) as foo's
attribute bar.

Well, the hard-core solution is to note that your class doesn't really
deal with the type Bar, but deals with a subtype of Bar for which x >
23 in all cases. Since types are represented by classes, you should
subclass Bar so you have a class that represents this subtype. The
class is trivial (with Eiffel conventions):

class RESTRICTED_BAR
inherits BAR
invariant x > 23
END
So, one invariant that had better hold to ensure a certain instance foo
of Foo is not about to crash, may be, depending on how Foo's detailed
structual geometry is, something like:

foo.beam1.force_transferred_A <= foo.pier1.max_load_top AND
foo.beam1.force_transferred_B <= foo.girder1.max_load_A

The natural place to state this invariant is in class Foo, by expressing
'foo' as 'self' in Python (or omitting it in languages which imply such
a lookup, of course).

I don't think that's the natural place. It's certainly one place to
consider, and may be the best one. However, it might work equally well
to use preconditions on the methods that add the beam and pier to Foo
to verify that the beam and pier in question are valid. If the
attributes of the beam and pier can't change, this would be the right
way to do it.
If I'm not allowed (because you think "it's silly"!) to express a class
invariant in terms of attributes of the attributes of an instance of
that class, I basically have to write tons of boilerplate, violating
encapsulation, to express what are really attributes of attributes of
foo "as if" they were attributes of foo directly, e.g. [...]
(etc). Changing a lot of dots into underscores -- what a way to waste
programmer time! And all to NO advantage, please note, since:

If you knew it was going to be to no advantage, why did you write the
boilerplate? That's also pretty silly. Care to provide reasons for
your wanting to do this?
...in the end the invariant DOES have to be checked when anything
relevant changes, anyway, with or without the silly extra indirection.

No, it doesn't have to be checked. Even invariants that don't suffer
from this don't have to be checked. It would be nice if every
invariant was checked every time it might be violated, but that's not
practical. If checking relationships between attributes attributes is
the best you can do, you do that, knowing that instead of an invariant
violation raising an exception after the code that violates it, the
exception may raised after the first method of your class that is
called after the invariant is violated. That's harder to debug than
the other way, but if it's the best you can get, it's the best you can
get.
But besides the wasted work, there is a loss of conceptual integrity: I
don't WANT Foo to have to expose the internal details that beam1's
reference point A transfers the force to pier1's top, etc etc, elevating
all of these internal structural details to the dignity of attributes of
Foo. Foo should expose only its externally visible attributes: loads
and forces on all the relevant points, geometric details of the
exterior, and structural parameters that are relevant for operating
safety margins, for example.

You're right. Choosing to do that would be a bad idea. I have no idea
why you would do that in any language I'm familiar with. I'd be
interested in hearing about the language you use that requires you to
do that.
I disagree, most intensely and deeply, that any reference to an
attribute of an attribute of self in the body of an invariant is
necessarily "unwise".

So do I.
Not all can be sensibly CHECKED, but most definitely all can be
EXPRESSED. Another one of my long-standing contentions with Eiffel is
the inability to express invariants (and pre- and post- conditions)
because the compiler is unable to figure out a decent way to check them;
Z and the VDL, just to name very old design languages, show easy ways to
allow full expression. Of course, if a condition is of the form, say,
"all items of potentially infinite iterable X satisfy predicate P", it
may not be runtime-checkable -- big furry deal, I want to be able to
EXPRESS it anyway, because apart from runtime checking there are other
precious uses of such conditions (e.g., the compiler might be able to
DEDUCE from such a condition some important optimization, or the
compiletime proof of other assertions, when run in the appropriate
mode).

So you think that a tool being imperfect means you shouldn't use it
all? So you don't test your code, because testing can't reveal all
bugs? That's odd - I always thought that testing was a critical part
of program development. I'd be interested in hearing about any
research that justifies doing development with testing, preferably
with URLs.

<mike
 
A

Alex Martelli

Mike Meyer said:
Well, the hard-core solution is to note that your class doesn't really
deal with the type Bar, but deals with a subtype of Bar for which x >
23 in all cases. Since types are represented by classes, you should
subclass Bar so you have a class that represents this subtype. The
class is trivial (with Eiffel conventions):

class RESTRICTED_BAR
inherits BAR
invariant x > 23
END

Yes, but then once again you have to "publicize" something (an aspect of
a class invariant) which should be dealt with internally; also, this
approach does not at all generalize to "bar1.x>23 OR bar2.x>23" and any
other nontrivial constraint involving expressions on more than
attributes of a single instance's attribute and compile-time constants.
So, besides "hard-coreness", this is just too limited to serve.

I don't think that's the natural place. It's certainly one place to
consider, and may be the best one. However, it might work equally well
to use preconditions on the methods that add the beam and pier to Foo
to verify that the beam and pier in question are valid. If the
attributes of the beam and pier can't change, this would be the right
way to do it.

What ever gave you the impression that the loads on beams and piers (and
therefore the forces they transfer) "can't change"? That would be a
pretty weird way to design a framework for structural modeling in any
language except a strictly functional (immutable-data) one, and I've
already pointed out that functional languages, thanks to their immutable
data approach, are very different from ones (like Eiffel or Python)
where data routinely does get changed.

If I'm not allowed (because you think "it's silly"!) to express a class
invariant in terms of attributes of the attributes of an instance of
that class, I basically have to write tons of boilerplate, violating
encapsulation, to express what are really attributes of attributes of
foo "as if" they were attributes of foo directly, e.g. [...]
(etc). Changing a lot of dots into underscores -- what a way to waste
programmer time! And all to NO advantage, please note, since:

If you knew it was going to be to no advantage, why did you write the
boilerplate? That's also pretty silly. Care to provide reasons for
your wanting to do this?

If I had to program under a styleguide which enforces the style
preferences you have expressed, then the stupid boilerplate would allow
my program to be accepted by the stylechecker, thus letting my code be
committed into the source control system; presumably that would be
necessary for me to keep my job (thus drawing a salary) or getting paid
for my consultancy billed hours. Just like, say, if your styleguide
forbade the use of vowels in identifiers, I might have a tool to convert
such vowels into consonants before I committed my code. I'm not saying
there cannot be monetary advantage for me to obey the deleterious and
inappropriate rules of any given arbitrary styleguide: it may be a
necessary condition for substantial monetary gains or other preferments.
I'm saying there is no advantage whatsoever to the organization as a
whole in imposing arbitrary constraints such as, "no vowels in
identifiers", or, "no access to attributes of attributes in invariants".

No, it doesn't have to be checked. Even invariants that don't suffer
from this don't have to be checked. It would be nice if every
invariant was checked every time it might be violated, but that's not
practical. If checking relationships between attributes attributes is
the best you can do, you do that, knowing that instead of an invariant
violation raising an exception after the code that violates it, the
exception may raised after the first method of your class that is
called after the invariant is violated. That's harder to debug than
the other way, but if it's the best you can get, it's the best you can
get.

Let's silently gloss on the detail that calling "invariant" something
that is in fact not guaranteed not to vary (or at least not to vary
without raising exceptions) is a recipe for semantic confusion;-) The
point remains that forcing me to define a beam1_load method, which just
delegates to beam1.load, and use beam1_load in my invariant's code
instead of beam1.load, is a silly rule -- yet it follows from the
stylistic prohibition on using beam1.load directly there, which
highlights the fact that said stylistic prohibition is silly in its
turn.

You're right. Choosing to do that would be a bad idea. I have no idea
why you would do that in any language I'm familiar with. I'd be
interested in hearing about the language you use that requires you to
do that.

Eiffel PLUS your constraint against using attributes' attributes in an
invariant (language + additional constraint you desire) induces me to
wrap each beam1.load access as an attribute of my class under the name
beam1_load (and so on for all attributes' attributes which I need to
access internally in my invariants). I guess I can put band-aids on the
self-inflicted wounds by keeping those attributes private, but it would
be better to avoid the wounds in the first place by ditching the style
constraint against using attributes' attributes in invariants.


Yet you called it "silly" -- which DOES imply "unwise" (and more).

So you think that a tool being imperfect means you shouldn't use it
all? So you don't test your code, because testing can't reveal all
bugs? That's odd - I always thought that testing was a critical part
of program development. I'd be interested in hearing about any
research that justifies doing development with testing, preferably
with URLs.

Start with:

http://www.mtsu.edu/~storm/
http://www.softwareqatest.com/
http://www.testing.com/
http://www.faqs.org/faqs/software-eng/testing-faq/

and feel free to come back and ask for more once you've exhausted the
wealth of pointers, articles, books and surveys these URLs will direct
you to.

As far as I know, the only outstanding figure in the history of
programming who decisively condemned testing because "it can only show
the presence of bugs, never their absence" was Djikstra; it's funny that
he failed to notice the parallel with Popper's epistemology -- by the
same thought-structure, we should condemn scientific experiments,
because, per Popper, they can only show the falsity of a scientific
theory, never its truth.

I never said nor implied that a tool's imperfections must prohibit its
use: I was just pointing out that your assertion about important
conditions which *can't be expressed* is simply false (as well as
totally inapplicable to the specific examples being discussed).


Alex
 
A

Alex Martelli

Mike Meyer said:
That's an API which makes changing the object more difficult. It may
be the best API for the case at hand, but you should be aware of the
downsides.

Since x and y are important abstractions of a "2-D coordinate", I
disagree that exposing them makes changing the object more difficult, as
long of course as I can, if and when needed, change them into properties
(or otherwise obtain similar effects -- before we had properties in
Python, __setattr__ was still quite usable in such cases, for example,
although properties are clearly simpler and more direct).

You could make a case for a "2D coordinate" class being "sufficiently
primitive" to have immutable instances, of course (by analogy with
numbers and strings) -- in that design, you would provide no mutators,
and therefore neither would you provide setters (with any syntax) for x
and y, obviously. However, a framework for 2D geometry entirely based
on immutable-instance classes would probably be unwieldy (except in a
fully functional language); as long as we have a language whose normal
style allows data mutation, we'll probably fit better into it by
allowing mutable geometrical primitives at some level -- and as soon as
the mutable primitives are reached, "settable attributes" and their
syntax and semantics come to the fore again...


Alex
 
M

Mike Meyer

Yes, but then once again you have to "publicize" something (an aspect of
a class invariant) which should be dealt with internally

Contracts are intended to be public; they are part of the the class's
short form, which is the part that's intended for public consumption.
If your vision of invariants is that they are for internal use only,
and clients don't need to know them, then you probably ought to be
considering another language.
also, this approach does not at all generalize to "bar1.x>23 OR
bar2.x>23" and any other nontrivial constraint involving expressions
on more than attributes of a single instance's attribute and
compile-time constants. So, besides "hard-coreness", this is just
too limited to serve.

I believe it's the best solution for the case at hand. It causes the
violation of the invariant to be caught as early as possible. As I
mentioned elsewhere, it's not suitable for all cases, so you have to
use other, possibly less effective, tools.
What ever gave you the impression that the loads on beams and piers (and
therefore the forces they transfer) "can't change"?

Your incomplete specification of the problem. You didn't say whether
or not they could change, so I pointed out what might - key word, that
- be a better solution for a more complete specification.
If I'm not allowed (because you think "it's silly"!) to express a class
invariant in terms of attributes of the attributes of an instance of
that class, I basically have to write tons of boilerplate, violating
encapsulation, to express what are really attributes of attributes of
foo "as if" they were attributes of foo directly, e.g. [...]
(etc). Changing a lot of dots into underscores -- what a way to waste
programmer time! And all to NO advantage, please note, since:
If you knew it was going to be to no advantage, why did you write the
boilerplate? That's also pretty silly. Care to provide reasons for
your wanting to do this?

If I had to program under a styleguide which enforces the style
preferences you have expressed, then the stupid boilerplate would allow
my program to be accepted by the stylechecker, thus letting my code be
committed into the source control system; presumably that would be
necessary for me to keep my job (thus drawing a salary) or getting paid
for my consultancy billed hours. Just like, say, if your styleguide
forbade the use of vowels in identifiers, I might have a tool to convert
such vowels into consonants before I committed my code. I'm not saying
there cannot be monetary advantage for me to obey the deleterious and
inappropriate rules of any given arbitrary styleguide: it may be a
necessary condition for substantial monetary gains or other preferments.
I'm saying there is no advantage whatsoever to the organization as a
whole in imposing arbitrary constraints such as, "no vowels in
identifiers", or, "no access to attributes of attributes in invariants".

True. But if you think this is an arbitary constraint, why did you
impose it in the first place?
Let's silently gloss on the detail that calling "invariant" something
that is in fact not guaranteed not to vary (or at least not to vary
without raising exceptions) is a recipe for semantic confusion;-) The
point remains that forcing me to define a beam1_load method, which just
delegates to beam1.load, and use beam1_load in my invariant's code
instead of beam1.load, is a silly rule -- yet it follows from the
stylistic prohibition on using beam1.load directly there, which
highlights the fact that said stylistic prohibition is silly in its
turn.

Yes, it's a silly rule. Why did you impose it?
Eiffel PLUS your constraint against using attributes' attributes in an
invariant (language + additional constraint you desire)

No, that's *your* constraint. I can think of no rational reason you
would want to impose it, but you have.
Yet you called it "silly" -- which DOES imply "unwise" (and more).

No, I called one specific example silly. Yesterday, I fixed a bit of
python that did:

datetime = str(datetime)
year = int(datetime[x:y])
# and so on to pull month, day, hour, and minute out

I'd call this use of str, int and slicing silly as well. You would
apparently therefore conclude that I think *any* use of str, int and
slicinng is silly. You'd be wrong to do so, just as you were wrong to
conclude from my thinking that one particular example of referencing
an attributes attribute in ann invariant that any use of an attributes
attribute in an invariant is silly. That particular straw man is
strictly *your* creation. If you want it justified, you're going to
have to do it yourself.
So you think that a tool being imperfect means you shouldn't use it
all? So you don't test your code, because testing can't reveal all
bugs? That's odd - I always thought that testing was a critical part
of program development. I'd be interested in hearing about any
research that justifies doing development with testing, preferably
with URLs.

Start with: [...]
and feel free to come back and ask for more once you've exhausted the
wealth of pointers, articles, books and surveys these URLs will direct
you to.

Sorry, I misspoke. I meant to ask you to justify your believe in doing
development *without* testing.

<mike
 
M

Mike Meyer

Since x and y are important abstractions of a "2-D coordinate", I
disagree that exposing them makes changing the object more difficult, as
long of course as I can, if and when needed, change them into properties
(or otherwise obtain similar effects -- before we had properties in
Python, __setattr__ was still quite usable in such cases, for example,
although properties are clearly simpler and more direct).

Exposing them doesn't make making changes more difficult. Allowing
them to be used to manipulate the object makes some changes more
difficult. Properties makes the set of such changes smaller, but it
doesn't make them vanish.

Take our much-abused coordinate example, and assume you've exposed the
x and y coordinates as attributes.

Now we have a changing requirement - we want to get to make the polar
coordinates available. To keep the API consistent, they should be
another pair of attributes, r and theta. Thanks to Pythons nice
properties, we can implement these with a pair of getters, and compute
them on the fly.

If x and y can't be manipulated individually, you're done. If they
can, you have more work to do. If nothing else, you have to decide
that you're going to provide an incomplete interface, in that users
will be able to manipulate the object with some attributes but not
others for no obvious good reason. To avoid that, you'll have to add
code to run the coordinate transformations in reverse, which wouldn't
otherwise be needed. Properties make this possible, which is a great
thing.

<mike
 
P

Paul Rubin

You could make a case for a "2D coordinate" class being "sufficiently
primitive" to have immutable instances, of course (by analogy with
numbers and strings) -- in that design, you would provide no mutators,
and therefore neither would you provide setters (with any syntax) for x
and y, obviously. However, a framework for 2D geometry entirely based
on immutable-instance classes would probably be unwieldy

I could imagine using Python's built-in complex numbers to represent
2D points. They're immutable, last I checked. I don't see a big
conflict.
 
B

Bernhard Herzog

You could make a case for a "2D coordinate" class being "sufficiently
primitive" to have immutable instances, of course (by analogy with
numbers and strings) -- in that design, you would provide no mutators,
and therefore neither would you provide setters (with any syntax) for x
and y, obviously. However, a framework for 2D geometry entirely based
on immutable-instance classes would probably be unwieldy

Skencil's basic objects for 2d geometry, points and transformations, are
immutable. It works fine. Immutable object have the great advantage of
making reasoning about the code much easier as the can't change behind
your back.

More complex objects such as poly bezier curves are mutable in Skencil,
and I'm not sure anymore that that was a good design decision. In most
cases where bezier curve is modified the best approach is to simply
build a new bezier curve anyway. Sort of like list-comprehensions make
it easier to "modify" a list by creating a new list based on the old
one.

Bernhard
 
A

Alex Martelli

Mike Meyer said:
Take our much-abused coordinate example, and assume you've exposed the
x and y coordinates as attributes.

Now we have a changing requirement - we want to get to make the polar
coordinates available. To keep the API consistent, they should be
another pair of attributes, r and theta. Thanks to Pythons nice
properties, we can implement these with a pair of getters, and compute
them on the fly.

If x and y can't be manipulated individually, you're done. If they
can, you have more work to do. If nothing else, you have to decide
that you're going to provide an incomplete interface, in that users
will be able to manipulate the object with some attributes but not
others for no obvious good reason. To avoid that, you'll have to add
code to run the coordinate transformations in reverse, which wouldn't
otherwise be needed. Properties make this possible, which is a great
thing.

Properties make this _easier_ (but you could do it before properties
were added to Python, via __setattr__ -- just less conveniently and
directly) -- just as easy as setX, setY, setRho, and setTheta would (in
fact, we're likely to have some of those methods under our properties,
so the difference is all in ease of USE, for the client code, not ease
of IMPLEMENTATION, compared to setter-methods).

If we keep the internal representation in cartesian coordinates
(attributes x and y), and decide that it would interfere with the
class's usefulness to have rho and theta read-only (i.e., that it IS
useful for the user of the class to be able to manipulate them
directly), we do indeed need to "add code" -- the setter methods setRho
and setTheta. But let's put that in perspective. If we instead wanted
to make the CoordinatePair class immutable, we'd STILL have to offer an
alternative constructor or factory-function -- if it's at all useful to
manipulate rho and theta in a mutable class, it must be at least as
useful to be able to construct an immutable version from rho and theta,
after all. So, we ARE going to have, say, a classmethod (note: all the
code in this post is untested)...:

class CoordinatePair(object):
def fromPolar(cls, rho, theta):
assert rho>=0
return cls(rho*math.cos(theta), rho*math.sin(theta))
fromPolar = classmethod(fromPolar)
# etc etc, the rest of this class

well, then, how much more code are we adding, to implement setRho and
setTheta when we decide to make our class mutable? Here...:

def setRho(self, rho):
c = self.fromPolar(rho, self.getTheta())
self.x, self.y = c.x, c.y
def setTheta(self, theta):
c = self.fromPolar(self.getRho(), theta)
self.x, self.y = c.x, c.y

That's the maximum possible "difficulty" (...if THIS was a measure of
real "difficulty" in programming, I doubt our jobs would be as well paid
as they are...;-) -- it's going to be even less if we need anyway to
have a method to copy a CoordinatePair instance from another, such as

def copyFrom(self, other):
self.x, self.y = other.x, other.y

since then the above setters also become no-brainer oneliners a la:

def setRho(self, rho):
self.copyFrom(self.fromPolar(rho, self.getTheta()))

and you might choose to further simplify this method's body to

self.copyFrom(self.fromPolar(rho, self.theta))

since self.theta is going to be a property whose accessor half is the
above-used self.getTheta (mostly a matter of style choice here).


Really, I don't think this makes a good poster child for your "attribute
mutators make life more difficult" campaign...;-)


Alex
 
A

Alex Martelli

Paul Rubin said:
I could imagine using Python's built-in complex numbers to represent
2D points. They're immutable, last I checked. I don't see a big
conflict.

No big conflict at all -- as I recall, last I checked, computation on
complex numbers was optimized enough to make them an excellent choice
for 2D points' internal representations. I suspect you wouldn't want to
*expose* them as such (e.g. by inheriting) but rather wrap them, because
referring to the .real and .imag "coordinates" of a point (rather than
..x and .y) IS rather weird. Wrapping would also leave you the choice of
making 2D coordinates a class with mutable instances, if you wish,
reducing the choice of a complex rather than two reals to a "mere
implementation detail";-).

The only issue I can think of: I believe (I could be wrong) that a
Python implementation might be built with complex numbers disabled (just
like, e.g., it might be built with unicode disabled). If that's indeed
the case, I might not want to risk, for the sake of a little
optimization, my 2D geometry framework not working on some little
cellphone or PDA or whatever...;-)


Alex
 
A

Alex Martelli

Bernhard Herzog said:
Skencil's basic objects for 2d geometry, points and transformations, are
immutable. It works fine. Immutable object have the great advantage of
making reasoning about the code much easier as the can't change behind
your back.

Yes, that's important -- on the flip side, you may, in some cases, wish
you had mutable primitives for performance reasons (I keep daydreaming
about adding mutable-number classes to gmpy...;-)

More complex objects such as poly bezier curves are mutable in Skencil,
and I'm not sure anymore that that was a good design decision. In most
cases where bezier curve is modified the best approach is to simply
build a new bezier curve anyway. Sort of like list-comprehensions make
it easier to "modify" a list by creating a new list based on the old
one.

True, not for nothing were list comprehensions copied from the
functional language Haskell -- they work wonderfully well with immutable
data, unsurprisingly;-). However, what if (e.g.) one anchor point
within the spline is being moved interactively? I have no hard data,
just a suspicion that modifying the spline may be more efficient than
generating and tossing away a lot of immutable splines...


Alex
 
P

Paul Rubin

No big conflict at all -- as I recall, last I checked, computation on
complex numbers was optimized enough to make them an excellent choice
for 2D points' internal representations. I suspect you wouldn't want to
*expose* them as such (e.g. by inheriting) but rather wrap them, because
referring to the .real and .imag "coordinates" of a point (rather than
.x and .y) IS rather weird. Wrapping would also leave you the choice of
making 2D coordinates a class with mutable instances, if you wish,
reducing the choice of a complex rather than two reals to a "mere
implementation detail";-).

Right, you could use properties to make point.x get the real part of
an internal complex number. But now you're back to point.x being an
accessor function; you've just set things up so you can call it
without parentheses, like in Perl. E.g.

a = point.x
b = point.x
assert (a is b) # can fail

for that matter

assert (point.x is point.x)

can fail. These attributes aren't "member variables" any more.
 
A

Alex Martelli

Paul Rubin said:
Right, you could use properties to make point.x get the real part of
an internal complex number. But now you're back to point.x being an
accessor function; you've just set things up so you can call it
without parentheses, like in Perl. E.g.

a = point.x
b = point.x
assert (a is b) # can fail

Sure -- there's no assurance of 'is' (although the straightforward
implementation in today's CPython would happen to satisfy the assert).

But similarly, nowhere in the Python specs is there any guarantee that
for any complex number c, c.real is c.real (although &c same as above).
So what? 'is', for immutables like floats, is pretty useless anyway.

for that matter

assert (point.x is point.x)

can fail. These attributes aren't "member variables" any more.

They are *syntactically*, just like c.real for a complex number c: no
more, no less. I'm not sure why you're so focused on 'is', here. But
the point is, you could, if you wished, enable "point.x=23" even if
point held its x/y values as a complex -- just, e.g.,
def setX(self, x):
x.c = complex(x, self.y)
[[or use x.c.imag as the second argument if you prefer, just a style
choice]].


Alex
 
E

Erik Max Francis

Paul said:
Right, you could use properties to make point.x get the real part of
an internal complex number. But now you're back to point.x being an
accessor function; you've just set things up so you can call it
without parentheses, like in Perl. E.g.

a = point.x
b = point.x
assert (a is b) # can fail

for that matter

assert (point.x is point.x)

can fail. These attributes aren't "member variables" any more.

Which is perfectly fine, since testing identity with `is' in this
context is not useful.
 
M

Mike Meyer

def setRho(self, rho):
c = self.fromPolar(rho, self.getTheta())
self.x, self.y = c.x, c.y
def setTheta(self, theta):
c = self.fromPolar(self.getRho(), theta)
self.x, self.y = c.x, c.y

That's the maximum possible "difficulty" (...if THIS was a measure of
real "difficulty" in programming, I doubt our jobs would be as well paid
as they are...;-) -- it's going to be even less if we need anyway to
have a method to copy a CoordinatePair instance from another, such as

It's a trivial example. Incremental extra work is pretty much
guaranteed to be trivial as well.
Really, I don't think this makes a good poster child for your "attribute
mutators make life more difficult" campaign...;-)

The claim is that there exists cases where that's true. This cases
demonstrates the existence of such cases. That the sample is trivial
means the difficulty is trivial, so yeah, it's a miserable poster
child. But it's a perfectly adequate existence proof.

<mike
 
S

Steven D'Aprano

Steven D'Aprano said:
In particular,
you can get most of your meaningless methods out of a properly
designed Coordinate API. For example, add/sub_x/y_ord can all be
handled with move(delta_x = 0, delta_y = 0).

Here is my example again:

Then, somewhere in my application, I need twice the
value of the y ordinate. I would simply say:

value = 2*pt.y
[end quote]

I didn't say I wanted a coordinate pair where the y ordinate was double
that of the original coordinate pair. I wanted twice the y ordinate, which
is a single real number, not a coordinate pair.

Here you're not manipulating the attribute to change the class -
you're just using the value of the attribute. That's what they're
there for.

[bites tongue to avoid saying a rude word]

That's what I've been saying all along!

But according to the "Law" of Demeter, if you take it seriously, I
mustn't/shouldn't do that, because I'm assuming pt.y will always have a
__mul__ method, which is "too much coupling". My Coordinate class
must/should create wrapper functions like this:

class Coordinate:
def __init__(self, x, y):
self.x = x; self.y = x
def mult_y(self, other):
return self.y * other

so I am free to change the implementation (perhaps I stop using named
attributes, and use a tuple of two items instead).

I asked whether people really do this, and was told by you that they not
only do but that they should ("only with a better API design").

So we understand each other, I recognise that abstraction is a valuable
tool, and can be important. What I object to is taking a reasonable
guideline "try to keep coupling to the minimum amount practical" into an
overblown so-called law "you should always write wrapper functions to hide
layers more than one level deep, no matter how much extra boilerplate code
you end up writing".


Except only the omniscennt programmer can do that perfectly.

I'm not interested in perfection, because it is unattainable. I'm
interested in balancing the needs of many different conflicting
requirements. The ability to change the implementation of my class after
I've released it is only one factor out of many. Others include initial
development time and cost, bugs, ease of maintenance, ease of
documentation, how complex an API do I expect my users to learn,
convenience of use, and others.

[snip]
And that's not the kind of behavior I'm talking about here, nor is it
the kind of behavior that the LoD is designed to help you with (those
are two different things).

How are they different? Because one is a class and the other is a module?
That's a meaningless distinction: you are still coupled to a particular
behaviour of something two levels away. If the so-called Law of Demeter
makes sense for classes, it makes sense for modules too.

[snip]
That's an API which makes changing the object more difficult. It may be
the best API for the case at hand, but you should be aware of the
downsides.

Of course. We agree there. But it is a trade-off that can (not must, not
always) be a good trade-off, for many (not all) classes. One of the
advantages is that it takes responsibility for specifying every last thing
about ordinates within a coordinate pair out of my hands. They are floats,
that's all I need to say.

If you think I'm arguing that abstraction is always bad, I'm not. But it
is just as possible to have too much abstraction as it is to have too
little.


[snip]
Again, this is *your* API, not mine. You're forcing an ugly, obvious API
instead of assuming the designer has some smidgen of ability.

But isn't that the whole question? Should programmers follow slavishly the
so-called Law of Demeter to the extremes it implies, even at the cost of
writing ugly, unnecessary, silly code, or should they treat it as a
guideline, to be obeyed or not as appropriate?

Doesn't Python encourage the LoD to be treated as a guideline, by allowing
class designers to use public attributes instead of forcing them to write
tons of boilerplate code like some other languages?


I've
already pointed out one trivial way to deal with this, and there are
others.

Mike, the only "trivial way to deal with this" that you have pointed out
was this:

"For example, add/sub_x/y_ord can all be handled with move(delta_x = 0,
delta_y = 0)."

That's a wonderful answer *for the wrong question*. I thought I explained
that already.
 

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,769
Messages
2,569,582
Members
45,065
Latest member
OrderGreenAcreCBD

Latest Threads

Top