Generic Vs Object Oriented Programming

In an interesting interview at http://www.stlport.org/resources/StepanovUSA.html AlexanderStepanov says: "I think that object orientedness is almost as much of a hoax as ArtificialIntelligence." See ObjectOrientationIsaHoax.


Stepanov is IMO needlessly hostile in his opinions towards OO. I'll try to describe the differences between generics and objects, as I've felt them, briefly in here. The more aggressive discussion follows further below...

Generics cannot:

Objects cannot (in a language which uses StaticTyping; DynamicTyping doesn't have these restrictions):

Workarounds for overcoming the limitations exist in both directions. Java's downcasts are one such mechanism, and you can put objects or something providing equivalent power (functions / collections of functions) into a generic data structure.

Note that there are two kinds of generics:

ObjectiveCaml and HaskellLanguage both have both of the above, the former in the form of modules, the latter in the form of existential classes.


Later in the interview there is the following:

Q: This mean a radical change of mind from both imperative and OO thinking. What are the benefits, and the drawbacks, of this paradigm compared to the "standard" OO programming of SmallTalk or, say, Java?

A: My approach works, theirs does not work. Try to implement a simple thing in the object oriented way, say, max. I do not know how it can be done. Using generic programming I can write:

 template <class StrictWeakOrdered>
 inline StrictWeakOrdered& max(StrictWeakOrdered& x,
 StrictWeakOrdered& y) {
return x < y ? y : x;
 }

and

 template <class StrictWeakOrdered>
 inline const StrictWeakOrdered& max(const StrictWeakOrdered& x,
 const StrictWeakOrdered& y) {
return x < y ? y : x;
 }

Yeah, that's pretty easy. In Smalltalk it's much harder. Implement this method on object:

 max: anObject
^self > anObject
ifTrue: [self]
ifFalse: [anObject]

Much more difficult.

' (you do need both & and const &). And then I define what strict weak ordered means. Try doing it in Java. You can't write a generic max() in Java that takes two arguments of some type and has a return value of that same type. Inheritance and interfaces don't help. And if they cannot implement max or swap or linear search, what chances do they have to implement really complex stuff? These are my litmus tests: if a language allows me to implement max and swap and linear search generically - then it has some potential.

Can someone who knows explain what he is getting at? Is GenericProgramming that much superior?


The name StrictWeakOrdered is just a placeholder here. There doesn't need to be a class named StrictWeakOrdered anywhere in the system. Including a header file containing this code in a source file allows the programmer to invoke max(x, y) anywhere therein, no matter what object types x and y are. However, the statement will fail to compile iff the statement x < y would cause a compile error. Thus, the only restriction on x and y is that operator<() exists, either as a free function taking x and y as arguments, or as a member function for the class of x taking y as argument. If x and y are native types, this is automatic.

In Java, on the other hand, there are no generic functions. Some people consider this good, of course. So, the appropriate way to do this is to have x and y be members of a Comparable class (or to provide an extra Comparator), and declare a utility class:

  public final class Ordering {
public final static Comparable max(Comparable x, Comparable y) {
return (x.compareTo(y) < 0) ? y : x;
}

// Optional: public final static Object max(Object x, Object y, Comparator cmp) { return (cmp.compareTo(x, y) < 0) ? y : x; }

// Min, median, etc. }

You can then invoke Ordering.max(x,y) as appropriate. Note that Alexander Stepanov could have provided a three-argument version too.

The question is, which is appropriate to your needs?

-- EricJablow

It's difficult to see what need there would be for the approach in this examples while using language with dynamic typing. It's a few years since I worked in C++, but back then we used C++ templates to fake up many things that Lisp and Smalltalk people take for granted (closures, strong collections, functoids etc). Apart from that, the advantage of C++ templates, which are only one form of genericity, is a particular form of late binding. It'd be interesting to know what Stepanov would have to say about other forms of genericity.

It's not clear to me quite what he's getting at with this example. Certainly, an OO (or any other kind of) language that has static typing can benefit from some explicit mechanism for genericity. Languages with dynamic typing have other mechanisms for doing the same things.

As for the idea of expressing your algorithms in as abstract a fashion as possible well, probably YouArentGonnaNeedIt, plus, things like Beck's TemplateMethod do something very similar. -- KeithBraithwaite

Java is pretty much screwed without the generics introduced in 1.5. However, for a language like SmallTalk or Lisp you are exactly right. There is no need for templates in these languages. In fact GenerativeProgramming (and unfortunate name choice that is too close to GenerativeCommunication) talks about this at length. A language with a dynamic type system does not need templates. The problem with Java is that it really rode the fence and has neither a static type system nor is it truly dynamic. As a result, it requires even more type casting than a language like C or CeePlusPlus. For this reason, it really requires templates. However, one should not confuse genericity with GenericProgramming. -- RobertDiFalco


Stepanov, although clearly a very smart person, apparently never recovered from that bacterial infection that sparked off the development of the StandardTemplateLibrary. -- AndrewQueisser


The problem I have with both the C++ and SmallTalk ways of doing things is that they don't explicitly specify the requirements for the arguments to max(). I'm very happy with Java's interfaces and would like to see a Java-like language that looks something like this:

 interface WeaklyComparable? {
boolean operator<(ThisClass? other);
boolean operator>(ThisClass? other);
 };

int implements WeaklyComparable?; float implements WeaklyComparable?;

where class T implements WeaklyComparable?: boolean max(T a, T b) { if(a<b) { return a; } else { return b; } }

That is, the user of a class should be able to retroactively declare that the class implements an interface, provided that it already supports all the methods. (If not you might need to write an adapter class.)

Note also that max() doesn't use the greater-than operator but requires it anyway. This allows the implementation of max() to be more easily changed later.

(Also, built-in-types should be structs, like in C#.)

(Also, the type system needs to make sense unlike my example.)

-- BrianSlesinsky

The problem with this is that you'd need to explicitly recognize and assert that your type implements every single property/interface that you use on it. That means you'd have to keep going back to your original type and changing its definition. This is no better than declaring an object that implements many interfaces. The advantage of the GenericProgramming approach is that the definition of the object implicitly specifies the ways it can be used. In other words, if it has a < operator, it must also have a strict-weak ordering. The Ruby example below is also nice; but it's fundamentally different from the C++ example in that the C++ version won't be used unless it can be compiled, i.e. unless the < operator has been defined. This allows one to do interesting recursion with templates. See CompileTimeGenericAverageFunctionInCeePlusPlus. -- DavidKTurner


This code above looks almost exactly like Haskell typeclasses. Standard typeclasses are Eq for equality, Ord for ordering (requires that the type already be an instance of Eq), Num for numeric types, etc. It's easy to make new typeclasses also

this is some helpful demo info: http://www.syntaxpolice.org/lectures/haskellTalk2/slides/x169.html there's a simple typeclass in my Haskell tutorial for the impatient. I create a CharExts? class that has isVowel and isConsonant methods. http://www.scannedinavian.org/AvianWiki/HaskellDemo

As for true generics in Haskell, there's much more cool stuff, check this out: http://www.scannedinavian.org/AvianWiki/GenericsDemo In this demo, the XML typeclass is defined, and later how to turn different types into XML. composition is defined separately, you don't need a type specific visitor method for example.

Another step past this is GenericHaskell?, which is much more powerful than this basic support of Generics. -- ShaeErisson


Ruby can do this, only nicer. -Snippet (test.rb):

  x,y=12,23.7

def max(x,y) raise ArgumentError? unless x.respond_to?("<") return x<y ? y : x end

print "x=#{x}, y=#{y}, max(x,y)=#{max(x,y)}\n"

def max(x,y) unless y.kind_of?(Comparable) and x.kind_of?(Comparable) raise ArgumentError?, "Arguments must be Comparable!", caller end return x<y ? y : x end

print "x=#{x}, y=#{y},max(x,y)=#{max(x,y)}\n" x=proc { print "bla" } print "x=#{x}, y=#{y},max(x,y)=#{max(x,y)}\n"

-Output:
  >ruby test.rb
  test.rb:19: Arguments must be Comparable! (ArgumentError?)
  x=12,  y=23.7,  max(x,y)=23.7
  x=12,  y=23.7,max(x,y)=23.7
  >Exit code: 1

-- PeterThoman? (my first ever Wiki edit, hope this works)


Can someone who knows explain what he is getting at? Is GenericProgramming that much superior?

I think this is a silly argument. No OO does not provide a mechanism for easily creating simple functions or programs. The intent is to manage large numbers of functions in complex programs. The value OO brings is scoping. It binds functions to their appropriate types. Note that the "generic" max() function fails when used with dissimilar data types or with types which do not have the > operator defined. -- WayneMack

Right, and I think the current discussion of generic-vs-OO doesn't address the "scoping" advantages of OO languages. Those advantages are relatively well understood and the discussion has moved on. What's being discussed is whether the interface of a class should be tied to its type. In SmallTalk and other dynamically typed languages type and interface are not directly tied to each other but in C++ a regular (non-template) function can only accept arguments of a specific type or subtype. A template (generic) function can accept anything that supports the operations used within the function (max needs the < operator.) The main difference now is that interface mismatches are detected at compile time (C++) or runtime (SmallTalk, etc.) It seems to me that the generic-vs-OO discussion is carried out mostly within the C++ community because for the dynamic typing crowd there is nothing to discuss. (I'm extrapolating here, being C++ myself.) -- AndrewQueisser


WayneMack identifies a problem when the max() function is called with the wrong sort of object. This problem has been addressed, by using SetsOfRequirements. There is a lot of information on this in TheBoostGraphLibrary. -- JohnFletcher


BertrandMeyer gives an excellent summary of the debate in ObjectOrientedSoftwareConstruction; for a summary of his summary see GenericsVsSubtyping


The expressive difference between Generics and ObjectOrientation is in:

  1. How easy it is to write a program.
  2. How easy it is to read a program.
  3. How easy it is to debug a program.
  4. How easy it is to modify a program.

If you think StaticTyping is better than DynamicTyping because you always make mistakes on method parameters, then you will very easily fix that 5% of the typing mistakes. Most mistakes are not typing mistakes though, so they can't be caught by StaticTyping.

Writing Generics doesn't seem simpler than writing ObjectOriented programs.

Consider the following template declarations:

 typedef xstr::xstring<
xstr::fixed_char_buf<64> > 
small_string;

template <size_t SIZE, class CharT=char, class Traits=std::char_traits<CharT> > class fixed_char_buf;

Contrary to what rookie developers may think, the above declarations are simple compared to real production code that uses templates extensively. And that's nothing compared to the compiler errors you will get and the difficulty for debugging it. And only to achieve a 5% speed increase that never materializes because you are always debugging your code.

Compare with:

template<class R0, class R1, class R2>

_bi::bind_t<R0, Find_SZ R0 (Find_CCH *) (R1), typename _bi::list_av_1<R2>::type>
Find(Find_SZ R0 (Find_CCH *f) (R1), R2 r2)
{
typedef Find_SZ R0 (Find_CCH *F) (R1);
typedef typename _bi::list_av_1<R2>::type list_type;
return _bi::bind_t<R0, F, list_type> (f, list_type(r2));
}

Also keep in mind that the code above has another disadvantage: whenever you declare new variables, there is the possibility of creating a new class at compile time, just because the parameter happens to be sightly different. The code is therefore massive and it takes too long to load.

-- GuillermoSchwarz.


CategoryCpp CategoryCppTemplates CategoryPolymorphism CategoryProgrammingLanguageComparisons


EditText of this page (last edited December 5, 2004) or FindPage with title or text search