
Classes Considered Harmful [pdf] - pottereric
http://web.cecs.pdx.edu/~black/publications/ClassesHarmful.pdf
======
paulrpotts
Having used C++, Java, C#, Python, Ruby, NewtonScript, Dylan, Scheme, and
various other languages "in anger" I can say that in general I agree with the
author's frustrations. My favorite (as in most flexible, least boilerplate)
approaches to object-oriented programming have been:

\- NewtonScript's frames with prototype inheritance. Allows "objects" to
consist of very small data structures that point to ROM objects and only use
RAM for slots that actually vary, using copy-on-write. The actual
implementation had some issues and of course that platform is dead, but the
concept was great.

\- Dylan's adaptation of generic functions, with multiple inheritance. There
are classes but they don't have methods "inside" them. The class hierarchy
describes the dispatch for the generic functions. Allows some really nice
styles of programming (dispatching on multiple parameters) that are perhaps
possible but absolutely hideous in C++.

\- Qt's method. The use of a separate preprocessor step and generated code for
dispatch was a hack to get around limitations of C++, but although you don't
want to look too closely at that generated code if you value your sanity, at a
practical level it works pretty well.

In summary, I'd just add that it is a deep and abiding shame of my industry
that people wind up in silos learning one model of object-oriented programming
and never consider other models that are different and can be cleaner and far
simpler to think about.

~~~
miles7
In addition to Dylan, Julia also allows multiple dispatch via generic
functions.

I have found this approach to multiple dispatch leads to code that's much
easier to reason about and maintain. My use case is a tensor library where the
tensors have a single interface but various storage backend types, which
'interact' with each other. It doesn't make sense to define these interactions
through methods belonging to only one class or the other.

~~~
jghn
Also R via the S4 OOP system, which was directly inspired by Dylan

------
asavinov
I like this kind of critical papers but I cannot agree with the author in this
case. I have studied this problem quite deeply while working on concept-
oriented programming and concept-oriented data model
([http://conceptoriented.org/](http://conceptoriented.org/)) for many years by
also trying to minimize the number of basic constructs. Yet, my conclusion is
that classes are actually needed.

There are several major reasons for that. One of them is that we actually need
two different relations: 1) membership relation, and 2) inheritance (or
inclusion in more general case). Unfortunately, we cannot reduce them to one
relation. If we do (in prototype-based languages) then normally we will still
distinguish the role of this one relation depending on the context.

Mathematically, we have the membership relation '∈' between an element and a
set, and we have the subset relation '⊆'. Just as we need both of them, we
need both class instances and classes. In other words, if somebody argues that
classes are not needed, then it is analogous to the statement that sets are
not needed and it is enough to have only membership relation among elements.
It is possible to develop such a theory but then we will get an alternative
mathematics. Or we will implicitly treat some elements as sets.

~~~
contingencies
_we actually need two different relations: 1) membership relation, and 2)
inheritance (or inclusion in more general case)._

No. You don't _need_ OO at all, let alone bolt-on conceptual baggage like
this.

 _The notion of object oriented programming is completely misunderstood. It 's
not about objects and classes, it's all about messages._ \- Alan Kay[0]

