Object-oriented programming (was Creating PCT-based social-economic agents)

[Martin Taylor 2010.03.07.10.15]

[From Bill Powers (2010.03.06.1630 MST)]

Martin Taylor 2010.03.05.14.54 –

  BP earlier: The last, the object-oriented

aspect, is the worst part of it from my point of view. I’m sure this is
just a preference of mine, but I have a problem with thinking of
“objects” that are defined not only by their properties, but by
“methods” that define how those properties are to be used.

MT: “That’s not a bug, but a feature”. It actually is a huge advantage
when things get big and complex. You mkae sure that the little object
does the right thing when it’s asked, and everything that happens is
encapsulated within the object. So if you want something moved left,
you can write your code to do that without worrying whether what is
being moved is a window or a cursor, provided it understands how to
move itself left.

Does that mean that when a window class contains a movewindow method,
that method is copied into each instance of the class? Or is the window
moving code written and compiled just once, and called by each instance
of a window? If the latter, there’s no duplication of code, but then I
wonder why not just have one procedure for moving a window and call it
whenever you want a window moved? That’s how I would do it in
procedural code.

Object-oriented code is procedural code, at least if you are writing in
a language like C++ or Java. OpenLaszlo defines the objects in
explicitly declarative XML, but the functioning parts are all
procedural. I think you mean “monolithic” rather than “procedural”,

I would assume that’s how it is done in object-oriented programming as
well, but since the details are hidden in the compiler, the programmer
doesn’t know it. Even if you were to write the code separately for each
instance of the window, a good compiler might notice that and produce
it only once in the executable. It’s for the programmer’s benefit that
you put what is common into the class specification. You write it only
once, and thereby reduce the likelihood of hard to find typos. If you
make a typo, it will show up in the behaviour of every window, whereas
if you made a typo in writing the code for window22, you might spend a
while wondering what went wrong, perhaps not even realizing that the
problem was in window22.

One way of looking at object-orientation is as an extension of the idea
of subroutines and what in C are called “structures”. A C structure is
an object without process, and a subroutine is a process tied to no
kind of structure. In O-O code, the subroutines are associated with the
structures, so they can be simplified and don’t have to be concerned
with the different kinds of circumstance in which they might be called.
If you want a globally accessible subroutine, you write it into the
highest level class from which all classes and objects are inherited.

Why hide access to the method from the programmer?

You don’t. What you do is to ensure that when the programmer is working
on some other part of the code, nothing done there is going affect the
way your windows work. That is not the case when you use global
variables, accessible from anywhere in the code. It also means that if
you figure out a niftier way to make the windows do what that method
offers to the rest of the program, your changes won’t affect how the
rest of the program works. It’s a mid-level protection scheme.

Are they really doing anything different in
object-oriented code?

Not in the sense that all programs are Turing machines. Probably not
much in the way the executable binaries come out. But a great deal
differently in how easy it is for a programmer to make sure little bits
and pieces of the program work properly and fit together properly. It’s
a bit like a hierarchic control system as opposed to a one-level
control system in which all the different controlled variables of
whatever complexity are constructed directly from the sensory inputs.
In the hierarchic system, each level perceptions are composed of a few
lower-level ones, whereas in the flat one-level system, some of the
perceptual input functions are not only very complicated, but also must
duplicate a lot of what is done in constructing the simpler
perceptions. I know the analogy isn’t terribly close, but perhaps it
might give you an idea of why a lot of methods are only a few lines
long – they use the few lines of other methods in a possibly nested
manner, and at each stage the probability is very high that the code is
correct.

I notice that they have to have provisions for overrides
when someone wants to do a method in a slightly different way – and
that would be necessary if you’re not allowed to alter the method for a
new program.

You certainly are allowed to change methods for a new program, but if
you are using the same kind of object in many different programs, you
probably don’t want to, as you will have pared its methods down to what
is common across all programs. Then you may not need to override those
methods, but you might. Typically, you override when you make a child
class that is a child class becaus you want it to behave differently
from the parent. More commonly, you don’t override when you make the
child, but you add new attributes and methods.

Consider a slider (usually displayed as a bar with a marker that can be
moved from one end of the bar to the other, but possibly displayed as a
knob that can be turned like the heat control on a kitchen range). In a
monolithic approach you would write different code for the two kinds of
slider, but in an O-O approach you would segregate what they have in
common from the parts where they differ.

