
A Square Is Not A Rectangle - raganwald
http://cafe.elharo.com/programming/a-square-is-not-a-rectangle/
======
silentbicycle
Computer programming is young enough as a field that the mainstream hasn't
realized it isn't possible to fit everything into one unified taxonomy.
(Biologists and librarians have both spent over a century trying, and they
have a lot of insight on the matter.) Putting types into a hierarchy is a
useful abstraction sometimes, but there's not some Grand Truth in it -- often,
attempts at cramming disparate elements into a hierarchy just create extra
complexity. Most systems will keep trying to turn back into graphs unless you
anchor the hierarchy in a specific context (what does 'a IS-A b' mean,
really?), and the context tends to shift with the problem definition.

Worse still, it's the sort of complexity that _looks like work_ , even though
it's usually more like _flailing in quicksand_.

I think not having to deal with class hierarchies is a big reason why some
people find dynamically-typed languages so freeing. I'm still kind of new to
Erlang, but I think its push towards modeling things via a graph of
communicating processes, with a relatively flat type structure, is worth
considering as an alternative. It explicitly focuses on the message protocol
between actors, rather than a tree of subtypes. Granted, I haven't worked a
large enough project to see it break down and get ugly yet. (C++ style OO
sounds good on paper, too.)

Regardless, it isn't classes per se that lead to OO's problems, but the
tendency to overuse inheritance. Among statically-typed languages, Haskell's
typeclasses seem like another good solution - they break the hierarchy of
inheritance up into composition of properties.

Also, the late-binding in OOP means that any class can potentially be
responsible for maintaining its invariants even after any of its methods has
been overridden by _any possible future subclasses._ That's not necessarily
true even with dynamically-typed languages.

~~~
joe_the_user
Your points concerning inheritance are good but "the field is young" seems
like just a pseudo-profound explanation for the situation. CS as a field
really can't be compared _in this way_ to other fields - it's not really a
science since it doesn't deal with the discovery of verifiable facts. It is
more like some combination of mathematics, humanities, management and
engineering.

CS has to connect very fuzzy things, human beings, with very exacting things,
computers. The problems of CS aren't really about the age of the discipline
but difficulty of the domain. For example, when you start to get into biology-
as-programming-with-DNA, you are dealing with an even more complex domain and
biologists look to CS for clues in this situation.

~~~
silentbicycle
That's why I mentioned librarians. Professional catalogers have spent over a
century trying to figure out how to organize resources so that people doing
research can find relevant information, and classifications that are useful
for everyone prove surprisingly elusive. The Dewey Decimal system is from the
1870s, after all. (Its flaws are well-known, now, and there are several other
cataloging systems in use.)

I was thinking about a quote (I _think_ it's from Philip Greenspun or Joe
Armstrong, though in ten minutes of googling I wasn't able to source it.
Anyone?) observing that programming seems to be in a pre-scientific phase (in
the Kuhn-ian sense) -- there still isn't a broad consensus about major ideas,
so advances often come via books and manifestos, rather than gradually through
organized research.

(Edit: The closest match I'm getting is regarding AI. I've been reading about
Prolog, so that might be where I read the quote...)

~~~
joe_the_user
Hmm,

The thing is that the phrase "pre-scientific" implies that CS is destined to
reach a scientific phase. I don't know if that's at all certain.

... Another thing, just to think about, is how Google today often lets one
find more cross-links than any hierarchical system would allow you to find,
yet Google is, itself, not a well defined system using the semantics of the
items involved but rather a "dumb" algorithm that only looks at the links
between things...

~~~
jerf
I don't know if programming (as opposed to computer science, in the research
sense) will ever attain "science" status because we don't stay in one place
long enough to be settled. If we were all still writing terminal apps on 80x25
green screens, the science would be pretty well settled on how to do that by
now, with recipes for every occurrence that any normal programmer would ever
hit readily available, and easy access to people who could do the somewhat
harder cases.

"What about the really hard cases?" you might ask, and the entire point of my
hypothetical is that if we can't do it in 80x25, we _don't_ do it.... and the
contrast to the real world where we kept going is exactly my point. So, when
will programming finally stabilize enough to become a science? I don't know,
but I don't see it even in the long term (30+ years).