[0] From my fortune clone @
[https://github.com/globalcitizen/taoup](https://github.com/globalcitizen/taoup)

~~~
dkarapetyan
Message passing only takes you so far. As soon as you want to start reasoning
you will structure your messages into groups and now you're back to what looks
suspiciously like classes.

I'm not arguing for one or the other but that extreme late-binding as espoused
in much of the smalltalk way of doing OO only takes you so far. Static
reasoning about program structure is helpful and just message passing alone
does not give you enough structure to do that.

------
Koshkin
The practical usefulness of classes in C++ comes not from an "object-
orientedness" that they supposedly give the language. It comes from the fact
that they are merely a _mechanism_ that can be used to create abstract data
types, thereby providing a better encapsulation, and do many other things,
rather than a _policy_ saying that every object must be an instance of a
class. So, one has to be careful when talking about classes, because they may
effectively mean different things in different programming languages, even
though on the surface they may look similar or appear to serve the same
purpose.

~~~
naasking
Classes tightly couple functions on types with the type declaration itself.
It's often not even clear in which class certain methods belong.

Better to separate data types from functions on data types the way it's done
in functional languages, and if you still want object-oriented programming,
you can support it like Ada does, or as syntactic sugar over the ordinary call
syntax like some functional languages do.

~~~
gertef
Better why?

~~~
wyager
Superior composability with simpler semantics and sounder type systems.

Class hierarchies are inherently unfriendly to strong, sound type systems.
Interface composition isn't.

Composing interfaces is clean and straightforward. Composing classes isn't,
and you run into things like the diamond problem.

~~~
hifier
Classes do not imply inheritance. Classes are a means of defining an
interface, albeit a heavy one.

The statement that it is better to separate data and behavior is far from
self-evident IMO, and I would be very interested to understand why this is
such a deeply held belief. Seriously, why?

~~~
Jweb_Guru
It's not always true, by any stretch of the imagination. Closures couple data
and behavior and most (though not all) functional programmers consider
closures to be pretty central to FP. I'd add that closures are hard to use in
that way without allocating, which is why I think one potentially reasonable
argument against coupling data and behavior is performance-driven, but that's
a more subtle argument that ignores compiler performance (and there are times
where you lose more from all the extra code taking up your icache than you
gain in speed due to not allocating; plus, as C++ and Rust demonstrate, you
can have closures without allocation if you're willing to heavily restrict
them).

~~~
wyager
> Closures couple data and behavior

The use of closures in functional programming is an implementation detail, not
a fundamental aspect of the underlying theory, and closures only "couple" data
and behavior in the very literal sense that it is a function that happens to
point to some data under the hood, not in the syntactic or semantic sense
we're referring to when we say that OOP classes couple behavior and data.

A closure in a pure language can't couple to data any more than a lambda
expression without free variables (from the programmer's perspective), because
they can have the exact same type and semantics.

In other words, there's no semantic difference between "f = \x . x + 2" and "f
= let y = 2 in \x . x + y" even though the latter is a closure in the absence
of inlining.

~~~
Jweb_Guru
Purity seems to be totally orthogonal to this question, to me. I'm referring
to the ability to return thunks from within called functions, such that the
data are totally encapsulated in the closure (are not passed in explicitly and
cannot be retrieved manually). That seems to be coupling data and behavior to
me, since you can't run the function without the data it was coupled with at
construction time. Whether that's fundamental or not is, of course, a
subjective question, but I have a hard time thinking of a language that bills
itself as functional that doesn't provide that capability natively (Rust and
C++ make it difficult, but neither really bill themselves as functional).

~~~
wyager
> Purity seems to be totally orthogonal to this question, to me.

Ah, well there's the confusion. A language with semantically impure closures
emphatically _does not_ reproduce the semantics of the lambda calculus unless
you restrict yourself to only pure, immutable variables. But if you reproduce
the lambda calculus, there's no need for closures as we know them. It's just
an implementation that happens to work well. You could supercompile the
lambda-abstraction at the application site and avoid having a closure. It's
just not a good idea.

But I agree, in a language with mutable variable capture, you can reproduce
the semantics of OOP by providing a bundle of functions closed over mutable
references to the "object" data.

> a language that bills itself as functional that doesn't provide that
> capability natively

I'm not sure exactly what capability you're referring to. Could you clarify a
little bit?

~~~
Jweb_Guru
I'm referring to "information hiding" by providing a function that uses
currying in order to close over data that cannot be accessed from the caller.
For example:

    
    
        (* foo.ml *)
        let bar =
          let foo = lambda x . lambda y .
            if y == 0 then { getFoo : lambda z . 0 }
            else { getFoo : lambda z . (x * z - x + z) / y }
            end
          in foo 3
    
        (* bar.ml *)
    
        include foo.ml
    
        let y = bar 5
        bar.getFoo(4)
    

It sure seems to me that bar is hiding the 3 from bar.ml (and there are much
more complicated examples where foo.ml really can't get at the captured type,
for instance), and at the same time it's capturing the value of 5. The
function getFoo on bar is bound to it (the record type can desugar to more
curried lambdas, if necessary, it's just tedious and this illustrates my point
better).

None of this seems to have anything to do with mutation, to me, though it
certainly seems to have both information hiding and binding logic and data.
The fact that it can be compiled not to use closures (using whole-program
compilation) is immaterial; the same is true for the same program specified
with objects in mutable languages (SML has both mutation and closures, and
MLton does exactly that).

------
ravenstine
There seems to be a common fixation on objects "modeling the real world" with
the counter argument for classes being that they don't model the real world.
True, there isn't a physical chair class in real life(philosophically, there
aren't even chairs), but there is a common abstract _idea_ of what would
describe the function and attributes of a chair. Granted, it differs a little
from person to person, but objects themselves are all different in some way.
As far as I can tell, in most OO languages, there's no reason why classes
_must_ use inheritance, though I am certainly willing to be schooled in that
regard.

~~~
lmm
> As far as I can tell, in most OO languages, there's no reason why classes
> must use inheritance, though I am certainly willing to be schooled in that
> regard.

That there is no precise definition of what a class is and isn't, and that the
traditional notion of a class conflates several independent concepts, is
precisely the big criticism! To my mind "traditional classes" means
inheritance, and recent languages without inheritance (Rust or Go) have tended
to not use the term "class".

~~~
pottereric
Forgive my ignorance. What do Rust and Go call their data structures that are
analogous to classes?

~~~
sidlls
There really isn't a direct analog in Rust. The best approximation in Rust is
actually a combination of constructs in the language: structs and traits.

Structs are almost exactly like the C-objects of the same name: they define
layout in memory for a data structure with discrete types as fields, and there
are various packing options available. However in addition to this a struct-
specific implementation can exist which defines static and instance based
methods. As there is no inheritance in Rust, a struct's "impl" is unique to
itself.

Polymorphism is implemented via the traits system. A trait defines a set of
methods that objects meant to have said trait must implement (unless a default
implementation for a given method in the trait exists). Any struct can have an
implementation for a given trait, so these can be considered a (rough) analog
to abstract base classes/interfaces (e.g. C++ classes that are either pure
virtual or have virtual functions with default implementations).

~~~
lacampbell
You say traits are a rough analogue for C++ base classes with virtual methods.
What are the fundamental differences?

~~~
sidlls
I'm not familiar enough with the internals of how Rust handles v-tables and
the like in light of its other features to answer that competently.

In practice, one defines an interface in C++ by having a (hopefully) stateless
class with pure virtual method declarations, and then classes derived from
this class must implement these methods (in order to instantiate them anyway).
In Rust one defines a data structure (either a struct or enum) and then,
separately, writes the "impl" for it for a trait.

~~~
steveklabnik
The big difference is that when you have a trait object in Rust, it's a double
pointer: a pointer to the vtable, and a pointer to the data. In C++, in my
understanding, you'd have a single pointer to both the vtable and data laid
out next to each other.

~~~
lacampbell
So rust traits are effectively/functionally abstract classes with restricted
functionality and a different implementation?

~~~
steveklabnik
Sort of. The closest thing in C++ would be concepts. That is, even using
traits in this way is the minority case; they're more usually used for
monomorphized, statically dispatched code.

But, given the case where you want dynamic dispatch, then in a sense, they
are, yes.

~~~
lacampbell
Right. I suppose in Rust you generally use variants and pattern matching when
dynamic dispatching is required.

~~~
steveklabnik
You could, but that requires that you know all of the variants ahead of time.
The advantage of trait objects is that you don't have to.

It really just depends on what exactly you're trying to do.

------
macintux
Most eye-opening moment in my software engineering class was when the
professor asked, of four core OO features (don't recall the precise list now),
which was superfluous.

The answer, of course, was inheritance, and once the question was asked it
wasn't hard to see how much better life could be without it. I've been on an
anti-inheritance kick ever since.

Now I see how much better life can be without OO entirely, but that's a
different discussion.

~~~
mjevans
I think that inheritance can have a place. It makes a lot of sense when
writing //small variations// on an otherwise rich inner class. Small
variations might include layering on a different input or output mechanism, or
possibly handling 'extra' data that the object stores but previously did not
modify.

~~~
wereHamster
You can achieve the same with composition or parametrisation. Both of which
are the _only_ tools in a functional language.

~~~
mpweiher
You can achieve the same in any Turing complete language.

However, composition and parametrization require up-front design and are
usually only viable for large-ish variations.

As the parent said, inheritance handles the case of _refinement_ , that is,
_programming by difference_ , which composition and parametrization handle
with difficulty (if planned for) or not at all (if not planned for).

The bad rap inheritance gets is that it is often misused in places where
composition or parametrization were appropriate. But that's just the old "it
hurts when I poke myself in the eye with a sharp stick": don't do that.

[http://www.metaobject.com/papers/Diplomarbeit.pdf](http://www.metaobject.com/papers/Diplomarbeit.pdf)

------
chowells
The biggest problem with classes is that they conflate two different concepts
in a way that limits reusability.

When you have classes, you have subclassing, and subclassing means two
different things at the same time. First, subclasses are the subtyping
operation. If you want type B to be a subtype of A, B must subclass A. Second,
they provide code reuse via inheritance.

Each of those things is useful, but there's no reason to tie them together. In
fact, modern best practices in OO say to favor interfaces and delegation over
subclassing, and that's precisely because it separates those two concerns.

As far as I can tell, this is what the paper was getting at - separating
delegation of implementation from subtyping. Since this is already what OO
best practices suggest, why is it controversial?

------
leblancfg
Although he spends quite some time on exploring the alternatives, the only
reason the author specifically states where classes are harmful is _because
they are premature optimization_. That's it?

I guess "Classes considered a PITA" just didn't make a good title.

~~~
lgas
He delegates to his other paper with "A long discussion of the issues can be
found in my submission to MASPEGHI 2013 [1]". That paper can be found here:

[http://www.cs.jyu.fi/maspeghi2013/papers.html](http://www.cs.jyu.fi/maspeghi2013/papers.html)

specifically:

[http://www.cs.jyu.fi/maspeghi2013/p17-black.pdf](http://www.cs.jyu.fi/maspeghi2013/p17-black.pdf)

------
iokevins
For what it's worth, the footer reads, "Submission to NOOL / 2015/10/12".

NOOL seems to stand for "New Object-Oriented Languages", a workshop of the ACM
SIGPLAN conference on Systems, Programming, Languages and Applications:
Software for Humanity (SPLASH).

[http://2016.splashcon.org/home](http://2016.splashcon.org/home)

[http://2016.splashcon.org/track/nool2016](http://2016.splashcon.org/track/nool2016)

~~~
more_original
And the paper does appear in the program of NOOL 2015(!), so it has been
accepted.

[http://2015.splashcon.org/track/nool2015#program](http://2015.splashcon.org/track/nool2015#program)

------
noblethrasher
At least in the cases of C# and Java, classes and interfaces recapitulate a
weak form of ML-style functional programming rather an impoverished version of
Smalltalk-style object-oriented programming: “Inheritance” from a class
corresponds to logical disjunction (and hence is equivalent to a sum type) and
“implementation” of an interfaces corresponds to logical conjunction (and
hence corresponds to product types). The visitor pattern is a verbose form of
pattern matching.

You can recover the strong form of ML-style FP (albeit without the nice type
inference) if you limit inheritance to abstract classes (thus, all classes are
sealed/final or abstract), mark all fields as readonly/final, and use
interfaces for destructuring (which addresses problems 1,2 and 3 under the
“Alternatives” section).

As an aside, one of the big issues with “classical” OOP is that it enjoins the
programmer to create phenomena using logic (i.e. wizardry) rather than of
using math to model a system (i.e. science and engineering). Among other
problems, this leads to designs that break symmetry (e.g. if modeling a bucket
brigade, the perspective of many humans passing by a single bucket should be
just as valid and available as the “natural” perspective of many buckets
passing by a single human). Of course, appeals to symmetry is question
begging, and I don’t have time to litigate that here, but it sure seems like
symmetry is key to building scalable systems (where scalability means not just
number of users, but also things like number of edits to a codebase).

In summary: Classical OOP seems to require that you either have a prior (well-
designed and debugged) model against which you can program, or really, really
good taste.

------
brudgers
Link to _The Treaty of Orlando_ :
[http://web.media.mit.edu/~lieber/Publications/Treaty-of-
Orla...](http://web.media.mit.edu/~lieber/Publications/Treaty-of-Orlando-
Chapter.pdf)

------
codr4life
"I find OOP methodologically wrong. It starts with classes. It is as if
mathematicians would start with axioms. You do not start with axioms - you
start with proofs. Only when you have found a bunch of related proofs, can you
come up with axioms. You end with axioms. The same thing is true in
programming: you have to start with interesting algorithms. Only when you
understand them well, can you come up with an interface that will let them
work."

A. Stepanov
([http://www.stlport.org/resources/StepanovUSA.html](http://www.stlport.org/resources/StepanovUSA.html))

~~~
keithnz
I think this is more based on the early methodology advice for developing OO
software. But the methodology is independent of the constructs (though often
limited by it).

In practice these days often the most effective way when developing OO code is
working the "proofs" till you have a set of classes.

Also, some people go for very FP inspired OO code.

~~~
codr4life
Those are clearly exceptions to the rule, though. You can write sort of
Haskell in any language, OOP is still bullshit.

------
newtscamander
The premise is the introduction is wrong.

"so before we have written a single program in our language, before we know
whether shared behaviour will be important in the applications that will be
written in it"

read the POODR book, or watch this

[https://youtu.be/OMPfEXIlTVE?list=PL5s3t9kPeAN6aDxaSywIbeFJO...](https://youtu.be/OMPfEXIlTVE?list=PL5s3t9kPeAN6aDxaSywIbeFJOCfm7ixpi)

it explains how shared behavior works without code duplication and why people
tend to mess it up.

------
pdonis
Not a single line of code in the entire paper. Without that I have little or
no idea what the author is talking about.

------
scriptkiddy
I write most of my code in Python. I think that's give me a leg to stand on
when it comes to discussing OOP.

I find that classes, if nothing else, are a fantastic way to namespace data
and methods that operate on said data. For instance, if I have a chair object,
I could use a class to describe the chair. I could store the materials it's
made out of, the height of the seat, whether it is an office chair, a
barstool, or something else. I could also write methods that can say, price
the chair based on data about the chair.

In this particular case, I wouldn't subclass the chair at all. There's no
point. Every time I need a chair, I would just create a new instance of Chair.
That's why chair is an object. It's a self contained thing that I can pass
around and pull data out of or mutate.

I don't really use deep inheritance at all in my code. I think the deepest
I've ever subclassed something is once. I find inheritance to be useful if I
need to be able to create multiple objects that deal with very different
things, but perform similar operations on them. A good example of this is
Django class based views(it's own controversial topic). I created my own
resource class to use for handling API specific requests. The methods of
pulling filters out of the query string and serializing database objects are
exactly the same for every single request, but the objects that these classes
operate on are vastly different. Therefore, I need to specify a serialization
schema for each database object type, but I don't necessarily need to rewrite
the function that does the serialization. I use my Resource class in order to
more easily re-use these generic methods.

Just my two cents.

------
cousin_it
I can buy a language without classes if it gives me these things:

* Interfaces

* Records

* Abstract data types

* All the above should use foo.bar() syntax

Classes provide those things, which is good, but also provide implementation
inheritance, which is bad.

~~~
biocomputation
>> but also provide implementation inheritance, which is bad.

Inheritance isn't bad.

Abusing inheritance is bad.

For the record, you can also abuse composition, polymorphism, operator
overloading, free functions, interfaces, records, and abstract data types.
I've abused them all and seen them abused as well.

Inheritance is just another tool. Like all tools, we are expected to use them
wisely.

~~~
cousin_it
Do you know any situations where implementation inheritance is the best tool?

~~~
mjevans
I mentioned one such example about 6 hours before your post.

As the other reply stated, when you mostly want to keep the base class but
over-ride a small specific subset of what it does.

E.G.

The base class uses class member functions to do save/load records from a
store.

You want to implement a variant of that class which instead works well with a
database engine you like.

You can then derive a class and define JUST the storage and recall functions,
and inherit everything else.

~~~
cousin_it
Interfaces are a better tool for that case IMO. One small utility interface
(save/load) and two implementations would be easier to test in isolation and
wouldn't suffer from the fragile base class problem.

------
usrusr
Funny how when the author finally arrives at the point where he intends to go
from "unnecessary" to "bad", he has little more to say than "there is no space
to repeat it here".

------
bjourne
I like Factors ([http://factorcode.org/](http://factorcode.org/)) approach to
classes. In that language, a class is a category and not a "mold" from which
objects are created. E.g, I can say the following:

    
    
        33 number?
        t
        33 integer?
        t
        integer number class<=
        t
        number object class<=
        t
    

The last two lines demonstrates a little of the class algebra possible. It
says that if an object is an instance of the integer class, then it also an
instance of the number class and of the object class. That is, the set of
objects that are integers is a subset of the set of objects that are numbers
which is a subset of all objects.

I can add new classes which refines categories of existing objects:

    
    
        PREDICATE: positive < number 0 > ;
        PREDICATE: prime-number < integer prime? ;
        17 positive?
        t
        17 prime-number?
        t
        13.4 positive?
        t
        

Note that both real and integer values can be "positive" which means that
"positive" isn't a strict subset of either class. That kind of refinement
isn't possible in most oo languages.

------
antiquark
Hilarious how he blatantly ignores the existence of C++. He talks about
Smalltalk, Java, Grace, Emerald, Pharo... but no C++!

~~~
pjmlp
Many academics love to ignore it.

------
MarcusBrutus
Even in OO languages (such as Java and C#) people are routinely advised to
favor composition over inheritance whenever given half a chance. Classes
definitely smell (or rather reek) of the infamous 90s that inflicted such
atrocities on humanity as the Rational Rose "products" and the UML
"methodologies". It also goes to show why the "typescript" Microsoft strategy
is ill thought. Unless of course it's simply (as I think it is) the Mother of
all Embrace Extend Extinguish strategies in which case it is very cleverly
thought. Sadly ECMAScript has also jumped on the OO bandwagon by offering
"classes" syntactic sugar but at least it is left to the developer to use it
or not, whereas in the Microsoft-land of "typescript" you get classes rammed
down your throat.

------
asavinov
> "classes do not “model the real world”"

Classes are supposed to model sets (of objects), that is, instances represent
real objects and classes represent real sets of objects. So the question is
whether sets are as real as the members they consist of. For example, if a set
of chairs is a reality that we can comprehend and want to represent in the
system.

Yet, I think the problem is that we are probably not happy how classes model
sets in OOP. They actually do not - and it is a major problem. (Classes in OOP
are mostly templates for instantiating objects.) Also, we are not happy with
how inheritance works and it also limits possible benefits of classes. But it
is not a reason to say that classes per se are harmful. I would say that they
are not as good as they could be :(

------
sytelus
Amount of boilerplate one needs to implement "good" OO design is just
horrendous. For example, in C++, previously we had rule of 3. Now its rule of
5! Just having a virtual abstract interface means almost half dozen lines of
boilerplate! Even in managed languages such as C# or Java, it would require
significant effort to create, say, read-only version of collection given a
read-write collection. One of the goal for language design should be to
eliminate all these forced boiler plate. It is definitely the time for new
paradigm in language design. However I'm not sure how the "delegates" would
solve this which is proposed alternative in this paper.

~~~
pakl
I believe Objective C's @property declarations brings some level of what you
describe to C/C++. They generate the boilerplate for you.

E.g., @property (readonly) NSString* myName;

------
ap22213
What is the point of these types of arguments and opinions if they don't have
any evidence to back them? I don't give a shit if it's functional or object-
oriented or procedural or whatever comes next. I just want a language that is
proven, via empirical evidence and reproducibility, to solve my particular use
case better than any others.

And, given all the use cases I have, I'd be amazed if one class of language
can do it all. I'm guessing it's impossible.

It's way better for my engineering team to know how to use a bunch of
different specialized tools, and how to use them together, than to focus on
one type of tool.

------
neo2006
IMO language should be designed to be more effective, simpler or powerful, not
to avoid programers to miss use them. All the "rules" about not using goto for
example are legitimate but people forget that sometimes using goto could be a
good practice in some languages. A semantic is just a tool you can miss used
it`s programer issue not a language/semantic issue. OOP is a way to program
among other we can argue that it's harder than another or easier but we can't
say it's good or bad, it depend on how you use it and hwo effective you are
using it.

~~~
Clubber
An adage I use is you should know all the rules so you know when and which to
break.

~~~
Sophistifunk
The problem is that this requires rules to not actually be rules, but strong
recommendations. Compilers are good at rules, less so with strong
recommendations, which end up in static analysis tooling.

~~~
Clubber
The compiler is fine with using GOTO, it's people that are not.

------
wrongdog
I feel like I just read a lot of opinions by one frustrated dude without a
real sense of _why_ classes are bad (or even really why they frustrated him so
much). Am I missing something?

~~~
hguillermo
No. You are right. Everything can be bad or good depending on how you use it.

------
keithnz
so, the argument is it's harmful because it's a premature optimization? the
evidence being simply alternatives to classes? It seems like the paper doesn't
present a good case for why classes are harmful. Seems the whole premise if
flawed based on a glib quote from Knuth

Premature optimization ( if that is what classes are ) doesn't mean the
optimization is harmful. It could actually be really good.

The quote means, when you find some evil, and track it back, you'd find
premature optimization. You can also find awesome code, track it back, and
also find premature optimization.

So, you need to prove classes are evil, and then track it back to premature
optimization. This paper fails to do that and therefore fails to make the case
for harm

------
kazinator
Paper neglects to discuss CLOS approach: generic functions instead of methods,
etc.

The claim that classes don't do object creation because some operator like
"new" actually does that is jaw-droppingly stupid.

------
newtscamander
The premise is wrong, read the POODR book.

------
lacampbell
The paper was a good read. I was kind of dreading it'd be another tired rehash
of attacking straw men from someone who thinks having used Java or C++ makes
them expert on OOP.

In the early history of smalltalk Alan Kay talks about how he was never
entirely satisfied with inheritance, and having inheritance in the language
(quoting in full, hard to link to):

 _A word about inheritance. Simula-I had neither classes as objects nor
inheritance. Simula-67 added the latter as a generalization to the ALGOL-60
<block> structure. This was a great idea. But it did have some drawbacks:
minor ones like name clashes in multiple threaded lists (no one uses threaded
lists anymore), and major ones like rigidity in the extended type structures,
need to qualify types, only a single path of inheritance, and difficulty in
adapting to an interactive development system with incremental compiling and
other needs for instant changes. Then there were a host of problems that were
really outside the scope of Simula's goals: having to do with various kinds of
modeling and inferencing that were of interest in the world of artificial
intelligence. For example, not all useful questions could be answered by
following a static chain. Some of them required a kind of "inheritance" or
"inferencing" through dynamically bound "parts" (i.e. instance variables).
Multiple inheritance also looked important but the corresponding possible
clashes between methods of the same name in different superclasses looked
difficult to handle, and so forth.

On the other hand, since things can be done with a dynamic language that are
difficult with a statically compiled one, I just decided to leave inheritance
out as a feature in Smalltalk-72, knowing that we could simulate it back using
Smalltalk's LISPlike flexibility. The biggest contributor to these AI ideas
was Larry Tesler who used what is now called "slot inheritance" extensively in
his various versions of early desktop publishing systems. Nowadays, this would
be called a "delegation-style" inheritance scheme [Liberman 84]. Danny Bobrow
and Terry Winograd during this period were designing a "frame-based" AI
language called KRL which was "object-oriented" and I believe was influenced
by early Smalltalk. It had a kind of multiple inheritance—called
perspectives—which permitted an object to play multiple roles in a very clean
way. Many of these ideas a few years later went into PIE, an interesting
extension of Smalltalk to networks and higher level descriptions by Ira
Goldstein and Bobrow [Goldstein & Bobrow 1980].

By the time Smalltalk-76 came along, Dan Ingalls had come up with a scheme
that was Simula-like in its semantics but could be incrementally changed on
the fly to be in accord with our goals of close interaction. I was not
completely thrilled with it because it seemed that we needed a better theory
about inheritance entirely (and still do). For example, inheritance and
instancing (which is a kind of inheritance) muddles both pragmatics (such as
factoring code to save space) and semantics (used for way too many tasks such
as: specialization, generalization, speciation, etc.) Alan Borning employed a
multiple inheritance scheme in Thinglab [Borning 1977] which was implemented
in Smalltalk-76. But no comprehensive and clean multiple inheritance scheme
appeared that was compelling enough to surmount Dan's original Simula-like
design._

~~~
kazinator
_I was kind of dreading it 'd be another tired rehash of attacking straw men
from someone who thinks having used Java or C++ makes them expert on OOP._

It squarely looks like that to me. I'm not fooled by the transparent ruse of
dropping a few names like Smalltalk to appear informed.

------
non_repro_blue
The meme is becoming lame.

Talentless developers who fight and argue are the only real hazard in the
world of programming.

Mature trends and languages have attracted decades of developers good and bad,
and because they're widely used, they have the misfortune of also providing a
home to the largest corpus of terrible code.

Mark my words: in a generation, functional programming will accumulate just as
much garbage, inadequately programmed by just as many careless and/or passive-
aggressive developers desperately clinging to their jobs throughout the next
wave of whatever becomes the next Steve Ballmer stack ranking code review
process.

It doesn't matter how many words or ideas you throw at this circumstance.
Terrible developers will still manage to commit garbage.

Life finds a way.

~~~
jaredklewis
I feel this counter meme (programming languages/language features/paradigms
aren't bad, developers are) also get's thrown around a lot, but I don't
understand the argument.

Are all languages really equally good? Is C really no more productive than
assembly? Is Swift really no more productive than Objective-C? For many higher
level applications is Java really no more productive than C? Doesn't using
something higher level like Python make certain kinds of tasks easier than the
same task in Java?

Since the inception of programming languages, there has been continuous
improvement of the existing languages as well creation of new languages, both
of which have introduced new paradigms and features to programming. These new
features and paradigms often replace older features and paradigms. Not
everything works, but much of the time the new features and paradigms have
made writing and maintaining good programs much easier.

Unless you believe our current tools are already completely optimal, it seems
only natural to try to improve upon them.

So is your argument that programming languages in a generation will be equal
productive/un-productive as what we have now? That seems too pessimistic to
me. I think our tools will be much better, just as I feel our tools now are
better than they were in 2000 or 1990.

If the argument is that functional programming is not an improvement, that's
fine, how do you see languages improving? What's the direction they should
take?

~~~
non_repro_blue
Turing tarpits aside, people speak very highly of C, just as they may do the
same for assembly, but depending on context.

Languages fill a niche, and outside of their area of utility it's easy to
notice weaknesses.

But arguments in favor of functions are similar to those in favor of objects.
They both joust for the award of being adept at doing more with less code, in
terms of mythical-man-month project scalability.

I've experienced that problem with huge OOP code trees, and I still don't
foresee Pure Functional programming supplying a cure for that problem. Maybe
I'm just not one of the lucky ones yet. I don't know.

But when I read Pure Functional code, even though it's tighter, with less
boiler plate, it doesn't feel like honest improvement. It's like Coke or
Pepsi, McDonalds or Burger King.

OOP might yeild thousands of source files and hundreds of lines per some
files, but if a Pure Functional project does the same thing with fewer lines
of code in fewer files, it's still doing the same thing.

So in a decade you'll probably find the same complaints, in the same places,
but at least people will have different hair styles.

------
jackmott
Computer science gets a lot of papers with arguments for a certain way of
doing things.

Very few with controlled experiments to find out if a way of doing things is
better than another.

Every time I mention this, people say that it is hard to do such experiments.
Yes, it is hard to actually know things. while it is very easy to make
plausible arguments, for almost anything.

~~~
tonyplee
Just challenge the promoter to rewrite a c++ base program in their new
favorite method, paradigm.

For example, the author of this paper can try to rewrite the google chrome
browser - a huge C++ project in the "non-Harmful" way.

~~~
nine_k
Rewriting is not very likely.

A new development could use a new paradigm. Same applies to goto vs structured
programming, plain procedures vs classes and OOP-style method dispatch, etc.

------
BuuQu9hu
The E programming language, and its newer relatives like Monte, lack classes;
instead, objects are defined by object literals and the extends keyword
performs object composition by delegation.

~~~
goatlover
You mean it's prototypal, like Self, IO, Javascript?

~~~
abecedarius
No, it's similar to creating an object this way in ES6:

    
    
        function makeVector(x, y) {
            return {
                norm: () => Math.sqrt(x*x + y*y),
                // ... more methods ...
            };
        }
    

but frozen so that consumers can't change it except by calling a method.

It's odd how this is the 'obvious' way to generalize lambda expressions from
functions to objects, yet it's rare to see this style, and you always have to
clarify that you're not talking about prototypes. (IIRC Emerald, mentioned in
the paper, also works something like this.)