In an O-O approach, you might make a base class that only had
attributes of low, current, and high values, with nothing related to
the display. It would have methods for setting and reporting those
values, each being a one-liner (which, as you say, would waste compute
cycles). Then you might write a child class for a linear slider that
knows how to display itself, adding new attributes for the location of
the low and high end, and another child class that knows how to display
itself as a knob with different new attributes, for the centre,
diameter, and low and high rotation angle. You might write a quite
different class for displaying knobs of different visual styles, just
as when you are putting a knob on a door, the locking mechanism has the
properties that the lock is open at one rotation angle and closed at
another rotation angle, but the knob might be a lever, a brass sphere,
or a black knobbly object.

It seems to me that object-oriented programming is
intended for large software developers with a big room full of cubicles
and programmers developing pieces of a program too big for one person
to handle. But if you’re just one person writing the program and want
to be able to tailor each program to the specific task, isn’t it a
little silly to hide code from yourself?

No. For most programmers (and certainly for me), it is hard to keep in
mind at all times every detail of code that extends over more than a
few tens of lines. Most programs that do anything interesting are
hundreds of lines long.

What if the bug in your program is caused by an
interaction with something inside a method?

I think that question is arguing on the wrong side. It is precisely
because a bug in your program outside an object is unlikely to
influence the behaviour inside the object that O-O programming is
easier to handle. Either the method works (and you may have written a
test harness to test it under stress conditions) or it doesn’t. If your
bug is caused because you expect the method to do something it wasn’t
designed to do, that could be considered a problem in the calling code,
or it could be considered a reason to subclass the object and create a
method that does what your calling code wants done. Either is possible.

If you don’t know how the method works, how will you ever
find it?

Why would you not know how the method works if you want to know it?
Does knowing how a method works matter if you know the specification of
the method’s arguments and results and can trust that it has been
debugged? If you don’t trust that it has been properly debugged, can’t
you look at its specifications and see whether it gives the right
answer under test conditions, or, if you must, look at its code and see
if it does seem to have been coded properly?

There’s another problem I think I see, though you can tell me if I’m
wrong. That is the waste of computer resources. If you have to call a
subroutine to read or write values of a variable, every call costs
probably twenty times as many machine cycles as just writing or reading
a global variable at a memory address. Pointer arithmetic slows the
process further. I know the response that would get from a conventional
software engineer: “Get a bigger faster computer.” That doesn’t work
for me: I have the biggest and fastest computer I can afford.

The usual answer to this is not “Get a bigger faster computer” but
“Which do you value more, programming and debugging time or execution
time”. The fastest program is one without choice points or subroutines,
in which the execution goes from line 1 to line N and stops. Maybe it
takes gigabytes of code to do it, but it’s faster than the same program
written an ten lines with a loop that has an index running up to the
billions. A good compiler will minimize the overhead of the loop, but
it’s still there, if only in the added counter, counting which was done
by the programmer when writing the gigabyte-long linear version.

The issue isn’t whether the properties of a coding technique influence
execution time. They usually do. The issue is how much they influence
it, and how important that slowdown is. If you can save a day of
programming time in writing a program that runs in half a day, it
doesn’t matter if the cost of the overhead was 100%. If you are looking
for maximum speed in software that runs a fighter plane, then it’s
worth spending weeks or months trying to speed it up – but it had
better be largely debugged. And there’s another trade-off. Does the
programming technique reduce the likelihood of hidden bugs at the cost
of slowdown? If it does, then what is it worth to you to have how much
slowdown?

Overall, I doubt that we are talking about a 10% slowdown, and possibly
not even a 1% slowdown from using O-O methods, at least not if the
compiler is any good. How much programming time would it save in any
particular program? Probably none, if you are learning the technique
and the language. Probably a lot, if you are fluent in both. Would it
be worth the learning time? That’s another trade-off everyone has to
make for themselves.