------
KevinMS
Problem is from historical conventions of what a square and rectangle are.

A square is not really a thing, its a description of a thing. In this case,
proportions of the sides of a shape.

So when you translate this into object oriented language, you are subclassing
a concept with one of its potential variations. It would be like making a
class called MusicalNote and then a subclass named MusicalNoteC, even though
MusicalNote already has it covered.

So "square" shouldn't be a subclass because its just a proportion of the data
within "rectangle", so more appriopriately, 'square' would be a method of
Rectangle since it deals with internal data.

------
barrkel
This just comes down to the difference between identity that changes with
mutation, and identity which is constant with mutation.

All squares are rectangles. Some rectangles are squares. If the object used to
represent squares and rectangles is mutable in width and height, then its
squareness cannot be immutably associated with its identity, and thus cannot
be encoded as a type, since objects in static languages have immutably
associated types.

There's some deep point about naming objects after their attributes, without
paying attention to whether the attributes may change. Experienced programmers
know this well, but laypeople don't need to make the distinction often as
their language usage is usually only temporary, not needing to account for
changes over time. It's the statement that must always be true, when all the
right qualifiers and conditions have been included, that's hard to make.

~~~
joe_the_user
No, there is no easy answer to this problem in a way that jives with all
doctrines and also works in practice.

OO is ambiguous as to whether a "subclass" of an object is _specialization_ of
the parent object or a generalization. In practice, most subclasses are a
generalizations and specializations introduce all sorts of problems,
especially enforcing constraints.

This naturally comes down to OO trying to shoe-horn (at least) four related-
but-not-necessarily-identifical activities together: code/data encapsulation,
program design, code-reuse, and modeling (the translation of real-world
objects-and-categories to code).

The net result is that OO languages, tools and methods _can_ be useful for
writing large program and _can_ give you effects that are better than the
alternatives but that the pronouncement of OO gurus wind-up sounding idiotic
and following the book to the letter can create situations that negate the
particular benefits of OO.

~~~
barrkel
By Liskov substitution principle, or with the set view of types, subclasses
are always specializations / subsets, unless you're introducing some kind of
home-baked type system built on top of the existing type system.

