Topic: Mixins Considered Silly


Author: djones@megatest.com (Dave Jones)
Date: Fri, 21 May 1993 21:36:21 GMT
Raw View


Author: maxtal@physics.su.OZ.AU (John Max Skaller)
Date: Sat, 22 May 1993 13:21:43 GMT
Raw View
In article <C7EC1x.G5@megatest.com> djones@megatest.com (Dave Jones) writes:
>From article <1993May20.192523.22267@ucc.su.OZ.AU>, by maxtal@physics.su.OZ.AU (John Max Skaller):
>...
>>  No. This is an inherent problem with polymorphism.
>> Templates dont have that problem. Its one of the reasons
>> I'm not a die hard fan of 'object oriented' programming.
>
>"Polymorphism" dates from 1839, so says my dictionary, but to
>computing, it is a pretty new word -- maybe fifteen or twenty years?
>The CS meaning is not in my dictionary. Anyway what I am getting around
>to is, in my lexicon, templates perform a kind of polymorphism, namely
>overloading. Function templates overload function names directly.
>Class templates overload the member functions and access operators
>to the class members.

 I call that genericity.
>
>What is your definition of "polymorphism"?

 Virtual functions.

>For that matter, what do
>you mean by "object oriented programming"? Can't you _orient_ your
>program around _objects_ without using dispatch tables (veetables)?

 No, not really. I can orient my program around User
Defined Types that are value oriented -- which I call ADTs,
and these are implemented with *classes*, but if the thing
is not accessed via a pointer (or reference) its not an
'object oriented' object. :-)

 I agree my terminology is

 a) more specific than the general English notions

 b) entirely determined in the framework of C++

In particular, the general power of ADT classes is severely
weakened by the making the unit of encapsulation the
class: the only excuse for this is it is necessary
for object orientation and polymorphism (that is,
refering to objects by pointers, and using virtual
functions, respectively).

Actually, C++ allows an escape from this (friend functions).
But a proper module construct above the level of the class
would be better for ADT classes .. but disable
(virtual function) polymorphism completely. (As far as I can determine)

Summary: I'm sorry if my unqualified use of general words for
specific ideas leads to confusion .. but I dont know what else
I can do.


--
        JOHN (MAX) SKALLER,         INTERNET:maxtal@suphys.physics.su.oz.au
 Maxtal Pty Ltd,      CSERVE:10236.1703
        6 MacKay St ASHFIELD,     Mem: SA IT/9/22,SC22/WG21
        NSW 2131, AUSTRALIA




Author: djones@megatest.com (Dave Jones)
Date: Thu, 20 May 1993 04:28:10 GMT
Raw View
The "mixin" concept defers until runtime bindings that can be made at
compile/link time. The cost at runtime is possibly increased execution time.
What is the gain? Well maybe in some situations I guess it might result
in smaller object code, so okay maybe it's not totally silly. But I don't
think it is a revolutionary concept either. FORTH programmers have been
calling everything indirectly for decades.

I've edited the "mixin" example code that Max Skaller posted, changing it
to use templates for abstraction rather than virtual functions.

The resulting source code is 1/3 smaller. That's because the virtual base
class declarations become unnecessary. I will admit that their omission is
not pure gain. They do serve as suplimental documentation as to what is
required of the concrete classes and functions. Indeed C++ could have
been defined to require similar redundancy for the use of templates.
However I don't think I would be in favor of that. Such a declaration for
the "types of classes" that a template could take as parameters would
never be sufficient documentation. A human language description such as the
one at the top of the example source file will always be necessary, whether
templates or virtual functions are used for abstraction.

In the edited mixin example, I took the liberty of coding the
functions BinarySearch, BubbleSort, et cetera, as the functions that they
are, rather than as classes. I also removed the cast-away-const, which IMHO
is a big red flag. The "mixin" search-class needed to make a comparison,
but did not "know" what types of things it was comparing! So it made a
demand on client classes that they squirrel away one of the things to
be compared, and that they find the other. In "mixin" technology,
isn't there a way to mix in a function?

BTW, I have briefly used a commercial library that employs virtual mixins
extensively. When I had questions that the documentation did not
answer to my satisfaction, I found it very hard to figure from the
code what was actually going on. Perhaps when I got used to the convolutions,
the reading would have been easier.

Here is the template version...

#include <iostream.h>
// Mixin example using TEMPLATES: sorting and searching
//
// This software will sort and search an indexed set for which
// some total ordering is defined.
//
// The indices are integers 0..n, for some definite n.
//
// It must be possible to compare the data at locations i and j
// and return -1, 0, or +1 depending on the whether the
// ith element is less than, equal to, or greater than the jth element.
// The comparison MUST yield a total ordering or results are not
// guarranteed.
//
// It must also be possible to exchange the ith and jth elements.
// Note this need not imply moving the elements, exchanging
// the integer associated with an element suffices.
//
// Finally, for the search, it is necessary to compare the ith element
// with the candidate element. (In that order!)
//
// The searches and sorts assume ascending order:
// reverse the sign of compare and test to use descending order

// *****************************
// *** Some Implementations ****
// *****************************

template<class T, class A>
int LinearSearch(const A &a, const T &t)
{
  int n= a.nelements();
  cout<<"Linear Searching "<<n<<" elements"<<endl;
  for(int i=0; i<n; ++i)
  {
    int cmp= compare(t, a[i]);
    if(cmp==0)return i;
    if(cmp>0)return -1;
  }
  return -1;
}