There’s another side-effect to all this, which is that your objects are
reusable. Write one control system class, and you have the code for
every control system you may ever want to run. Subclass your base
system for different levels of control, meaning different kinds of
perceptual variable, and you have the components of every complex
control hierarchy you will ever want to test. You have them in a
library, and if you want to try out social structures with independent
interacting hierarchies you can organize your control systems that way.
If you want a complicated environment (which is the only justification
for a complex control structure) you have to write for classes of
objects that interact in the environment, but you have your hierarchy
components at hand, so half the job is done.

I’ll have a look at the other languages you mentioned. Somewhere in the
world there must be a language that is plain vanilla procedural as
simple to write (and read) as Pascal and as easily linked to the
operating system’s API as Delphi is.

Again, I think you should replace “procedural” with “monolithic” in the
above. As I have argued above, for small programs a monolithic approach
may be simpler, but for larger programs it isn’t, and if you want to
take advantage of your work in later programs, there is no contest. And
I disagree that it’s a good idea to link to the operating system’s APIs
directly. To do so limits the users of your program to the platform on
which you wrote. There should be an intervening standard layer that
links to the system APIs.

The other “languages” are not languages, but systems in which many
useful objects and methods are provided for you, along with (in some
cases, notably Repast) links to other easily available software. They
are all aimed at developing systems of many interacting agents (which
was the original thrust of this thread). None of them are languages in
the sense you mean.

By the way, here’s the first lines of the Wikipedia entry for Object
Pascal: "
Object Pascal refers to a branch of object-oriented derivatives of
Pascal, mostly known as the
primary programming language of Delphi. Pascal
compilers, including those for Object Pascal, generally run very fast
while producing highly optimized code."

So I guess you can write in an object-oriented manner in Delphi, too,
though the result would not be platform-independent. However, following
a link on the Wikipedia page, I find “Free Pascal” :

···

Free Pascal (FPC for short, and formerly known as FPK
Pascal
[1])
is a free, portable and open
source
compiler for Pascal and
Object Pascal languages. It supports a
number of dialects, including the two most popular Borland dialects—Turbo
Pascal
and Delphi—and
some Mac Pascal constructs.

Free Pascal is available on many architectures and operating systems (see Targets),
and belongs to the write once, compile anywhere
campaign. It has an excellent support for integration of assembly language, and supports multiple
architectures and notations in the internal assembler.

With a separate IDE project called Lazarus, cross-platform graphical application development
is possible with little effort.


Perhaps that’s another possible route. I’m downloading the Mac OS X
compiler (and the Lazarus visual programming system) as I write this,
and will see if I can compile some of your Delphi programs.

Martin

[From Bill Powers (2010.03.07.1551 MSTY)]

Martin Taylor 2010.03.07.10.15 –

Object-oriented code is
procedural code, at least if you are writing in a language like C++ or
Java. OpenLaszlo defines the objects in explicitly declarative XML, but
the functioning parts are all procedural. I think you mean
“monolithic” rather than
“procedural”.

I wouldn’t know – I use Delphi in the procedural mode most of the time,
using only the objects (and classes) that Delphi automatically creates
and otherwise sidestepping all the stuff about inheritance and overrides
and a dozen other things an O-O programmer seems to have to know
about.

I have to thank you for your patient discussion of O-O programming. It
encouraged me to think that maybe it’s worthwhile learning to do it. I
get impatient with having to put aside the projects I’m working on to
learn a new language, which seems like a unnecessary distraction from
what I’m really interested in doing. But if the result is a net gain in
programming skill, and if the Holy Grail of cross-platform accessibility
is achieved, the digression would be worth the trouble.

I’m glad you’re going to try Lazarus. It takes some conversions to
compile Delphi code, but the necessary utilities are included in the
compiler’s IDE for converting Delphi units, projects, and packages to
Lazarus. I haven’t explored that much – it will be good to have someone
to talk with about it. You can learn it, write up the tutorials, and
spoon-feed me. Actually, Bruce Abbott is a good O-O programmer and he
could join in, too. He taught me a lot about Delphi. If we all practiced
our arm-twisting, we might get Rick Marken to join in, and David
Goldstein wants to learn, too. David has successfully compiled and run
Delphi source code that I have sent him, and so has Rick. So we’re not
exactly starting from zero. Maybe if we all start working together on
getting a common cross-platform programming language going, others will
want to join in and we can think about putting an educational package
together for students. Does that sound like a worthwhile CSG
project?

Best,

Bill P.