I would classify the parts of OO differently. I think your second and fourth
activities are the same, but one models abstract concepts, and the other
models concepts of concrete things. I wouldn't bring in code reuse at this
level; every practical programming paradigm tries to include code reuse. It's
the mechanism that's a distinguishing feature, and the mechanism OO uses is a
combination of subtype polymorphism and dynamic dispatch. It's a baked-in
version of the function table / union|variant record idiom from procedural
programming, and it has a transposed expression in sum types and pattern
matching in functional programming: OO limits the addition of new (virtual)
methods (on the base) but accepts the creation of new types (even at runtime),
while FP limits the creation of new types (in most implementations I've seen)
but accepts the addition of new methods (which costs some in discoverability
in modern IDEs, and has namespace implications).

~~~
joe_the_user
Hmm...

 _By Liskov substitution principle, or with the set view of types, subclasses
are always specializations / subsets, unless you're introducing some kind of
home-baked type system built on top of the existing type system._

So, does that mean that subclasses shouldn't have extra member variable in the
way that a square doesn't have any extra member variables in inheriting?? I
know that there are some systems which try to do something like this - Darwin
and Date's Third Manifesto, for example, though they don't try to say they're
doing OO. And, of course, this is far away from what most people do in
defining objects.

The various tools and approaches are useful and your particular take on it
might make it consistent if it was generally accepted. But at this point, the
many confusions about what OO are set in people's minds. What's being defined
now is Agile - which is naturally another semi-original, semi-useful, semi-
contradictory toolset.

But hey, I guess my conclusion is it's all good if you don't take it too
seriously and do stupid things just to satisfy some absurd constraint - like
in the hay-day of OO when people let go of common sense and built these huge,
fragile multiple inheritance trees that wound up not having the slight
understanding of - or used ten separate, nested classes to define the complex
numbers...

~~~
barrkel
> _does that mean that subclasses shouldn't have extra member variable_

Not sure what you meant here. Extra members, whether they be properties,
fields, methods, types, whatever, should be fine in subclasses. If it was
removing members from the superclass, that would be problematic from a
subtyping / polymorphic perspective, but adding members makes it more
specific.

------
raganwald
I have a simpler dictum: Square was-a Rectangle. In other words, we wish to
implement a square using the rectangle class, but we do not acknowledge that a
square is a rectangle at all. Implementation inheritance should sometimes be
independent of interface inheritance.

If I was forced to use interface inheritance for this, I would say that both
squares and rectangles are abstract quadrangles, but rectangles allow
individual sides to be set and squares do not.

~~~
yannis
The article is good at pointing the logic faults.

As you correctly say, the abstraction is wrong. If you were abstracting
classes for a graphics program the abstraction could even go one level higher
in abstracting p o l y g o n s, rather than rectangles and squares. You can
even have crossed rectangles (<http://en.wikipedia.org/wiki/Rectangle>).

Using such an abstraction IMHO any polygon shape would consist of line objects
which would consist of points (x,y and even z)!

OOP abstractions can sometimes be masochistic! ;)

~~~
toadpipe
_> OOP abstractions can sometimes be masochistic!_

You think?

The article is a train wreck of analysis piled on the train wreck of "OO
Design", which is itself a train wreck of an attempt to promote reuse by
modeling code with data types, which is itself a train wreck of an attempt to
imagine what hypothetical programmers in some hypothetical universe might find
"useful" as masturbatory aids, hypothetically speaking. (This is not actually
true - OO[D] is really an attempt to commoditize programming.) Most of the
comments here are just dumping more trains on the pile. The abstraction
shitheap is so high at this point that we can barely even remember that we are
supposed to be solving some kind of actual problem.

The best part of the article is when the author acknowledges that the code
might not actually need to be thread safe and so avoids wasting his time with
that particular assumption. There is nothing else to be salvaged. A square is
not a rectangle? Goodbye mathematics - what is the point of a type system
again? More to the point: what planet are the users of this API from?
Obviously not this one, and just as obviously no one knows or cares. Not ONCE
does the author stop to wonder what services the actual users actually need;
it's only about what this guy thinks MIGHT comply with some random "design"
rules. One wonders if the users even exist as anything other than just another
abstraction, and wondering about what problems those users might actually be
trying to solve is right out.

Object Oriented Design can be seen as a case of reusing a cliche and
butchering it at the same time, with the cliche being the shooting of ones
self in the foot, and the butchery being the placement of said foot squarely
upon the face of beauty immediately before pulling the trigger.

~~~
barrkel
I think you're going _way_ overboard with your cynicism and criticism of OOP -
cynicism bordering on paranoia. I've read your other comment, alleging that
OOP "was pushed by manager types who just wanted as many reports as possible".

The dominant paradigm before OOP was procedural programming, and it had (and
has) real problems: tendency towards storing data in globals, lack of a
machine-checked mechanisms for subtyping, less discoverable APIs (often a
namespacing issue), and difficulty in sharing code when representing
abstractions where many kinds of values need to be treated very similarly but
with little differences, GUI widgets in particular.

OOP is unmistakably a better approach for a large class of problems, primarily
because it encodes good procedural practice into well-defined concepts that
are checked by the machine, either at compile time, run time, or both.

Functional programming has a different kind of benefit: whereas OOP helped
modelling values, FP helps modelling algorithms. Because strict FP demands
pure functions and immutable data, you can reason about complex functions and
expressions knowing that there are no side-effects that can bite you. The
benefits of FP over OOP aren't as clear as OOP over procedural programming, as
taking the strict approach makes state management problematic and limits
freedom in data structure design and ultimately algorithmic complexity, but
the benefits are very clear in certain areas.

~~~
toadpipe
I note that every advantage of OOP you cite is a modularization feature (even
subtyping, since OOP basically conflates modules with types).

Clearly you are not entertained by rants based on my experiences, so maybe I
should just cut 'n paste pg:

"Object-oriented programming is exciting if you have a statically-typed
language without lexical closures or macros. To some degree, it offers a way
around these limitations. (See Greenspun's Tenth Rule.)

Object-oriented programming is popular in big companies, because it suits the
way they write software. At big companies, software tends to be written by
large (and frequently changing) teams of mediocre programmers. Object-oriented
programming imposes a discipline on these programmers that prevents any one of
them from doing too much damage. The price is that the resulting code is
bloated with protocols and full of duplication. This is not too high a price
for big companies, because their software is probably going to be bloated and
full of duplication anyway.

Object-oriented programming generates a lot of what looks like work. Back in
the days of fanfold, there was a type of programmer who would only put five or
ten lines of code on a page, preceded by twenty lines of elaborately formatted
comments. Object-oriented programming is like crack for these people: it lets
you incorporate all this scaffolding right into your source code. Something
that a Lisp hacker might handle by pushing a symbol onto a list becomes a
whole file of classes and methods. So it is a good tool if you want to
convince yourself, or someone else, that you are doing a lot of work."

<http://www.paulgraham.com/noop.html>

~~~
barrkel
Modularization is independent of programming discipline; you can have modules
in procedural programming, OOP, FP, etc.

I didn't argue that OOP is exciting, just that it is not a conspiracy
perpetrated by a business elite to reduce the productivity of the average
programmer.

And appeal to authority won't work on me, sorry. FWIW, I think pg goes too far
in his rant - I think he mistakes a certain strand of Java enterprise
development in large corporations for all business OOP.

The programming languages I use most often outside of C - Delphi (I maintain
the compiler, written in C) and C# - both support closures, and of course C
supports textual macros. As a compiler engineer I'm not oblivious to the fact
that code is data and vice versa: turning code into data, and data into code
is my meat and potatoes, as is symbolic manipulation of code trees (I
implemented Delphi's closure support). But there's a downside to treating all
the world as a list, and building all your structures out of conses: lack of
documentation, encapsulation, machine checking. These things matter when
you're trying to build an economy of software modules in a closed source
world.

In my own past in business development, I even used call/cc for certain web
client / server dialog scenarios, using a DSL especially built for GUI data
binding and events over an AJAX protocol. Assuming that all, or even the
majority, of business developers are inefficient bumblers performing make-work
is foolish, insulting and extremely myopic.

~~~
toadpipe
Modularization is indeed independent of programming discipline. That is
basically my point. Breaking a program down into the correct components is
all-important, but most of this happens well below the module level. Unless
the system has been over-engineered to death, as OOP encourages.

Three cheers for not being swayed by appeal to authority, but I'm curious
about what you think counts as "business OOP" that doesn't count as
"enterprise," as "enterprise" seems vague enough to encompass pretty much
anything.

Your critique of lists and conses is again centered on the notion that
documentation and encapsulation should be done in code and enforced by the
machine. These things may well be necessary to build an economy of software
modules in a closed source world, but the original article is just one example
of the brain damage that this sort of world creates.

I'm glad you've been exposed to functional concepts, and I don't think that
business developers are idiots, but I do think they live in an environment
that is by and large toxic to good programming practices. I'm not paranoid, I
just see a lot of evidence for the validity of Conway's Law.

------
akeefer
Beyond just the obvious "this is a bad way to design things" message, another
good takeaway is just the fact that most OO-design tutorials/discussions/books
use the worst possible examples that teach people bad habits.

When done well, the useful part of OO design is encapsulation and data-hiding,
not inheritance, but everyone gets hung up on inheritance because it's easy to
cobble together "is-a" examples from real-world-like things, which just trains
people to abuse the model.

In the real world outside OO-design books and freshman CS classes people don't
have separate classes for different shapes, they just have "Polygon" or
whatever the graphics buffer requires. And no one has separate subtypes of
"AbstractVehicle" for "PassengerCar" and "Motorcycle" and "CommercialTruck,"
they just have a "Vehicle" class with a bunch of data on it and rely on the
client code knowing what to do with that data.

~~~
iron_ball
This is why Eric Evans' "Domain-Driven Design" should be on every programmer's
reading list. He applies object-oriented principles (though not exclusively)
to real-world-sized problems, frequently pointing out the traps inherent in
the enterprisey languages most big systems will use.

------
jhancock
I understand the author is trying to show pitfalls of OO design. But the
square/rectangle example doesn't do it for me.

1 - "There are actually several problems here. Thread safety is one, but let’s
assume the class doesn’t need to be thread-safe."

The Rectangle implementation has a thread issue without regard to the Square
subclass:

    
    
       public double getPerimeter() {
        return 2*width + 2*height;
      }
    

2 - "In object oriented programming, it is necessary that a subclass be able
to fulfill the contract of its superclass. In this case, that means the square
has to respond to setHeight() and setWidth() calls. However doing so enables
the square to violate the nature of a square. A square cannot stand in for a
rectangle.

You can try to work around this by overriding the setHeight() and setWidth()
methods. For example, one might call the other:...

However this is fundamentally unsatisfying because there is no reasonable
expectation that calling one of setHeight() on a Rectangle object will also
invoke the setWidth() method or vice versa."

This is where things unwind. It is perfectly expected that calling setHeight()
on a Square will also change its width.

3 - "We could instead just forbid setHeight() and setWidth() completely by
throwing UnsupportedOperationException"

Yep, you could do that and it does show you are doing more work in working
around using Square as a subclass of Rectangle. So either don't subclass it or
accept the fact that its perfectly normal that if you have a Square, changing
either its width or height will effect the other.

The author is trying too hard here. Its decent writing and a good attempt to
show how subclassing can lead to unintended consequences. But this is not a
great example.

~~~
txxxxd
"It is perfectly expected that calling setHeight() on a Square will also
change its width."

If you're writing code that operates on Rectangles you wouldn't expect
setHeight to have the side-effect of changing the width too.

It's only "perfectly expected" if you know about the implementation details of
Square, which may not even have existed until after a lot of Rectangle code
was already in place.

~~~
endtime
>It's only "perfectly expected" if you know about the implementation details
of Square, which may not even have existed until after a lot of Rectangle code
was already in place.

I don't quite agree. You don't need to know implementation details of Square
(e.g. does SetHeight set the height and call SetWidth, or does it just call
SetSide? Or does it, as well as SetWidth, set some new property called side?),
you just need to know how a Square differs from a Rectangle (width == height).

Because Squares are a subset of Rectangles, any Rectangle code should also
work with Squares. Even without Square, prevents you instantiating a Rectangle
and always calling SetWidth(x) and SetHeight(x) one after the other.

------
ubernostrum
Sigh.

Those who do not read Quine are doomed to reproduce his results, as I remind
people every time Uncle Bob or someone else of the cabal starts going off on
this problem:

[http://www.reddit.com/r/programming/comments/7zy40/writing_u...](http://www.reddit.com/r/programming/comments/7zy40/writing_unit_tests_is_reinventing_functional/c07w5bx)

~~~
davidmathers
I'll see your Quine and raise you a Bolzano:

<http://plato.stanford.edu/entries/bolzano/>

<http://plato.stanford.edu/entries/bolzano-logic/>

------
davidmathers
Aka: <http://en.wikipedia.org/wiki/Circle-ellipse_problem>

~~~
DannoHung
This smells like a job for Type Classes.

------
noblethrasher
Studying functional programming can really illumniate certain concepts in
object oriented programming. The solution is a sum type which you can simulate
in OOP using an abstract base class (in C# parlance) and a set of sibling
classes that derive from it. So, as other have mentioned, you could have an
abstract Polygon (or quadralateral) base class

------
gcanyon
I had an instructor once who started a lecture with this example, and then
argued that rectangle should be a subclass of square.

------
tigerthink
Mirrored from Google's cache: <http://jottit.com/qykre/>

------
tetha
Well, the problem in my opinion is, that a square is a rectangle, indeed.
However, this is not square is-a rectangle;, but rather: square is-a rectangle
with rectangle.height == rectangle.width;

This is overall something I have been pondering (and recently planning and
implementing) to extend the standard object orientation: adjectives (opposed
to 'nouns' (objects) and 'verbs' (methods)).

An adjective would simply describe a noun furhter and simplify a dispatch
inside a method depending on the state of an object.

Some pseudosyntax from the current state of ideas would go like this:

    
    
      adjective_group rectangle_type(R) {
         if(R.width == R.height) {
           return square;
         } else if(R.width * 4 = R.height * 3) {
           return widescreen;
         }
       }
    
      draw(square rectangle R) {
         // specialized draw for squares
       }
       draw(widescreen rectangle R) {
         // specialized draw for widescreen rectangles
       }
       draw(rectangle R) {
         // general drawing method for rectangles
       }
    

This goes pretty deep if one starts to think about it: * Buffers: empty, full,
something in between * Abstract syntax trees: well-typed, ill-typed *
Accounts: banned, active, moderator, admin

Basically, it simplifies the pattern

    
    
      method(object) {
        if (predicate_1(object.state)) {
          // stuff
        } else if(predicate_2(object.state)) {
          // other stuff
        } else {
          // default stuff
        }
      }
    

I am just currently working on a dispatch mechanism which is at least a bit
intuitive :)

~~~
silentbicycle
Have you ever used a language that makes heavy use of pattern matching?
Prolog, in particular, or to a lesser extent (due to increasing amounts of
distraction by other novel features) Erlang, OCaml/SML, or Haskell. It sounds
like you're essentially describing functional dispatch based on structural
attributes or current characteristics, rather than a (generally fixed) object
identity.

~~~
tetha
yes, this is where a large part of the idea comes from.

------
JulianMorrison
My Haskell way of thinking of it would be that Square and Rectangle are
distinct (non-inheriting) data types, Square and Rectangle are both instances
of the type-class Rectangular (which inherits and provides default functions
for Closed and Polygonal), Square has a transformation into Rectangle, and
Rectangle has a transformation into Maybe Square.

------
flatline
OO is an incredibly powerful tool but is unnecessary for so many tasks. The
contrived examples of many books show beautifully, as does this example,
simple inheritance hierarchies that get at the gist of it but are not really
an appropriate use of the technology. Everything does not need to be modeled
as an object, oftentimes with only one or two methods, no internal state, etc.
Some of the newer multi-paradigm languages (F# and Scala come to mind at first
glance) seem poised to rescue us from improper use of OOP when you are stuck
in an OO environment with a non-OO pattern or problem to solve. You get the OO
stuff when you need it, with a more flexible syntax, without being strapped
into a design straightjacket.

------
Tichy
"Another is that it’s possible to give the sides negative lengths."

Is that really a problem that needs fixing? Also the with/height problem - one
could argue the fault would lie with the code that tries to do both...

A while ago I had a realization as to what defines the Java developer mindset:
it is fear driven development (FDD).

(Taking Java as an example because it is the only world I know a little bit
better).

Maybe sometimes FDD is being called for, or maybe it really is the superior
development style. Personally, I am quite sick of it, and it definitely makes
it harder to get things done in my opinion.

------
fsniper
If you believe OOP is "making physical objects' memory maps", instead of
"related data put together" then yes there are some problems on some
conditions. But this is caused by unrelated physical mapping need. If you can
let go about this belief and think a bit lower level, OOP is a good enough
methodology for thinking "like" objects. But it's not perfect. I don't know if
this mapping need is good for software engineering.

------
scharan
I really like this comment from Uncle Bob: Inheritance is _not_ ISA. It is a
redeclaration of variables and functions in a sub-scope!

[http://cafe.elharo.com/programming/a-square-is-not-a-
rectang...](http://cafe.elharo.com/programming/a-square-is-not-a-
rectangle/#comment-436689)

------
igrekel
Funny I remember seeing a library (used somewhere internally) where the
Rectangle inherited from square. It was a surprising discovery when reading
the codebase but the implementation as odd tough.

------
r00k
Make sure to check out Uncle Bob's comment (it's first after the post.)

------
apotheon
403 Forbidden

Damn. Did I miss it?

~~~
apotheon
Okay, so I came back finally and checked again to see if I could get in.

My thought, now, is that a functional approach to making square a "subclass"
(by stretching the term "subclass" out of the confines of its traditional
Java-style OOP usage) of rectangle.

    
    
        (define (square s)
          (rectangle s s))
    

Simple. Yes?

------
spooneybarger
a square is indeed not a rectangle therefore i see this as the only flaw that
needs to be pointed out:

public class Square extends Rectangle

inheritance for the sake of implementation is bad OO design.

~~~
raganwald
It is an old concept that got ignored somewhere along the road to java.
Inheritance for the sake of implementation is called WAS_A. In C++, this is
what private inheritance does. In languages like Java and Ruby, WAS-A is often
implemented using delegation, so that a square HAS-A rectangle.

------
graywh
What about the rhombus?!