template< class T, class A>
int BinarySearch(const A &a, const T &t)
{
  int n= a.nelements();
  cout<<"Binary Chopping "<<n<<" elements"<<endl;
  int lo=0;
  int hi=n-1;
  while(lo<=hi)
  {
    int mid=(lo+hi)/2;
    int cmp= compare(t, a[mid]);
    if(cmp==0)return mid;
    if(cmp<0)lo=mid+1;
    else hi=mid-1;
  }
  return -1;
}

template<class A>
void BubbleSort( A &a)
{
  int n= a.nelements();
  cout<<"Bubble sorting "<<n<<" elements"<<endl;
  for (int i=0; i<n-1; ++i)
    for(int j=n-2; j>=i; --j)
      if(compare(a[j],a[j+1])>0)
 a.exchange(j,j+1);
}

template<class T>
int compare(const T& a, const T& b)
{
  if(a<b)return -1;
  if(a==b)return 0;
  return 1;
}

template<class T>
class Array {
  T * data;
  int n;
public:
  void exchange(int i, int j);
  int nelements()const {return n;}
  T& operator[](int i){return data[i];}
  const T& operator[](int i)const{return data[i];}
  Array(int _n, T *f):data(f), n(_n) {;}
};

template<class T> void Array<T>::exchange(int i,int j)
{
  T temp=operator[](i);
  operator[](i)=operator[](j);
  operator[](j)=temp;
}

template<class T>
ostream& operator<<(ostream& out, const Array<T>& a)
{
  for(int i=0; i<a.nelements(); ++i)
    cout<<"Elem "<<i<<" is "<<a[i]<<endl;
  return out;
}

// ********************************
// ***** Example Mixin ************
// ********************************

template<class T>
class SSArray : public Array<T>
{
public:
  int find(const T &t)const { return BinarySearch(*this, t); }
  void sort() { BubbleSort(*this); }
  SSArray(int n, T *f) : Array<T>(n,f){}
};


// ****************************
// **** Instantiate Floats ****
// ****************************

ostream& operator<<(ostream& out, const Array<float>& a);

float* MakeFloats(int n)
{
 float *f;

 f=new float[n];
 for(int i=0; i<n; ++i)
 {
   cout<<"Give me float #"<<i<<" : ";
   cin>>f[i];
 }

 return f;
}

// **********************
// **** Test Routine ****
// **********************

typedef SSArray<float> Floats;

int main()
{
 int n;
 cout<<"Making Array of Floats"<<endl<<"Please tell me how many : ";
 cin>>n;

 Floats G(n, MakeFloats(n));

 cout<<"Unsorted elements are:"<<endl<<G<<endl;
 G.sort();
 cout<<"Sorted elements are:"<<endl<<G<<endl;

 float x;
 cout<<"Give me a float : ";
 cin>>x;
 while(x!=-1)
 {
   int i=G.find(x);
   cout<<"Found "<<x<<" at position "<<i<<endl;
   cout<<"Give me another float (-1 terminates): ";
   cin>>x;
 }
 return 0;
}




Author: maxtal@physics.su.OZ.AU (John Max Skaller)
Date: Thu, 20 May 1993 19:25:23 GMT
Raw View
In article <C7B5s6.5D6@megatest.com> djones@megatest.com (Dave Jones) writes:
>The "mixin" concept defers until runtime bindings that can be made at
>compile/link time. The cost at runtime is possibly increased execution time.
>What is the gain? Well maybe in some situations I guess it might result
>in smaller object code, so okay maybe it's not totally silly. But I don't
>think it is a revolutionary concept either. FORTH programmers have been
>calling everything indirectly for decades.

 It results in reduced compilation time: thats part of the
advantage of using object code libraries, in theory at least.

 You might well totally ignore polymorphism and use
templates for *everything*. I couldnt argue against that,
provided everything would fit: not reusing object code is
not a sin as much as a practical problem.
>
>I've edited the "mixin" example code that Max Skaller posted, changing it
>to use templates for abstraction rather than virtual functions.
>
>The resulting source code is 1/3 smaller. That's because the virtual base
>class declarations become unnecessary. I will admit that their omission is
>not pure gain.

 Its a not a realistic assesment: mixins are for building
libraries, and some of the routines could be quite big.
Polymorphism dues have *some* advantages surely?

>I also removed the cast-away-const, which IMHO
>is a big red flag. The "mixin" search-class needed to make a comparison,
>but did not "know" what types of things it was comparing! So it made a
>demand on client classes that they squirrel away one of the things to
>be compared, and that they find the other. In "mixin" technology,
>isn't there a way to mix in a function?

 No. This is an inherent problem with polymorphism.
Templates dont have that problem. Its one of the reasons
I'm not a die hard fan of 'object oriented' programming.

 But I think polymorphism has its place, and mixins
are the mechanism that seems to enable their use to
maximum advantage.

 BTW: Tom Cargill was first to point out this fault
in my design: compare simply cant be done polymorphically.

>
>BTW, I have briefly used a commercial library that employs virtual mixins
>extensively. When I had questions that the documentation did not
>answer to my satisfaction, I found it very hard to figure from the
>code what was actually going on. Perhaps when I got used to the convolutions,
>the reading would have been easier.

 Yes, and debugging is a nightmare.  True for templates too though.

--
        JOHN (MAX) SKALLER,         INTERNET:maxtal@suphys.physics.su.oz.au
 Maxtal Pty Ltd,      CSERVE:10236.1703
        6 MacKay St ASHFIELD,     Mem: SA IT/9/22,SC22/WG21
        NSW 2131, AUSTRALIA