[comp.object] Readability of Ada

jls@rutabaga.Rational.COM (Jim Showalter) (04/17/91)

A short while ago, I posted an example of what I view as fairly
typical C code and asked people what it did. Given that the code
was, as usual, written with two-character "identifiers" and
maximum utilization of C's cryptic syntax, it took people a while
to figure out that the intent of the code was to compute the Julian
date. Even then, all but a handful of people missed the two bugs
planted in the code.

Some attacked the example as a BAD example of C code, and the claim
was made that C could be written quite readably (one poster claimed
that C COULD be written as poetry). I asked for a representative
sample of C poetry. None has been forthcoming (I'll settle for C++
poetry, if you prefer). One poster submitted a version of my original
example that he felt was cleaned up--I thought it was still quite
horrible; I also thought his belief that it was now okay was proof
positive of what I'd been claiming about the C culture all along.

Others challenged me to provide the same functionality in Ada, for
comparative purposes. Very well--what follows is a skeleton of a
Julian_Calendar package, written in Ada. I have not fleshed out the
package with all the other functionality that would logically belong
there (e.g. computation of what day of week it was on an arbitrary
date, etc). [I have also stuck to the same basic algorithm as in
the C example, to make the comparison straightforward--if I were
really implementing a Julian_Calendar package for commercial consumption,
I'd do so using a universal calendar algorithm, for speed.]
The package does compile, execute, and give the correct results.

In reviewing the example below, keep my claims in mind:

1) Programs are written for people, not for computers. PEOPLE,
   not computers, do maintenance and review code. It is the job of
   the COMPILER to translate human-readable code into machine-
   readable code: programmers who try to eliminate the middleman
   cost their company money (and write non-portable, non-reusable
   code).

2) The history of software engineering has been more profoundly warped
   by the simple fact that most programmers are only hunt-and-peck
   typists than by anything else. This explains constricted identifier
   names and an emphasis on notational compaction at the expense of
   understandability.

   Corollary: the term "verbose", applied to a programming language,
   is COMPLIMENTARY, not perjorative.

3) Some languages, by their very design and syntax, are superior to
   others in terms of support for readability. Writing as well as
   possible in C, one can still not write programs that are as readable
   and understandable as programs written in Ada by an equally competent
   programmer with the same objectives in mind.

4) C++ is scant improvement over C in terms of its support for
   readability.

5) A language does not exist independently from its culture. The
   culture that grew up around C is hackerish, egocentric, undisciplined,
   etc. The culture that has grown up around Ada is, like the language,
   software engineering oriented.

I would be quite interested to see if anybody can write a C or C++
program to compute the Julian date that is as understandable as the
Ada example shown below. I would also be interested in having the
example below shown to beginning programmers (and even non-programmers)
to see how readable it seems to them--and then the same experiment
conducted with a version in C/C++.

Finally, I'd like to see an example of this in Eiffel.

===============================================================

package Julian_Calendar is

-- Day stuff:

    subtype Day is Positive range 1 .. 31;

    type Names is (Monday, Tuesday, Wednesday,
                   Thursday, Friday, Saturday, Sunday);

    subtype Weekdays is Names range Monday .. Friday;
    subtype Weekends is Names range Saturday .. Sunday;

-- Month stuff:

    type Month is (January, February, March, April, May, June, July,
                   August, September, October, November, December);

    type Days_Per_Month is array (Month) of Day;

    Normal_Year : constant Days_Per_Month := (January   => 31,
                                              February  => 28,
                                              March     => 31,
                                              April     => 30,
                                              May       => 31,
                                              June      => 30,
                                              July      => 31,
                                              August    => 31,
                                              September => 30,
                                              October   => 31,
                                              November  => 30,
                                              December  => 31);

    Leap_Year : constant Days_Per_Month := (January   => 31,
                                            February  => 29,
                                            March     => 31,
                                            April     => 30,
                                            May       => 31,
                                            June      => 30,
                                            July      => 31,
                                            August    => 31,
                                            September => 30,
                                            October   => 31,
                                            November  => 30,
                                            December  => 31);

-- Year stuff:

    type Year is range 0 .. 2_000_000_000;

    function Is_Leap_Year (This_Year : in Year) return Boolean;

-- Julian day stuff:

    subtype Julian_Day is Positive range 1 .. 366;

    function Julian_Day_For
                (This_Year : in Year; This_Month : in Month; This_Day : in Day)
                return Julian_Day;

    No_Such_Day : exception;

end Julian_Calendar;

package body Julian_Calendar is

    function Is_Leap_Year (This_Year : in Year) return Boolean is

      -- A leap year is any year evenly divisible by 4 that is
      -- not also evenly divisible by 400.

    begin
        return (This_Year mod 4 = 0) and not (This_Year mod 400 = 0);
    end Is_Leap_Year;

    function Julian_Day_For (This_Year  : in Days_Per_Month;
                             This_Month : in Month;
                             This_Day   : in Day) return Julian_Day is

        The_Day : Julian_Day := This_Day;

    begin
        if This_Year (This_Month) < This_Day then
            raise No_Such_Day;
        elsif This_Month = January then
            return The_Day;
        else

            -- Add up number of days in each month up to but not including
            -- the current month (current month is already taken care of
            -- by initial value of The_Day):

            for Current_Month in January .. Month'Pred (This_Month) loop
                The_Day := The_Day + This_Year (Current_Month);
            end loop;
            return The_Day;
        end if;
    end Julian_Day_For;

    function Julian_Day_For
                (This_Year : in Year; This_Month : in Month; This_Day : in Day)
                return Julian_Day is
    begin
        if Is_Leap_Year (This_Year) then
            return Julian_Day_For (Leap_Year, This_Month, This_Day);
        else
            return Julian_Day_For (Normal_Year, This_Month, This_Day);
        end if;
    end Julian_Day_For;

end Julian_Calendar;
--
* The opinions expressed herein are my own, except in the realm of software *
* engineering, in which case I borrowed them from incredibly smart people.  *
*                                                                           *
* Rational: cutting-edge software engineering technology and services.      *

amanda@visix.com (Amanda Walker) (04/17/91)

In article <jls.671858932@rutabaga> jls@rutabaga.Rational.COM (Jim
Showalter) writes:

   2) The history of software engineering has been more profoundly warped
      by the simple fact that most programmers are only hunt-and-peck
      typists than by anything else. This explains constricted identifier
      names and an emphasis on notational compaction at the expense of
      understandability.

This I agree with completely.

      Corollary: the term "verbose", applied to a programming language,
      is COMPLIMENTARY, not perjorative.

Eh, only up to a point.  It is possible to be *too* verbose, after all.
I don't want my code looking like a Stephen Donaldson novel... :)

      Writing as well as possible in C, one can still not write
      programs that are as readable and understandable as programs
      written in Ada by an equally competent programmer with the same
      objectives in mind.

I have my doubts about this.  Comments, choices of identifier names, and
code structure far outweigh any features of the language itself.  And even
looking at the effect those features do have, I'd class C and Ada together,
with Scheme, Lisp, or Algol-60 above them.

   4) C++ is scant improvement over C in terms of its support for
      readability.

No argument here :).  I'd even say it's a step backward, although I do
admit to liking // comments.

   5) A language does not exist independently from its culture.

I disagree with this completely.

      The culture that has grown up around Ada is, like the language,
      software engineering oriented.

From what I have seen so far, the Ada culture, insofar as it exists, is
oriented around government contracting...

Your Ada example seems to be something of a red herring; you could write
an Ada function that's just as hacky and scrunched up as your C example.
Holding these two examples up as equivalent, and thus proving your
point, is a straw man argument...
--
Amanda Walker						      amanda@visix.com
Visix Software Inc.					...!uunet!visix!amanda
-- 
"I know you're supposed to take life one day at a time -- but lately
 several days have attacked me at once."	--Anonymous

jdudeck@polyslo.CalPoly.EDU (John R. Dudeck) (04/18/91)

In an article amanda@visix.com (Amanda Walker) wrote:
>In article <jls.671858932@rutabaga> jls@rutabaga.Rational.COM (Jim
>Showalter) writes:
>      Corollary: the term "verbose", applied to a programming language,
>      is COMPLIMENTARY, not perjorative.
>Eh, only up to a point.  It is possible to be *too* verbose, after all.
>I don't want my code looking like a Stephen Donaldson novel... :)

I came across the book by Knuth about WEB, where programs look like
novels or poetry or something.  You run them through a preprocessor
and get Pascal out.  Gross.  I couldn't even finish the book.

>
>   4) C++ is scant improvement over C in terms of its support for
>      readability.
>
>No argument here :).  I'd even say it's a step backward, although I do
>admit to liking // comments.

C++ is more difficult than C to understand because it adds a whole level
of complexity that isn't visible in the code.  For example polymorphic
data structures apply different member functions to an object depending
on the object's type.  But the mechanism for this is implicit in the
language.  This permits C++ programs to be more compact than C programs,
because the mechanism doesn't have to be part of your code.  The result
is that when reading a C++ program, you have to have a pre-existing
understanding of the mechanism of polymorphic function calls.  I would
say that this makes the C++ program less readable.

As I understand the philosophy of the oringal design of Ada, this type
of mechanism was EXPLICITLY avoided by the language designers, in order
to avoid difficulties of understanding it by less-skilled programmers
that have to maintain the code.

>      The culture that has grown up around Ada is, like the language,
>      software engineering oriented.
>
>From what I have seen so far, the Ada culture, insofar as it exists, is
>oriented around government contracting...

Well, I would say Ada is designed to prevent clever or subtle programming,
which is probably a good thing in larger projects.

>Your Ada example seems to be something of a red herring; you could write
>an Ada function that's just as hacky and scrunched up as your C example.
>Holding these two examples up as equivalent, and thus proving your
>point, is a straw man argument...

What hasn't been shown is the obfuscated Ada version and the verbose
C version :^).


-- 
John Dudeck                                              "You can only push
jdudeck@Polyslo.CalPoly.Edu                             simplicity so far."
ESL: 62013975 Tel: 805-545-9549                -- AT&T promotional brochure

jls@rutabaga.Rational.COM (Jim Showalter) (04/19/91)

%   4) C++ is scant improvement over C in terms of its support for
%      readability.
%
%No argument here :).  I'd even say it's a step backward, although I do
%admit to liking // comments.

One person I know says C++ is indistiguishable from line noise...

%   5) A language does not exist independently from its culture.
%
%I disagree with this completely.

Why? All the style guides, training courses/books, idioms, lore,
etc are all essentially cultural artifacts. 

%      The culture that has grown up around Ada is, like the language,
%      software engineering oriented.
%
%From what I have seen so far, the Ada culture, insofar as it exists, is
%oriented around government contracting...

There is nothing contradictory between government contracting and
software engineering. Indeed, it turns out to be darned close to
impossible to succesfully complete a project as big as the FAA
rewrite of all flight control software WITHOUT software engineering.

I doubt the average hacker could even begin to start to comprehend
the scale and complexity of the sort of projects the government
undertakes. Getting a 45KSLOC C application running isn't even CLOSE.

%Your Ada example seems to be something of a red herring; you could write
%an Ada function that's just as hacky and scrunched up as your C example.

I could, but that's not my point. My point is: here is some Ada written
to be readable. It implements essentially the same algorithm as the
C code I presented earlier. Given that the C code presented earlier was
grungy, I INVITE all those C folks out there to submit examples in
response that are as UN-grungy as they are capable of making them. It
is my claim that even these examples will not be as readable as the
Ada example I posted.
--
* The opinions expressed herein are my own, except in the realm of software *
* engineering, in which case I borrowed them from incredibly smart people.  *
*                                                                           *
* Rational: cutting-edge software engineering technology and services.      *

dmg@ssc-vax.uucp (David M Geary) (04/20/91)

]   Jim Showalter


] A short while ago, I posted an example of what I view as fairly
] typical C code and asked people what it did. Given that the code
 ^^^^^^^^^^^^^^(1)
] was, as usual, written with two-character "identifiers" and
      ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^(2)
] maximum utilization of C's cryptic syntax, it took people a while
 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^(3)
] to figure out that the intent of the code was to compute the Julian
] date. Even then, all but a handful of people missed the two bugs
                   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
] planted in the code.
  ^^^^^^^^^^^^^^^^^^^
                    (4)

1) The posted code was *not* typical C.

2)  What?  *as usual*, written with two-character "identifiers"?
   After nearly 10 years of C programming, and looking at millions
   of lines of others C code, I have *never* seen *anyone* that 
   used *as usual* 2 char identifiers for variable names.

   Maybe Jim is confusing Unix's 2 char. names for commands such as:

   ls
   cc
   rm
   df ...

   etc.

   But that is *Unix*, not *C*.

3)  Oh yea, that's what I always try to do when I write C code -
    write with maximum utilization of C's cryptic syntax.  Get real.

4)  So people could not figure out Jim's incredibly bad C code.  So what?
    I can write code in Ada that noone can understand, either.

] Others challenged me to provide the same functionality in Ada, for
] comparative purposes. Very well--what follows is a skeleton of a
] Julian_Calendar package, written in Ada. I have not fleshed out the
] package with all the other functionality that would logically belong
] there (e.g. computation of what day of week it was on an arbitrary
] date, etc). [I have also stuck to the same basic algorithm as in
] the C example, to make the comparison straightforward--if I were
] really implementing a Julian_Calendar package for commercial consumption,
] I'd do so using a universal calendar algorithm, for speed.] 
] The package does compile, execute, and give the correct results.

  This is the most ridiculous thing I've ever seen.  What's the point?  I
can write garbage code in Ada, that noone could ever figure out, and write
readable C code.  So can anyone.  So what?

] Writing as well as possible in C, one can still not write
] programs that are as readable and understandable as programs
] written in Ada by an equally competent programmer with the same
] objectives in mind.

  Oh, come on.  I take back the claim above about the most ridiculous
thing I've ever seen.  *This* is the most ridiculous thing I've ever seen...
This is total, absolute, unabashed crap.

Then Amanda Walker writes:

]] Your Ada example seems to be something of a red herring; you could write
]] an Ada function that's just as hacky and scrunched up as your C example.
]] Holding these two examples up as equivalent, and thus proving your
]] point, is a straw man argument...

Exactly.  Although I would change the "seems to be something of" to "is a"
in Amanda's reply ;-)

Well, I was not about to be goaded into writing the julian code in C,
but the last statement I quoted above from Jim has pushed me over the edge.
Here is the code Jim has asked for in C:


.................. BEGIN C CODE FOR JULIAN DATE COMPUTATION ..................

#include <stdio.h>

/*| --------------------------------------------------------------------------
  |
  | I normally keep the assertBool() macro, PRIVATE, and PUBLIC #defines, 
  | and BOOLEAN type definition in a header file which is included with 
  | all my .c files...
*/

/*   assertBool() - if ex is not true, then the message "Assertion Failed: "
 |		    is printed to stderr, citing the file and line number
 |		    in which the assertion failed...
*/

#define assertBool(ex)                                                      \
{                                                                           \
 if(!(ex))                                                                  \
 {                                                                          \
   fprintf(stderr, "Assertion Failed:  %s, %d\n", __FILE__, __LINE__);      \
   return 0;                                                                \
 }                                                                          \
}

/*
 |  Functions below which are declared PRIVATE are only visible in this file,
 | and thus not available to functions outside of this file...
 |
 |  Functions which are not PRIVATE, are explicitly declared as PUBLIC, so
 | that it is clear that the function was originally meant to be PUBLIC.
 | PUBLIC functions are available by functions not residing in this file...
 |
 |  TRUE and FALSE are 1 and 0, respectively...
*/

#define PRIVATE static
#define PUBLIC 

#define TRUE    1
#define FALSE   0


/*  BOOLEAN portrays more meaning than simply int ...                        */
 
typedef int  BOOLEAN;


/* -------------------------------------------------------------------------- */


int  daysPerMonthNormalYear[] = { /* January   */ 31, /* February  */ 28,
				  /* March     */ 31, /* April     */ 30,
				  /* May       */ 31, /* June      */ 30,
				  /* July      */ 31, /* August    */ 31,
				  /* September */ 30, /* October   */ 31,
				  /* November  */ 30, /* December  */ 31
                                };

int  daysPerMonthLeapYear[] =   { /* January   */ 31, /* February  */ 29,
				  /* March     */ 31, /* April     */ 30,
				  /* May       */ 31, /* June      */ 30,
				  /* July      */ 31, /* August    */ 31,
				  /* September */ 30, /* October   */ 31,
				  /* November  */ 30, /* December  */ 31
                                };

/*
 |  Here we define some macros to validate month, year, and day passed to
 | functions:
*/

#define validateMonth(mon)          assertBool(month > 0 && month <= 12)
#define validateYear(year)          assertBool(year  > 0)
#define validateDay(year,month,day) assertBool(checkForValidDay(year,month,day))


/*-------------------------- BOOLEAN isLeapYear() --------------------------*/
/*
 |~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 | AUTHOR:    David Geary 
 | DATE:      4/91 
 |
 | SCOPE:     PRIVATE 
 |
 | PURPOSE:   To determine if a given year is a leap year. 
 |             
 |~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 | INPUTS:    int year:  year to determine (leap year) status of. 
 |             
 |~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 | ASSERTIONS:    BOOLEAN:   year must be valid. 
 |
 |                  FATAL:   None. 
 |
 |~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 | RETURNS:   TRUE if year is valid and a leap year, FALSE otherwise.
 |
 |~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 | FUNCTION CALLS:  SYSTEM:  None. 
 |  
 |                   OTHER:  None. 
 |  
 |~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*/  

PRIVATE BOOLEAN  isLeapYear(year)
  int year;
{
 validateYear(year);
 {
   return  year % 4 == 0  &&  year % 400 != 0;
 }
}

/*------------------ int* getAppropriateDaysInMonthArray() ------------------*/
/*
 |~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 | AUTHOR:    David Geary 
 | DATE:      4/91
 |
 | SCOPE:     PRIVATE 
 |
 | PURPOSE:   To obtain a pointer to the appropriate array of days per month,
 |	      depending upon whether the year is a leap year or not.
 |             
 |~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 | INPUTS:    int  year:  year to get appropriate array for. 
 |             
 |~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 | ASSERTIONS:    BOOLEAN:   year must be valid. 
 |
 |                  FATAL:   None. 
 |
 |~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 | RETURNS:   Pointer to appropriate days per month if year is valid, NULL
 |            if year is not valid.
 |
 |~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 | FUNCTION CALLS:  SYSTEM:  None. 
 |  
 |                   OTHER:  None. 
 |  
 |~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*/  

PRIVATE int*  getAppropriateDaysInMonthArray(year)
  int year;
{
 validateYear(year);
 {
   if(isLeapYear(year))
     return  daysPerMonthLeapYear;
   else
     return  daysPerMonthNormalYear;
 }
}

/*------------------------ BOOLEAN checkForValidDay() ------------------------*/
/*
 |~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 | AUTHOR:    David Geary 
 | DATE:      4/91 
 |
 | SCOPE:     PRIVATE 
 |
 | PURPOSE:   To validate that the value for a given day is correct, also
 |            given the year and month.
 |             
 |~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 | INPUTS:    int   year:  year  (what more can I say?)
 |            int  month:  month (ditto)
 |	      int    day:  day   (ditto) 
 |             
 |~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 | ASSERTIONS:    BOOLEAN:   year must be valid.
 |			     month must be valid.
 |
 |			     Note that an assertion is triggered if year or
 |			     month is not valid, but no assertion is triggered
 |			     if day is invalid.
 |
 |                  FATAL:   None. 
 |
 |~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 | RETURNS:   TRUE if year, month and day are valid, FALSE otherwise.
 |
 |~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 | FUNCTION CALLS:  SYSTEM:  None. 
 |  
 |                   OTHER:  getAppropriateDaysInMonthArray() 
 |  
 |~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*/  

PRIVATE BOOLEAN  checkForValidDay(year, month, day)
  int  year;
  int  month;
  int  day;
{
 validateYear(year);
 validateMonth(month);
 {
   BOOLEAN  isDayValid       = TRUE;
   int*     daysInMonthArray = getAppropriateDaysInMonthArray(year);

   if(day < 1 || day > daysInMonthArray[month-1])  
     isDayValid = FALSE;

   return isDayValid;
 }
}

/*------------------------- int computeJulianDay() -------------------------*/
/*
 |~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 | AUTHOR:    David Geary 
 | DATE:      4/91 
 |
 | SCOPE:     PUBLIC 
 |
 | PURPOSE:   To compute the julian day, given a year, month, and day. 
 |             
 |~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 | INPUTS:    int   year:   year.
 |	      int   month:  month.
 |	      int   day:    day.
 |             
 |~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 | ASSERTIONS:    BOOLEAN:   year must be valid.
 |			     month must be valid.
 |			     day must be valid.
 |
 |                  FATAL:   None. 
 |
 |~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 | RETURNS:   Julian day, if year, month, and day are valid, 0 otherwise.
 |
 |~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 | FUNCTION CALLS:  SYSTEM:  None. 
 |  
 |                   OTHER:  getAppropriateDaysInMonthArray() 
 |  
 |~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*/  

PUBLIC int  computeJulianDay(year, month, day)
   int  year;
   int  month;
   int  day;
{
 validateYear(year);
 validateMonth(month);
 validateDay(year, month, day);
 {
   int  monthCount       = 0;
   int  julianDay        = day;
   int* daysInMonthArray = getAppropriateDaysInMonthArray(year);

   while(monthCount < month-1)
     julianDay = julianDay + daysInMonthArray[monthCount++];

   return  julianDay;
 }
}

/*
 |  Normally, one would write a test program that resides in a file other
 | than this one ...
*/

main()
{
  int  julDay = computeJulianDay(1991, 4, 19);

  if(julDay == 0)
   printf("OOPS, either year, month, or day was not valid!\n");
  else
   printf("Julian Day for April 19, 1991:  %d\n", julDay);
}

.................. END C CODE FOR JULIAN DATE COMPUTATION. ...................


  I would say that the C code above is just as readable as Jim's Ada code.
For me, it is actually much more readable, as I do not use Ada (but am
familiar with it).  For instance, in Jim's code we find:

] for Current_Month in January .. Month'Pred (This_Month) loop

  I do not understand *exactly* what this means, although I can glean that
we are looping through months.  The "Month'Pred (This_Month)" does not make
sense to me.  To me, this is much more readable:

while(monthCount < month-1)

  Also, Jim does not clearly separate functions:

package body Julian_Calendar is

]   function Is_Leap_Year (This_Year : in Year) return Boolean is
]
]     -- A leap year is any year evenly divisible by 4 that is
]     -- not also evenly divisible by 400.
]
]   begin
]       return (This_Year mod 4 = 0) and not (This_Year mod 400 = 0);
]   end Is_Leap_Year;
]
]   function Julian_Day_For (This_Year  : in Days_Per_Month;
]                            This_Month : in Month;
]                            This_Day   : in Day) return Julian_Day is
]
]       The_Day : Julian_Day := This_Day;
]
]   begin
]       if This_Year (This_Month) < This_Day then
]           raise No_Such_Day;
]       elsif This_Month = January then
]           return The_Day;
]       else
]
]           -- Add up number of days in each month up to but not including
]           -- the current month (current month is already taken care of
]           -- by initial value of The_Day):
]
]           for Current_Month in January .. Month'Pred (This_Month) loop
]               The_Day := The_Day + This_Year (Current_Month);
]           end loop;
]           return The_Day;
]       end if;
]   end Julian_Day_For;
]
]   function Julian_Day_For
]               (This_Year : in Year; This_Month : in Month; This_Day : in Day)
]               return Julian_Day is
]   begin
]       if Is_Leap_Year (This_Year) then
]           return Julian_Day_For (Leap_Year, This_Month, This_Day);
]       else
]           return Julian_Day_For (Normal_Year, This_Month, This_Day);
]       end if;
]   end Julian_Day_For;
]
] end Julian_Calendar;

  This looks messy to me.  There is no clear distinction (other than the 
  keywords function, begin and end) as to where one function ends, and another
  begins.  Jim, might I (a lowly C programmer) be so bold as to suggest
  an improvement for the readability of your Ada code?  How about if you
  put a nice comment block before each function: (If I were an Ada programmer
  I would probably embelish the following comment blocks)

-- ---------------------------------------------------------------------------
--
--  FUNCTION:  Is_Leap_Year()
--
--  PURPOSE:   To Determine if a given year is a leap year.
--
--  RETURNS:   true if year is leap year, false if not

-- ---------------------------------------------------------------------------

]   function Is_Leap_Year (This_Year : in Year) return Boolean is
]
]     -- A leap year is any year evenly divisible by 4 that is
]     -- not also evenly divisible by 400.
]
]   begin
]       return (This_Year mod 4 = 0) and not (This_Year mod 400 = 0);
]   end Is_Leap_Year;
]

-- ---------------------------------------------------------------------------

--
--  FUNCTION:  Julian_Day_For()
--
--  PURPOSE:   To compute the julian day for a given year, month and day.
--             Notice that this function will raise an exception, if the
--             This_Day value passed is invalid.
--
--  RETURNS:   Julian date for given year, month and day.

-- ---------------------------------------------------------------------------

]   function Julian_Day_For (This_Year  : in Days_Per_Month;
]                            This_Month : in Month;
]                            This_Day   : in Day) return Julian_Day is
]
]       The_Day : Julian_Day := This_Day;
]
]   begin
]       if This_Year (This_Month) < This_Day then
]           raise No_Such_Day;
]       elsif This_Month = January then
]           return The_Day;
]       else
]
]           -- Add up number of days in each month up to but not including
]           -- the current month (current month is already taken care of
]           -- by initial value of The_Day):
]
]           for Current_Month in January .. Month'Pred (This_Month) loop
]               The_Day := The_Day + This_Year (Current_Month);
]           end loop;
]           return The_Day;
]       end if;
]   end Julian_Day_For;
]
 etc.

  In addition, using a few more blank lines might make Jim's code a little
more (is it possible?) readable.  Also, a comment indicating what if an
end if; statement terminates might help (just in case Jim writes if elsif, 
endif blocks (or whatever you call them in Ada) that are longer.  Therefore
the following:

]           for Current_Month in January .. Month'Pred (This_Month) loop
]               The_Day := The_Day + This_Year (Current_Month);
]           end loop;
]           return The_Day;
]       end if;
]   end Julian_Day_For;

becomes:

           for Current_Month in January .. Month'Pred (This_Month) loop
               The_Day := The_Day + This_Year (Current_Month);
           end loop;

           return The_Day;

       end if; -- end of   if This_Year (This_Month)

   end Julian_Day_For;


  Lastly, note that I am not an anti-Ada bigot.  (Clearly, Jim is an anit-C
bigot).  Note the tone from one of Jim's earlier postings:

]  Someone other than Jim.  (Jim has an annoying habit of not crediting those
			     he quotes in articles).
]] Jim.

]  And I think C is really more suited for Gods (and daemons) to program in
] than humans.

]] Not gods--idiot savants.

Note:  the (and daemons) was cute ;-)

Actually, I think Ada has quite a few nice features that C lacks.  
Personally though Ada is much too wordy for me.  It's harder for me to see 
begin and end, for instance, than it is { and }.  However, that's me, and, no 
doubt is due to the many years that I have spent writing C and C++ code.  
On the *average* Joe Blow's Ada code is *probably* more readable than Jim 
Blow's C code.  However, to make a claim that no C program can *ever* be as 
readable as the same thing implemented in Ada is assinine. 

  Lastly (oops ;-), note that the code above is very representative of the
code I write everyday in C.  In no way did I do *anything* in the above code
that I would not normally do when writing C in everyday life.  As a matter of
fact, if I were to do a julian date computation program for real, I would
have created classes (packaged structures and functions) such as year, month, 
and day, each of which would have functions that would do such things as 
validate(), isLeapYear() (for year class only), etc.  I would then hide the 
data structures from everyone outside of the .c files that are associated with 
each class (yes, this can be done in C too), in addition to hiding certain 
functions which would not be part of the interface to each class.  This would 
give me nice reusable *packages* which would, no doubt, mature over time.

  

bs@alice.att.com (Bjarne Stroustrup) (04/21/91)

I think that it is interesting to note that the example used in this
debate about the readability of Ada/C/C++ is in the domain where the
solutions are essentially equivalent. Hence the diversion (?) of the
debate into namin and commenting conventions. I do not think that there
are any significant differences between the languages at this level
so that the debate necessarily gets diverted into matters of taste,
management, and sociology. Those discussions have their place, but
I think that there is a significant issue in the readability of code
where a language supports or fail to support a programming paradigm.

So, how about comparing the languages on the example originally used
to introduce the technique we now commonly refer to as object-oriented
programming in the Simula textbook SIMULA BEGIN? The problem is to
write a little program that define a set of shapes and manipulates
them (I'll rotate a list of shapes). Then we add another shape and
see what effort that takes and whether the original code manipulates
shapes still works. There are other `tests' for object orientation
(both harder and easier on the expressiveness of programming languages),
but this one has the property of demonstrating a realistic problem
of program organization in a small problem and has been around for
about 20 years.

Naturally, I'll give my version in C++ and in a programmer's rather
than a software engineer's style of C++. The comments are used
primarily to explain C++ features to a non-C++ programmer and only
secondarily to document the program. If someone feel the urge they
can complete the example (it is for example missing suitable constructors)
and elaborate it suitably with comments and organizational details:

// first we define the Shape type as an abstract class:

	class Shape {	// representation details common to all shapes:
		point center;
		color col;
		// ...
	public:		// user interface:
		point where() { return center; }
		void move(point to) { center = to; draw(); }

		virtual void draw() = 0;
		virtual void rotate(int angle) = 0;
		// ...
	};

// `virtual' means that dynamic binding will be used in a call of a virtual
// function, `= 0' means that any class derived from this class must provide
// an implementation of the function.

// To define a particular shape, we must say that it is a shape
// and specify its particular properties (including the virtual functions).

	class Circle : public Shape {
		int radius;
	public:
		void draw();
		void rotate(int) {}    // yes, the null function

		// ...
	};

	void Circle::draw()
	{
		// ...
	}

// we can now write a piece of ``user core'' manipulating Shapes:

	void rotate_all(Vector<Shape*>& v, int angle)
		// rotate all members of array `v' `angle' degrees
	{
		for (int i = 0, i<v.size(); i++) v[i].rotate(angle);
	}

// I used a Vector template rather than a plain C array as the argument type
// Vector<Shape*>& means `reference to Vector of pointers to Shapes.'
// If that is considered `cheating' I can either go back to the C style
// or provide the Vector definition; it is about 20 lines of C++

// adding new Shape doesn't interfere with older Shapes
// or with older code manipulating Shapes:


	class Rectangle : public Shape {
		Point nw, se;
	public:
		void draw();
		void rotate(int angle);
		// ...
	};

	void Rectangle::draw()
	{
		// ...
	}

	void Rectangle::rotate(int angle)
	{
		// ...
	}

Please note that I'm not claiming that all programs require the
mechanisms used here (many do and many don't) nor that C++ is the
only language that can express this example easily (it is not, I
deliberately chose one of the oldest and most easily expressed examples
of object-oriented programming techniques).

Reeasonable questions in this context is to which extent abbreviations
such as + for `plus' & for `reference to' and { for `start scope' affect
readability and more generally how the issue of terseness vs verboseness
affects readability for novices and experts.

Another interesting issue is once a program has been expressed in a language
what guarantees does the language provide? This relates to the issues of
direct support vs programming techniques and to the thorny issue of run-time
versus compile-time checking.

preece@urbana.mcd.mot.com (Scott E. Preece) (04/23/91)

In article <jls.671858932@rutabaga> jls@rutabaga.Rational.COM (Jim Showalter) writes:

|   2) The history of software engineering has been more profoundly warped
|      by the simple fact that most programmers are only hunt-and-peck
|      typists than by anything else. This explains constricted identifier
|      names and an emphasis on notational compaction at the expense of
|      understandability.
|
|      Corollary: the term "verbose", applied to a programming language,
|      is COMPLIMENTARY, not perjorative.
|
|   3) Some languages, by their very design and syntax, are superior to
|      others in terms of support for readability. Writing as well as
|      possible in C, one can still not write programs that are as readable
|      and understandable as programs written in Ada by an equally competent
|      programmer with the same objectives in mind.
---
I can't evaluate your statements without a concrete definition of
readability.  A lot of poetry (which you use as a complimentary
adjective) is not very readable at all -- it takes intelligence,
application, and thought to work out what it means.

Suppose you have two examples implementing the same functions in two
different languages and you ask "Which is more readable?"  If by
readability you mean ease of correct interpretation by someone who has
no particular knowledge of either language, you may get a very different
answer than if you mean by readability ease of correct interpretation by
someone who is fluent in both languages.

The statement that verbosity is complimentary is simply broken and
reflects, I think, a misunderstanding of the way we understand language.
There is a reason that style manuals preach concision -- our minds have
limited scope.  Dense language can increase the amount of material that
we can comprehend before suffering stack overflow (there is, needless to
say, bad dense language as well as good dense language, and bad dense
language can be *much* worse than bad loose language, though one could
argue that its denseness at least makes its badness more obvious).

It would be foolish to program for the reading level of the novice
programmer just as it would be foolish to write instruction manuals for
the first-grade reading level.  Readability judgements must take into
account the effective use of idiom as well as the similarity or
difference from standard English syntax.

Frankly, I didn't find the Ada particularly readable -- it was too large
to fit on one screen and I lost interest in the exercise.  The C code
(once it was formatted and its variables made more useful) was small
enough to be seen in its entirety, allowing the eye to follow
interpretive explorations easily and quickly.

I think the statement in paragraph (3) is exactly as true as the
statement that "the equally competent poet can, of course, write more
beautiful poems in French than would be possible in English."  One would
have no trouble finding people who would support the statement in
Montreal or Paris (though they might disagree on specifics!), but I
don't find it particularly convincing...

scott

--
scott preece
motorola/mcg urbana design center	1101 e. university, urbana, il   61801
uucp:	uunet!uiucuxc!udc!preece,	 arpa:	preece@urbana.mcd.mot.com
phone:	217-384-8589			  fax:	217-384-8550

jls@rutabaga.Rational.COM (Jim Showalter) (04/23/91)

>2)  What?  *as usual*, written with two-character "identifiers"?
>   After nearly 10 years of C programming, and looking at millions
>   of lines of others C code, I have *never* seen *anyone* that 
>   used *as usual* 2 char identifiers for variable names.

I take it then you've never read the C book written by one of the
language's inventors? You know, the one with all the one and two
character identifier names? And I take it that you've never seen
'i' used as the control variable on a "for" loop? I mean, really,
I've seen ZILLIONS of one or two character identifier names. That
the code you've read and the code I've read have been disjoint sets
seems to defy the laws of probability. Are you sure you don't want
to retract your above claim? "Never" is a rather strong statement.

>3)  Oh yea, that's what I always try to do when I write C code -
>    write with maximum utilization of C's cryptic syntax.  Get real.

Maybe you don't, but it's a popular enough passtime that they have
PUZZLE columns in the trade press.

>] Writing as well as possible in C, one can still not write
>] programs that are as readable and understandable as programs
>] written in Ada by an equally competent programmer with the same
>] objectives in mind.

>  Oh, come on.  I take back the claim above about the most ridiculous
>thing I've ever seen.  *This* is the most ridiculous thing I've ever seen...
>This is total, absolute, unabashed crap.

Look, put up or shut up. I've gotten all sorts of flak about how silly/unfair
it is to compare the readability of one language over another, but I haven't
yet gotten an example from anybody of what they consider a readable version,
in C, of my Ada example. It would seem to me that, if this is so easy to
do, SOMEONE on the worldwide net would have been able to do it by now. I mean,
all I'm asking people to do is use that wonderful readable C language to
write a dinky little algorithm as readably as I did in Ada. Perhaps you'd
like to go first?

>Well, I was not about to be goaded into writing the julian code in C,
>but the last statement I quoted above from Jim has pushed me over the edge.
>Here is the code Jim has asked for in C:

[several screenfulls of heavily commented C code deleted to save bandwidth
and modem agony]

So, this is it? Allow me to make a few comments on your example:

1) It takes up considerably more room than my Ada example. Very odd, 
   seeing as how a common criticism people make of Ada is that it is
   verbose.

2) Much of the space taken up by your example is comments. You'll note
   that in my example there aren't very many comments. This is deliberate:
   I think Ada is readable enough to be largely self-documenting. You
   apparently believe no such thing about C, reinforcing the very point
   you are struggling to disprove.

3) Much of the rest of the space taken up by your example is occupied
   by definitions of stuff like Boolean, which is, of course, predefined
   in Ada. Odd that you would find it necessary to invent Boolean yourself,
   since one of the criticisms people make of Ada is that it has too many
   "unecessary" features. Apparently Boolean is not one of them.

4) While it is laudable that you invented an assertion mechanism, this
   is not necessary in my example because I can use Ada's exception
   mechanism. Apparently exceptions are ALSO not an "unecessary" feature.

5) You have this stuff about PUBLIC and PRIVATE functions. While I find
   this confusing enough that I'm not sure I understand it, I BELIEVE what
   you are doing is inventing some sort of crude form of visibility control.
   As with Boolean and exceptions, this is predefined in Ada. Yet another
   "unecessary" feature?

6) You check if the day < 1. I don't have to do this, since the language
   understands constraint violations inherently. AGAIN, I suppose, an
   "unecessary" feature?

Now, for your comments:

>] for Current_Month in January .. Month'Pred (This_Month) loop

>  I do not understand *exactly* what this means, although I can glean that
>we are looping through months.  The "Month'Pred (This_Month)" does not make
>sense to me.  To me, this is much more readable:

>while(monthCount < month-1)

Uh, except that if your Month is January, it should BLOW UP, since there
is no legal value for January - 1. You'll probably get away with this in C,
but it's nothing to brag about. I believe
the trade name for this is a "bug". You might want to write a test driver
that does more than test a single middle-of-the-road-condition as is the
case with your current test driver: I suggest checking boundary conditions
such as the first day of the first month, etc. I did this with mine and
it works just fine. With yours if C had anything approaching real constraint
checking you'd fall over dead.

>  Also, Jim does not clearly separate functions:

[package body code deleted]

>  This looks messy to me.  There is no clear distinction (other than the 
>  keywords function, begin and end) as to where one function ends, and another
>  begins.

That's because that's all that is needed. The keywords are completely
deterministic: when you see the word "function", you know you're starting
the next function. Simple, non-wordy, accurate.

> Jim, might I (a lowly C programmer) be so bold as to suggest
>  an improvement for the readability of your Ada code?  How about if you
>  put a nice comment block before each function:

How about if I don't? The use of comment blocks serves only to redundantly
paraphrase what is already there, in the typical case. Consider your example:

#>-- ---------------------------------------------------------------------------
#>--
#>--  FUNCTION:  Is_Leap_Year()
#>--
#>--  PURPOSE:   To Determine if a given year is a leap year.
#>--
#>--  RETURNS:   true if year is leap year, false if not

#>-- ---------------------------------------------------------------------------

#>]   function Is_Leap_Year (This_Year : in Year) return Boolean is
#>]
#>]     -- A leap year is any year evenly divisible by 4 that is
#>]     -- not also evenly divisible by 400.
#>]
#>]   begin
#>]       return (This_Year mod 4 = 0) and not (This_Year mod 400 = 0);
#>]   end Is_Leap_Year;
#>]

You have added no value and several lines with your proposed comment block.
That the function returns True or False is obvious from the return value (that's
what it's THERE for). That the function determines whether or not the given
year is a leap year is obvious from the NAME of the damned thing (that's why
I chose the name I did). And that the function is a function and is called
Is_Leap_Year is obvious too. Indeed, there is nothing in your comment block
that could not be programmatically derived from the existing Ada construct,
which makes it COMPLETELY trivial (at least if there were some additional
comments about the definition of a leap year, etc, there might be SOME value
added--but the only non-derivable comment is the one I had already put there).
The only time you should add comments is when the information they contain is
not already obvious. This is in keeping with the principal of Minimal Noise.

Of course, this is less an Ada/C issue than a commenting issue.

>(If I were an Ada programmer
>  I would probably embelish the following comment blocks)

No, you wouldn't--because most Ada style guides discourage such things.

> Also, a comment indicating what if an
>end if; statement terminates might help (just in case Jim writes if elsif, 
>endif blocks (or whatever you call them in Ada) that are longer.

Again, this is superfluous. Ada, by design, has a "comb-like" structure:
you can tell immediately from lexical level which construct you're dealing
with and, because if/then/elseif/else/end-if constructs do NOT suffer
from the dangling else problem of C, the matching up of an end with a begin
is strictly deterministic.

Besides which, if you add these comments to ends of ifs and then change the
conditional on the if, the comment needs to be changed in concert. This is
in violation of the principle of Single Point of Maintenance (by the way,
your comment blocks are a similar violation of this principle). Or have you
never noticed that comments and their associated code tend to wind up bearing
no resemblance to each other?

>  Lastly, note that I am not an anti-Ada bigot.  (Clearly, Jim is an anit-C
>bigot).  Note the tone from one of Jim's earlier postings:

A bigot is one who is prejudiced. Prejudice is the formation of opinions
prior to or in absence of the facts. I worked in C for years and years:
my opinions towards it were formed AFTER I had the facts in hand. Thus I
am not prejudiced, merely experienced. And thus I am not a bigot.

>Actually, I think Ada has quite a few nice features that C lacks.  
>Personally though Ada is much too wordy for me.

Which is of course why your example is three times longer than mine.

>On the *average* Joe Blow's Ada code is *probably* more readable than Jim 
>Blow's C code.

No argument.
--
* The opinions expressed herein are my own, except in the realm of software *
* engineering, in which case I borrowed them from incredibly smart people.  *
*                                                                           *
* Rational: cutting-edge software engineering technology and services.      *

jls@rutabaga.Rational.COM (Jim Showalter) (04/23/91)

%I think that it is interesting to note that the example used in this
%debate about the readability of Ada/C/C++ is in the domain where the
%solutions are essentially equivalent.

Do you honestly regard the C solutions as being as readable as the
Ada solution?

>I think that there is a significant issue in the readability of code
>where a language supports or fail to support a programming paradigm.

You mean like multi-thread execution not being supported by C++? ;-)

%So, how about comparing the languages on the example originally used
%to introduce the technique we now commonly refer to as object-oriented
%programming in the Simula textbook SIMULA BEGIN? The problem is to
%write a little program that define a set of shapes and manipulates
%them (I'll rotate a list of shapes). Then we add another shape and
%see what effort that takes and whether the original code manipulates
%shapes still works. There are other `tests' for object orientation
%(both harder and easier on the expressiveness of programming languages),
%but this one has the property of demonstrating a realistic problem
%of program organization in a small problem and has been around for
%about 20 years.

I've been wanting to ask someone who purports to know the answer
this question for over a year.

Consider the shapes example. Suppose that the given example goes
out in binary form, and arrives at my site, and I want to add a new
shape. Suppose that the initial shapes were limited to triangles and
squares. Suppose that I now add a Circle, which has a new method
defined for it that does NOT apply to triangles or squares and which
was never previously defined in the base class for shapes--radius.
This method returns the radius as some floating point number from 0
to whatever.

Now, I want to take a heterogeneous list of shapes, including triangles,
circles, and squares, and I want to iterate over the list and print
out all of the radii.

How do I do this? I can't get elements out of the list and call
the Radius method on all of them, because not all of them HAVE such
a method defined. I can't add the new method to the base class (with
a null implementation as the default for those shapes for which it
is a meaningless operation) because the base class is in binary. I 
can't ask the shapes to tell me their Kind because there is no such
operation defined on them in C++.

It is my claim that solving this problem in C++ results in a solution
that is every bit as messy as simply using a discriminated record
and an enumeration type in Ada--and Ada doesn't HAVE inheritance.
--
* The opinions expressed herein are my own, except in the realm of software *
* engineering, in which case I borrowed them from incredibly smart people.  *
*                                                                           *
* Rational: cutting-edge software engineering technology and services.      *

dmg@ssc-vax (David M Geary) (04/23/91)

> David Geary
] Jim Showalter

  Jim Showalter, after posting some extremely ugly C code for Julian date
calculation, and a subsequent posting of easy to read Ada code for the
same purpose makes the claim:

] Writing as well as possible in C, one can still not write
] programs that are as readable and understandable as programs
] written in Ada by an equally competent programmer with the same
] objectives in mind.

  This, of course, is ridiculous.  I responded to this statement by posting 
my own version calculating Julian dates which I feel was just as readable 
as Jim's Ada code.

  Jim has taken it upon himself to turn this into an ugly language war where
he is carrying the cross for Ada.  Jim responds emotionally with:

] I take it then you've never read the C book written by one of the
] language's inventors? You know, the one with all the one and two
] character identifier names? And I take it that you've never seen
] 'i' used as the control variable on a "for" loop? I mean, really,
] I've seen ZILLIONS of one or two character identifier names. That
] the code you've read and the code I've read have been disjoint sets
] seems to defy the laws of probability. Are you sure you don't want
] to retract your above claim? "Never" is a rather strong statement.  ...

] 1) It takes up considerably more room than my Ada example. Very odd, 
]    seeing as how a common criticism people make of Ada is that it is
]    verbose. ...

  I used an assert() macro very much like C's assert() macro:

] 4) While it is laudable that you invented an assertion mechanism, this
]    is not necessary in my example because I can use Ada's exception
]    mechanism. Apparently exceptions are ALSO not an "unecessary" feature. ...

  I define PRIVATE to be static, and then declare functions PRIVATE:

  #define PRIVATE static

  PRIVATE  BOOLEAN  isLeapYear(year) ...

] 5) You have this stuff about PUBLIC and PRIVATE functions. While I find
]    this confusing enough that I'm not sure I understand it, I BELIEVE what
]    you are doing is inventing some sort of crude form of visibility control.

  Look, Jim, I am not arguing against Ada.  I like Ada.  I would love to
be able to have many of Ada's features in C.  That's great that Ada has
builtin boolean types, does range checking on variables, has an exception
mechanism, etc.  I wish C had them.  Ok?  Calm down, take a deep breath...
No one is attacking Ada.  Repeat after me:  "Everyone is *not* trying to 
attack Ada.  Those people behind me are not C programmers trying to kill me".
;-)

  All I'm doing is responding to your claim:

] Writing as well as possible in C, one can still not write
] programs that are as readable and understandable as programs
] written in Ada by an equally competent programmer with the same
] objectives in mind.

  I maintain that my code was as easy to understand as yours.  Period.
I do not claim that C is superior to Ada.  I do not claim that C has more
features than Ada.  I am not following you home at night.  Ok?

  Lastly, Jim writes:

] ...I worked in C for years and years:
     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
] my opinions towards it were formed AFTER I had the facts in hand. Thus I
] am not prejudiced, merely experienced. And thus I am not a bigot.
  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

  After statements like:

] 4) While it is laudable that you invented an assertion mechanism, this
]    is not necessary in my example because I can use Ada's exception
]    mechanism. Apparently exceptions are ALSO not an "unecessary" feature. ...

] 5) You have this stuff about PUBLIC and PRIVATE functions. While I find
]    this confusing enough that I'm not sure I understand it, I BELIEVE what
]    you are doing is inventing some sort of crude form of visibility control.

  Students that have taken my introductory C course understand that

#define PRIVATE static

PRIVATE int  someFun() ...

  Restricts the scope of someFun() to the file it is in.  Students in
my introductory C course also know of C's assert() macro, and how to use it.
  Jim is obviously either out and out lying about his C experience, or he never 
learned the language well.  Ignorance is often the progenitor of bigotry. 

philip@pescadero.Stanford.EDU (Philip Machanick) (04/24/91)

In article <jls.672370717@rutabaga>, jls@rutabaga.Rational.COM (Jim Showalter) writes:
I'm not sure what this whole thread has to do with comp.object,
so maybe I'll only deal with the point that seems to fit the topic:
|> I've been wanting to ask someone who purports to know the answer
|> this question for over a year.
|> 
|> Consider the shapes example. Suppose that the given example goes
|> out in binary form, and arrives at my site, and I want to add a new
|> shape. Suppose that the initial shapes were limited to triangles and
|> squares. Suppose that I now add a Circle, which has a new method
|> defined for it that does NOT apply to triangles or squares and which
|> was never previously defined in the base class for shapes--radius.
|> This method returns the radius as some floating point number from 0
|> to whatever.
|> 
|> Now, I want to take a heterogeneous list of shapes, including triangles,
|> circles, and squares, and I want to iterate over the list and print
|> out all of the radii.
|> 
|> How do I do this? I can't get elements out of the list and call
|> the Radius method on all of them, because not all of them HAVE such
|> a method defined. I can't add the new method to the base class (with
|> a null implementation as the default for those shapes for which it
|> is a meaningless operation) because the base class is in binary. I 
|> can't ask the shapes to tell me their Kind because there is no such
|> operation defined on them in C++.
|> 
|> It is my claim that solving this problem in C++ results in a solution
|> that is every bit as messy as simply using a discriminated record
|> and an enumeration type in Ada--and Ada doesn't HAVE inheritance.
All this example says is that object-oriented programming doesn't solve
the design problem. Everyone knows that designing general class libraries
is non-trivial. This is not news. Everyone knows that Ada doesn't have
support inheritance (except in a very weak sense, through derived types).
You've found an example where there is no obvious way of using inheritance.
Does this invalidate the concept? In the early days of structured
programming, the literature teamed with examples of strange loops you
could only code with gotos. Did this stop the structured programming
movement?

For the record: I used Ada before I used C. I need an object-oriented
language, and Ada isn't one. Maybe C++ is a messy compromise, which
attempts to patch over a lot of outdated design decisions, but its
object-oriented features are a lot better than Ada's.
-- 
Philip Machanick
philip@pescadero.stanford.edu

barmar@think.com (Barry Margolin) (04/24/91)

In article <PREECE.91Apr22173631@etude.urbana.mcd.mot.com> preece@urbana.mcd.mot.com (Scott E. Preece) writes:
>The statement that verbosity is complimentary is simply broken and
>reflects, I think, a misunderstanding of the way we understand language.
>There is a reason that style manuals preach concision -- our minds have
>limited scope.  Dense language can increase the amount of material that
>we can comprehend before suffering stack overflow (there is, needless to
>say, bad dense language as well as good dense language, and bad dense
>language can be *much* worse than bad loose language, though one could
>argue that its denseness at least makes its badness more obvious).

That is a very good point.  Consider:

1. Ths sttmnt s 2 trse

2. This statement is about as verbose as is necessary.

3. The statement that is currently being read contains excess verbiage that
contributes little to the comprehension level of the reader, and perhaps
reduces the overall readability quotient of the sentence.

I find statement 1 to be analogous to APL, C is somewhere between 1 and 2,
Common Lisp about 2-2.2 (dependeing upon use of some optional features),
and Ada close to 3.  While I don't wish to recommend BASIC for any serious
programming, it probably comes pretty close to 2.

>Frankly, I didn't find the Ada particularly readable -- it was too large
>to fit on one screen and I lost interest in the exercise.

That's exactly what happened to me.  The fact that an exercise in writing a
single function requires implementing an entire package seemed excessive.
Sure, if one were writing an entire date-handling library one would write
that package, and it would probably include a calendar->Julian conversion
function that looks something like the one given.  But taken out of context
it was way too much.

No one has yet mentioned the significant design difference between the Ada
and C versions of that routine, by the way.  The Ada version had two arrays
of month lengths, one for regular years and another for leap years; the C
version simply adds a day if the year is a leap year and the month is >2.
I find the C version's design preferable, but I'm having a hard time coming
up with good reasons.  I think I just find the two almost identical arrays
unnecessary.  If there were some possibility that one would need to change
one of them without changing the other, such a design would be justified,
but this seems unlikely.  Perhaps if it initialized one array by copying
the other and then changing February's entry I would be more comfortable.
One thing, though: the C version would be better if there were a

	#define LEAP_MONTH 2

rather than using the magic number 2 in several places.
--
Barry Margolin, Thinking Machines Corp.

barmar@think.com
{uunet,harvard}!think!barmar

juul@diku.dk (Anders Juul Munch) (04/24/91)

jls@rutabaga.Rational.COM (Jim Showalter) writes:

[stuff deleted]

>I've been wanting to ask someone who purports to know the answer
>this question for over a year.

>Consider the shapes example. Suppose that the given example goes
>out in binary form, and arrives at my site, and I want to add a new
>shape. Suppose that the initial shapes were limited to triangles and
>squares. Suppose that I now add a Circle, which has a new method
>defined for it that does NOT apply to triangles or squares and which
>was never previously defined in the base class for shapes--radius.
>This method returns the radius as some floating point number from 0
>to whatever.

>Now, I want to take a heterogeneous list of shapes, including triangles,
>circles, and squares, and I want to iterate over the list and print
>out all of the radii.

Who wrote the heterogeneous-list code? The guy who supplied the shape
classes? Of course not, because then he would have realized the need
for a shape method to identify exactly what kind of shape it is.

>How do I do this? I can't get elements out of the list and call
>the Radius method on all of them, because not all of them HAVE such
>a method defined. I can't add the new method to the base class (with
>a null implementation as the default for those shapes for which it
>is a meaningless operation) because the base class is in binary. I 
>can't ask the shapes to tell me their Kind because there is no such
>operation defined on them in C++.

That's why any C++ library programmer with just a little bit of sense
adds such an operation. If the guy who wrote your binary collection
of shape classes didn't do that, that's not a C++ problem: No language,
not even Ada can protect you against stupid programmers.
  C++ has a built-in type-discriminating mechanism called "dynamic
binding". It's not universal, but it's there for free, without
the programmer having to write special code or declare special tags
for this purpose. And you still have the option of declaring and 
using an explicit tag, whenever you need one. But most of the time,
a sufficent mechanism is already there, not cluttering your code
with enumerator and union field names.

>It is my claim that solving this problem in C++ results in a solution
>that is every bit as messy as simply using a discriminated record
>and an enumeration type in Ada--and Ada doesn't HAVE inheritance.

I suppose the kind of language construct you are referring to is the
equivalent of Pascal variant records (RECORD-CASE) or C enums and unions.
Good luck with your table-driven software!!

By the way, how about adding the tag method like this:

  typedef enum { CIRCLE_TAG, TRIANGLE_TAG, SQUARE_TAG } TAG;
  class TAG_BASE
  {
    virtual TAG Tag() = 0;
  };

  #define TAGIFY(SHAPE)                                                  \
     class SHAPE##_TAG : TAG_BASE { TAG Tag() { return SHAPE##_TAG } };  \
     class MY_##SHAPE : SHAPE, SHAPE##TAG {}

  TAGIFY(CIRCLE); TAGIFY(SQUARE); TAGIFY(TRIANGLE);

This leaves you with three new classes, MY_CIRCLE, MY_SQUARE
and MY_TRIANGLE, with all the functionality of the old classes,
plus, if x is an instance of one of these classes, 
you may inquire whether "x.Tag()==CIRCLE_TAG", and your problem
is solved. 

Still, this approach has it's problems, and I'm not claiming
C++ to be superior to Ada. I am claiming, however, that
dynamic binding is a valuable feature which Ada is missing.  

>--
>* The opinions expressed herein are my own, except in the realm of software *
>* engineering, in which case I borrowed them from incredibly smart people.  *

I suggest you find some that are even smarter :-)

>*                                                                           *
>* Rational: cutting-edge software engineering technology and services.      *


-- Anders Munch

jls@rutabaga.Rational.COM (Jim Showalter) (04/24/91)

>It would be foolish to program for the reading level of the novice
>programmer

Not really. The vast majority of work on software that goes on (like,
about 70%) is MAINTENANCE. This is often performed by underpaid,
underskilled, and overwhelmed junior programmers, many of whom are
unfamiliar with the original design, weren't around when "clever"
decisions were initially made, etc. To NOT program from the outset
with an eye toward making the job easier for the downstream maintainer
is criminal.

>Frankly, I didn't find the Ada particularly readable -- it was too large
>to fit on one screen and I lost interest in the exercise.  The C code
>(once it was formatted and its variables made more useful) was small
>enough to be seen in its entirety, allowing the eye to follow
>interpretive explorations easily and quickly.

Uh huh. And did you catch the bugs? Note also that, in attempting to
respond to my challenge, the submitted C code was in one case three
times as lengthy as my Ada example, and the other was at least two
times as lengthy. Apparently even your colleagues found the original
example unreadable.
--
* The opinions expressed herein are my own, except in the realm of software *
* engineering, in which case I borrowed them from incredibly smart people.  *
*                                                                           *
* Rational: cutting-edge software engineering technology and services.      *

davis@barbes.ilog.fr (Harley Davis) (04/24/91)

In article <jls.672370717@rutabaga> jls@rutabaga.Rational.COM (Jim Showalter) writes:

   %So, how about comparing the languages on the example originally used
   %to introduce the technique we now commonly refer to as object-oriented
   %programming in the Simula textbook SIMULA BEGIN? The problem is to
   %write a little program that define a set of shapes and manipulates
   %them (I'll rotate a list of shapes). Then we add another shape and
   %see what effort that takes and whether the original code manipulates
   %shapes still works. There are other `tests' for object orientation
   %(both harder and easier on the expressiveness of programming languages),
   %but this one has the property of demonstrating a realistic problem
   %of program organization in a small problem and has been around for
   %about 20 years.

   I've been wanting to ask someone who purports to know the answer
   this question for over a year.

   Consider the shapes example. Suppose that the given example goes
   out in binary form, and arrives at my site, and I want to add a new
   shape. Suppose that the initial shapes were limited to triangles and
   squares. Suppose that I now add a Circle, which has a new method
   defined for it that does NOT apply to triangles or squares and which
   was never previously defined in the base class for shapes--radius.
   This method returns the radius as some floating point number from 0
   to whatever.

   Now, I want to take a heterogeneous list of shapes, including triangles,
   circles, and squares, and I want to iterate over the list and print
   out all of the radii.

   How do I do this? I can't get elements out of the list and call
   the Radius method on all of them, because not all of them HAVE such
   a method defined. I can't add the new method to the base class (with
   a null implementation as the default for those shapes for which it
   is a meaningless operation) because the base class is in binary. I 
   can't ask the shapes to tell me their Kind because there is no such
   operation defined on them in C++.

   It is my claim that solving this problem in C++ results in a solution
   that is every bit as messy as simply using a discriminated record
   and an enumeration type in Ada--and Ada doesn't HAVE inheritance.

I don't know about C++, but in several Lisp-based OO languages this is
cake.  Here's a solution in EuLisp:

(defmodule shape

 (defclass shape () () predicate shapep)

 (defclass triangle (shape)
   (...some triangle slots...)
   constructor make-triangle
   predicate trianglep)

 (defclass rectangle (shape)
   (...some rectangle slots...)
   ...)

 (defgeneric rotate (object theta))

 (defmethod rotate ((object triangle) (theta real))
   ...triangle rotation code...)

 ... other methods for rotate ...

 (export shape triangle rectangle rotate ...)

)

This module is delivered in binary form to the client.  The fact that
ROTATE is generic is documented.  The client then does:

(defmodule circle-module (expose shape)

 ;;; New subclass of SHAPE
  
 (defclass circle (shape)
   ((radius accessor circle-radius
            initarg radius))
   constructor (make-circle (radius))
   predicate circlep)

 ;;; Adding a new method for ROTATE.

 (defmethod rotate ((object circle) (theta real))
   ;; do nothing for circles
   ())

 ;;; Printing out the radii of circles in a list.
    
 (defun print-radii (shape-list)
   ;; Prints the radii of all the circles in SHAPE-LIST.
   (mapc shape-list
         (generic-lambda (shape)
           method (((shape circle))
                   (print (circle-radius shape))))))

)

There you go.  Nearly identical solutions will work in CommonLisp and
Le-Lisp, and probably every other Lisp with even the simplest object
oriented framework.

-- Harley Davis


--
------------------------------------------------------------------------------
nom: Harley Davis			ILOG S.A.
net: davis@ilog.fr			2 Avenue Gallie'ni, BP 85
tel: (33 1) 46 63 66 66			94253 Gentilly Cedex, France

jls@rutabaga.Rational.COM (Jim Showalter) (04/25/91)

>I maintain that my code was as easy to understand as yours.  Period.

And I maintain that it is not. Period. But in any event, the real question
is--can OTHER people who have only a small amount of programming experience
in either language read your example or my example more successfully?

]] 5) You have this stuff about PUBLIC and PRIVATE functions. While I find
]]    this confusing enough that I'm not sure I understand it, I BELIEVE what
]]    you are doing is inventing some sort of crude form of visibility control.

]  Students that have taken my introductory C course understand that

]#define PRIVATE static

]PRIVATE int  someFun() ...

]Restricts the scope of someFun() to the file it is in.

Duh. I know how macros work. What I'm saying is, I'm not sure I understand
the use, purpose, intent, etc of this mechanism. Is it crude visibility
control? Okay, fine--then I was right. Is it something else? If so, please
elaborate.

P.S. You state repeatedly that you're not against Ada and that you wish
     C had the features Ada has. May I ask why, then, you persist in
     programming in C? (This is a serious question--I'd like to understand
     why people would continue to inflict C on themselves when so many
     better alternatives exist.)

     Also, check out my neat new .sig:
--
* "Beyond 100,000 lines of code, you should probably be coding in Ada." *
*      - P.G. Plauger, Convener and Secretary of the ANSI C Committee   *
*                                                                       *
*                The opinions expressed herein are my own.              *

jls@rutabaga.Rational.COM (Jim Showalter) (04/25/91)

>All this example says is that object-oriented programming doesn't solve
>the design problem. Everyone knows that designing general class libraries
>is non-trivial. This is not news.

The example I've given is of something that comes up often enough I
don't regard it as a design problem--I regard it as a LANGUAGE design
problem.

The example I've given is of inheritance-with-addition: in other words,
supersetting. I inherit, but I add new capabilities. Seems to me this
is fairly obviously the way things often work in the real world. If
the expressed intent of adding inheritance to a language is to make
it more effectively model the real world, and if the inheritance model
is so weak in its capabilities that it can only model strict inheritance
WITHOUT supersetting, then I argue that not that much has really been
accomplished. I have a similar argument for subsetting.

Consider a few simple cases:

	Supersetting (the example I gave previously): not all shapes
	have a meaningful answer for Radius, but Circles do, and circles
    	are indeed shapes

	Subsetting: most birds fly, but Ostriches do not, yet Ostriches
	are birds.

While the subsetting case is fairly simple in C++ (although slightly
knobby), the supersetting case is a bitch--so much so, in fact, that
I argue it is just as clean to do it in Ada: and Ada doesn't HAVE
inheritance.

>I need an object-oriented
>language, and Ada isn't one.

1) WHY do you need an object-oriented language? I'm serious: what are
   you working on? Is it big? Is it complex? Perhaps what you ACTUALLY
   need is a software-engineering language (Ada qualifies). Just curious.

2) Ada IS object oriented--it supports objects and classes quite well.
   It just isn't INHERITANCE oriented. The nomenclature for what is and
   is not an object has been debased for marketing reasons: shit man,
   a Boolean variable is an object.

   Precision in language is a desirable thing.

>Maybe C++ is a messy compromise, which
>attempts to patch over a lot of outdated design decisions, but its
>object-oriented features are a lot better than Ada's.

Well, let's look at this. C++ adds the following to C:

	strong typing
	opaque (private) types
	separation of specification from implementation

The latest-greatest revision of C++ (C++++?) has added:

	exceptions
	templates (generics)

All five of the things I listed above are major improvements
from the standpoint of software engineering.
They also, by the way, have nothing to do with inheritance.

Interestingly enough, those five things are also available in Ada,
and have been for years. Great minds think alike.

In addition, in Ada, you get concurrency (tasking) which is only
a distant gleam in the POSIX.4.a effort, and you get compiler
standardization and validation. You also get tools that scale
to the largest software projects in the world.

So it boils down to inheritance. What I ask is--what are you working
on that makes inheritance the dominant factor? Do you really need
inheritance, or do you need industrial-strength software engineering
capability? I'm quite serious in this: like a lot of other snake
oil, I think inheritance has been grossly oversold. I mean, it's not
as if inheritance is the thing that is going to make or break the
FAA rewrite of all U.S. flight control software, or the software for
the space station, or the 2 mega-sloc rewrite of the Army's financial
systems.
--
* "Beyond 100,000 lines of code, you should probably be coding in Ada." *
*      - P.G. Plauger, Convener and Secretary of the ANSI C Committee   *
*                                                                       *
*                The opinions expressed herein are my own.              *

jls@rutabaga.Rational.COM (Jim Showalter) (04/25/91)

>I find statement 1 to be analogous to APL, C is somewhere between 1 and 2,
>Common Lisp about 2-2.2 (dependeing upon use of some optional features),
>and Ada close to 3.

Which explains why my Ada example was 1/3 as lengthy as one C example
submitted, and 1/2 as lengthy as the other.

>The fact that an exercise in writing a
>single function requires implementing an entire package seemed excessive.

Sigh. You know all that talk about reuse? Slap my wrist, but I just can't
help thinking about reuse when I design or code. Tch tch tch. Look--I COULD
have written the damned thing in one long function, a la the C example. 
The only problem is, that would be completely the wrong way to go about
doing it if I wanted to write a real routine for use on a real project
that emphasizes real reuse. Of course, I wouldn't expect this to be
particularly understandable to the hacker mentality, in which the goal,
apparently, is to reimplement quicksort as many times as possible before
one dies.
--
* "Beyond 100,000 lines of code, you should probably be coding in Ada." *
*      - P.G. Plauger, Convener and Secretary of the ANSI C Committee   *
*                                                                       *
*                The opinions expressed herein are my own.              *

jls@rutabaga.Rational.COM (Jim Showalter) (04/25/91)

]>Now, I want to take a heterogeneous list of shapes, including triangles,
]>circles, and squares, and I want to iterate over the list and print
]>out all of the radii.

]Who wrote the heterogeneous-list code? The guy who supplied the shape
]classes? Of course not, because then he would have realized the need
]for a shape method to identify exactly what kind of shape it is.

Uh huh. Precisely the point made in "Emperor Strikes Lethe"--you DON'T
have any control over the guy who writes the class hierarchy upon which
you depend. Indeed, in my example I specifically said that the class
hierarchy is delivered in binary for security/monetary reasons, so you're
at the mercy of it completely. And you just want to add a simple little
class with some additional functionality--seems reasonable to me--but
you CAN'T.

]That's why any C++ library programmer with just a little bit of sense
]adds such an operation. If the guy who wrote your binary collection
]of shape classes didn't do that, that's not a C++ problem

No? Then why do OTHER inheritance-oriented languages provide self-
identification of types automatically?

]No language,
]not even Ada can protect you against stupid programmers.

Uh huh. All the more reason not to oversell the benefits of inheritance,
considering you said earlier that building class hierarchies was very
difficult. If it is really that difficult--and I argue it is--then
Joe Blow is going to shoot his foot off. Giving the run-of-the-mill
hacker the ability to screw up in increasingly subtle and grandiose
ways does NOT strike me as a Great Leap Forward.

]C++ has a built-in type-discriminating mechanism called "dynamic
]binding".

Yah, I know--my C++ compiler provides this.

]By the way, how about adding the tag method like this:
]
]  typedef enum { CIRCLE_TAG, TRIANGLE_TAG, SQUARE_TAG } TAG;

Okay, so now add a rectangle. You're back to editing the tag enumeration,
and this is precisely how you'd do it in Ada.

]Still, this approach has it's problems, and I'm not claiming
]C++ to be superior to Ada. I am claiming, however, that
]dynamic binding is a valuable feature which Ada is missing.  

You're claiming it, but I don't think you've proven it.
--
* "Beyond 100,000 lines of code, you should probably be coding in Ada." *
*      - P.G. Plauger, Convener and Secretary of the ANSI C Committee   *
*                                                                       *
*                The opinions expressed herein are my own.              *

barmar@think.com (Barry Margolin) (04/25/91)

In article <DAVIS.91Apr24141956@barbes.ilog.fr> davis@barbes.ilog.fr (Harley Davis) writes:
>I don't know about C++, but in several Lisp-based OO languages this is
>cake.  Here's a solution in EuLisp:
...
> ;;; Printing out the radii of circles in a list.
>    
> (defun print-radii (shape-list)
>   ;; Prints the radii of all the circles in SHAPE-LIST.
>   (mapc shape-list
>         (generic-lambda (shape)
>           method (((shape circle))
>                   (print (circle-radius shape))))))

Don't you need to define a default method for type shape?  Or does EuLisp's
GENERIC-LAMBDA automatically define a default method that does nothing?
Also, I think you got the arguments to MAPC out of order (or is this a
gratuitous incompatibility between EuLisp and Common Lisp?).

>There you go.  Nearly identical solutions will work in CommonLisp and
>Le-Lisp, and probably every other Lisp with even the simplest object
>oriented framework.

Yes, OO programming in Smalltalk and many Lisp dialects generally doesn't
have many of the limitations that it does in C++ and similar languages.
The problem with C++ and friends is that the complete set of methods of a
class must be declared in the class definition.  In Lisp and Smalltalk,
methods can be added to a class at any time, so the person implementing a
generic function can decide which classes implement it.

If OO programming is supposed to make incremental development and
evolutionary programming easier, then the restrictions of C++ can be quite
severe.  There needs to be at least a way to specify a default method for
classes that don't implement an operation, or a way to test class
membership (Smalltalk and CLOS, naturally, have both, just to be complete).


--
Barry Margolin, Thinking Machines Corp.

barmar@think.com
{uunet,harvard}!think!barmar

gtephx (Tim Barrios) (04/25/91)

In article <1991Apr23.170144.22538@neon.Stanford.EDU>, philip@pescadero.Stanford.EDU (Philip Machanick) writes:
> For the record: I used Ada before I used C. I need an object-oriented
> language, and Ada isn't one. Maybe C++ is a messy compromise, which
> attempts to patch over a lot of outdated design decisions, but its
> object-oriented features are a lot better than Ada's.

i agree with your comment.  however, on large projects, i'd rather use
a language that supports software engineering rather than one that
supports 'pure' OOP.

-- 
Tim Barrios, AG Communication Systems, Phoenix, AZ
UUCP: ...!{ncar!noao!asuvax | uunet!zardoz!hrc | att}!gtephx!barriost
Internet: gtephx!barriost@asuvax.eas.asu.edu
voice: (602) 582-7101        fax:   (602) 581-4022

diamond@jit345.swstokyo.dec.com (Norman Diamond) (04/25/91)

In article <jls.672445428@rutabaga> jls@rutabaga.Rational.COM (Jim Showalter) writes:

>>Frankly, I didn't find the Ada particularly readable -- it was too large
>>to fit on one screen and I lost interest in the exercise.  The C code
>>(once it was formatted and its variables made more useful) was small
>>enough to be seen in its entirety, allowing the eye to follow
>>interpretive explorations easily and quickly.
>
>Uh huh. And did you catch the bugs?

The bugs in the C version or in the Ada version?  They had one bug in
common but the other bugs were different.  Anyway, the odds of catching
a bug increase if one is looking for bugs, or if one is examining the
program, rather than if one is examining the formatting style.
--
Norman Diamond       diamond@tkov50.enet.dec.com
If this were the company's opinion, I wouldn't be allowed to post it.

davis@barbes.ilog.fr (Harley Davis) (04/25/91)

In article <1991Apr24.212237.29388@Think.COM> barmar@think.com (Barry Margolin) writes:

   > ;;; Printing out the radii of circles in a list.
   >    
   > (defun print-radii (shape-list)
   >   ;; Prints the radii of all the circles in SHAPE-LIST.
   >   (mapc shape-list
   >         (generic-lambda (shape)
   >           method (((shape circle))
   >                   (print (circle-radius shape))))))

   Don't you need to define a default method for type shape?  Or does EuLisp's
   GENERIC-LAMBDA automatically define a default method that does nothing?
   Also, I think you got the arguments to MAPC out of order (or is this a
   gratuitous incompatibility between EuLisp and Common Lisp?).

You are correct on both points.  Sorry.  EuLisp generally tries to
avoid gratuitous imcompatibilities with CommonLisp.

-- Harley

--
------------------------------------------------------------------------------
nom: Harley Davis			ILOG S.A.
net: davis@ilog.fr			2 Avenue Gallie'ni, BP 85
tel: (33 1) 46 63 66 66			94253 Gentilly Cedex, France

juul@diku.dk (Anders Juul Munch) (04/26/91)

jls@rutabaga.Rational.COM (Jim Showalter) writes:

>]>Now, I want to take a heterogeneous list of shapes, including triangles,
>]>circles, and squares, and I want to iterate over the list and print
>]>out all of the radii.

>]Who wrote the heterogeneous-list code? The guy who supplied the shape
>]classes? Of course not, because then he would have realized the need
>]for a shape method to identify exactly what kind of shape it is.

>Uh huh. Precisely the point made in "Emperor Strikes Lethe"--you DON'T
>have any control over the guy who writes the class hierarchy upon which
>you depend. Indeed, in my example I specifically said that the class
>hierarchy is delivered in binary for security/monetary reasons, so you're
>at the mercy of it completely. And you just want to add a simple little
>class with some additional functionality--seems reasonable to me--but
>you CAN'T.

Apparently, while arguing *against* the usefulness of inheritance,
at the same time you are demanding that inheritance be used to
it's full potential. "And you just want to add a simple little class."
In that case, inheritance can't possibly be a hindrance.

>]That's why any C++ library programmer with just a little bit of sense
>]adds such an operation. If the guy who wrote your binary collection
>]of shape classes didn't do that, that's not a C++ problem

>No? Then why do OTHER inheritance-oriented languages provide self-
>identification of types automatically?

I agree that self-identification should be a language feature, and 
as far as I know, it will be. But I still don't think it's a  
a C++ problem, because it's so easy to add an equivalent feature
to any given class hierarchy. Provided you have the source code.
Bjarne Stroustrup didn't include the feature originally, because 
he didn't want to encourage code like
	switch(aShape.Class())
	{
		case CIRCLE: DrawCircle(aShape); break;
		case TRIANGLE: DrawTriangle(aShape); break;
		...

>]No language,
>]not even Ada can protect you against stupid programmers.

>Uh huh. All the more reason not to oversell the benefits of inheritance,
>considering you said earlier that building class hierarchies was very
>difficult. If it is really that difficult--and I argue it is--then
>Joe Blow is going to shoot his foot off. Giving the run-of-the-mill
>hacker the ability to screw up in increasingly subtle and grandiose
>ways does NOT strike me as a Great Leap Forward.

Who said object-oriented programming was for novices? On the contrary,
the benefits of OOP show when developing larger programs, and designing
a good class hierarchy doesn't come automatic, all of which makes
OOP more suited for the professional. True, OOP programming is being
oversold as a more natural way of programming. Personally, I don't
consider adding 'a' and 'b' by sending the message '+(b)' to 'a'
particularly natural.
	As for C++, the language is intended as the OOP upgrade
for C programmers -- i.e. not novices.

>]C++ has a built-in type-discriminating mechanism called "dynamic
>]binding".

>Yah, I know--my C++ compiler provides this.

>]By the way, how about adding the tag method like this:
>]
>]  typedef enum { CIRCLE_TAG, TRIANGLE_TAG, SQUARE_TAG } TAG;

>Okay, so now add a rectangle. You're back to editing the tag enumeration,
>and this is precisely how you'd do it in Ada.

I never said anything else. The point is that the problem can be
solved, and that it can be solved simply in just a few lines of code. 
Is this some kind of contest, where solutions are required not to
make use of any language features also found in Ada? You designed 
the problem so that it would be difficult to solve it only by means
of inheritance, so of course I grab into the bag of C++ features 
for something else that will solve the problem. And I find it. 

>]Still, this approach has it's problems, and I'm not claiming
>]C++ to be superior to Ada. I am claiming, however, that
>]dynamic binding is a valuable feature which Ada is missing.  

>You're claiming it, but I don't think you've proven it.

That's right, I'm claiming it. And I'm not proving it, because I don't
have the time for writing examples tenthousands of lines long.


-- Anders Munch

paj@mrcu (Paul Johnson) (04/26/91)

>]>Now, I want to take a heterogeneous list of shapes, including triangles,
>]>circles, and squares, and I want to iterate over the list and print
>]>out all of the radii.
>
>]Who wrote the heterogeneous-list code? The guy who supplied the shape
>]classes? Of course not, because then he would have realized the need
>]for a shape method to identify exactly what kind of shape it is.
>
>Uh huh. Precisely the point made in "Emperor Strikes Lethe"--you DON'T
>have any control over the guy who writes the class hierarchy upon which
>you depend. Indeed, in my example I specifically said that the class
>hierarchy is delivered in binary for security/monetary reasons, so you're
>at the mercy of it completely. And you just want to add a simple little
>class with some additional functionality--seems reasonable to me--but
>you CAN'T.

[ Long article which presuposes that the way to solve this problem is
via type tags ]

*READ* *MY* *LIPS* ! *NO* *MORE* *TYPE* *TAGS*

The original problem was to iterate over a list of generic shapes and
print out the radii of those that had them.  The way to do this is to
keep a separate list of objects with a radius and walk down that.  You
do NOT start sticking in CASE statements on type tags.  These are
precisely the things that OO programming is supposed to avoid.  Once
you put in one such routine, they proliferate, and you keep having to
go back to existing functions to add new cases for your new classes.

Later in the article the point was made that "all" languages except
C++ contain a way of finding out what type you are dealing with.  I
don't know about Smalltalk, but reverse assignment was included in
Eiffel in order to handle situations (such as reconstituting objects
from a flat file) where static type information is unavailable.  It
has no other purpose.

If you find yourself using type tags or some similar dodge, you have
done it wrong and are just storing up trouble.

Now don't let me have to tell you again!   :-)

Paul.

-- 
Paul Johnson |            Isn't modern education wonderful: one size fits all!
-------------^------------------v-------------------------v-------------------
GEC-Marconi Research is not 	| Telex: 995016 GECRES G  | Tel: +44 245 73331
responsible for my opinions.	| Inet: paj@gec-mrc.co.uk | Fax: +44 245 75244

jls@rutabaga.Rational.COM (Jim Showalter) (04/27/91)

>Apparently, while arguing *against* the usefulness of inheritance,

I did no such thing. I merely asked how to do a simple thing in C++.
The answer, so far, is "Uh, well, that's not one of the things you
can do easily in C++." Considering that my original question was
motivated by Bjarne Stroustrup's somewhat self-serving comparison
of the ease with which he can express inheritance in C++ vs Ada,
I think pointing out that I can find a large number of quite simple
couterexamples in which it is no cleaner in C++ than Ada (and Ada
doesn't HAVE inheritance...) is fair.

>at the same time you are demanding that inheritance be used to
>it's full potential.

Let's take a vote: how many OO afficionados out there believe that
being able to add additional operations to derived classes is a
tremendously radical desire on the part of someone using inheritance?

>I agree that self-identification should be a language feature, and 
>as far as I know, it will be. But I still don't think it's a  
>a C++ problem, because it's so easy to add an equivalent feature
>to any given class hierarchy. Provided you have the source code.

Right. Now reread my example: the existing class hierarchy is delivered
in BINARY. This is the real world: people want to protect their source.
So, you don't HAVE source code. Now run off and try again to solve my
example. I'll be here when you get back...

>>Uh huh. All the more reason not to oversell the benefits of inheritance,
>>considering you said earlier that building class hierarchies was very
>>difficult. If it is really that difficult--and I argue it is--then
>>Joe Blow is going to shoot his foot off. Giving the run-of-the-mill
>>hacker the ability to screw up in increasingly subtle and grandiose
>>ways does NOT strike me as a Great Leap Forward.

>Who said object-oriented programming was for novices?

Well, gee--just about every marketing blurb I've read about it since
about 1985. You know, the stuff that reads: "Hey, Mr. Software Manager,
I'm Mr. Object and I'm here to help you and your team be ever so much
more productive than you've been in the past! Object-oriented programming
is MORE NATURAL, MORE INTUITIVE, MORE EASY than any other kind of programming.
Why, a PINHEAD can do it!" and so forth and so on. Now, I know from 
experience that this is a load of crap...but until the snake oil salesmen
stop selling snake oil, I think it's fair to hold them to their claims,
don't you?

>	As for C++, the language is intended as the OOP upgrade
>for C programmers -- i.e. not novices.

Uh, I regard at least a significant number of C programmers as novices.
I mean, they learn it in school, right?

>Is this some kind of contest, where solutions are required not to
>make use of any language features also found in Ada?

Frankly, yes. since I was responding to Bjarne Stroustrup's claim
that inheritance was a paradigm not well-supported by Ada, in contrast
to C++. So it seems QUITE fair to me to come up with what I consider
simple examples of places where C++ support for inheritance degenerates
into the exact same solution you'd use in a language that doesn't even
claim to HAVE inheritance. Does this seem unfair to you? If so, WHY?

>I don't have time for writing examples tens of thousands of lines long.

Right, and neither, I've noticed, does anybody else. What they do
instead is write dinky toy examples a few hundred lines long, if that,
write a book, and sell snake oil to the masses. People who actually
then try to take the mechanism demonstrated in the toy example and
apply it to the real world soon discover that things are a lot nastier
than they'd been led to believe. It is from experiences like this that
folks wind up writing "Emperor Strikes Lethe" essays: after all, this
is from a guy who TRIED to build OO systems and ran into all kinds of
problems. The Cognos boondoggle is another example of magnificent failure.
And my simple examples of C++ inheritance collapse are another.
--
* "Beyond 100,000 lines of code, you should probably be coding in Ada." *
*      - P.G. Plauger, Convener and Secretary of the ANSI C Committee   *
*                                                                       *
*                The opinions expressed herein are my own.              *

preece@urbana.mcd.mot.com (Scott E. Preece) (04/27/91)

[jls responds to my note}
>>It would be foolish to program for the reading level of the novice
>>programmer
>
>Not really. The vast majority of work on software that goes on (like,
>about 70%) is MAINTENANCE. This is often performed by underpaid,
>underskilled, and overwhelmed junior programmers, many of whom are
>unfamiliar with the original design, weren't around when "clever"
>decisions were initially made, etc. To NOT program from the outset
>with an eye toward making the job easier for the downstream maintainer
>is criminal.
---
I'm not talking about "clever" decisions or obscure programming tricks,
I'm talking about idiomatic use of a programming language.  The only
measure of readability should be how the program text conveys its
purpose to a person reasonably fluent in the language.  Otherwise, if
the goal is language-independent comprehension, we need to be talking in
entirey different terms, like diagrams and English text...
--
scott preece
motorola/mcg urbana design center	1101 e. university, urbana, il   61801
uucp:	uunet!uiucuxc!udc!preece,	 arpa:	preece@urbana.mcd.mot.com
phone:	217-384-8589			  fax:	217-384-8550

philip@pescadero.stanford.edu (Philip Machanick) (04/27/91)

In article <jls.672698352@rutabaga> jls@rutabaga.Rational.COM (Jim Showalter)
writes:
>>Apparently, while arguing *against* the usefulness of inheritance,
>
>I did no such thing. I merely asked how to do a simple thing in C++.
>The answer, so far, is "Uh, well, that's not one of the things you
>can do easily in C++."
OK, so how easily _can_ this be done in C++? For anyone who's lost track,
the original example was
   Consider the shapes example. Suppose that the given example goes
   out in binary form, and arrives at my site, and I want to add a new
   shape. Suppose that the initial shapes were limited to triangles and
   squares. Suppose that I now add a Circle, which has a new method
   defined for it that does NOT apply to triangles or squares and which
   was never previously defined in the base class for shapes--radius.
   This method returns the radius as some floating point number from 0
   to whatever.
(I used integers to save typing - ignore the details).
   Now, I want to take a heterogeneous list of shapes, including triangles,
   circles, and squares, and I want to iterate over the list and print
   out all of the radii.

   How do I do this? I can't get elements out of the list and call
   the Radius method on all of them, because not all of them HAVE such
   a method defined. I can't add the new method to the base class (with
   a null implementation as the default for those shapes for which it
   is a meaningless operation) because the base class is in binary. I
   can't ask the shapes to tell me their Kind because there is no such
   operation defined on them in C++.

   It is my claim that solving this problem in C++ results in a solution
   that is every bit as messy as simply using a discriminated record
   and an enumeration type in Ada--and Ada doesn't HAVE inheritance.
My solution is reasonably simple, if not as clean as I'd like. I wrap a new
class "gen_shape" around the "library" class "shape", with inlined calls to all
the methods in shape. I then add a new method (member function in C++), which
computes the radius. As a bonus, I include an enumerated type giving names to
all the shape types, so I can distinguish between them.

Here's some detail:
--
#include "Shapes.h" /* the header for the library */

class circle : public shape
{public:
  // assume we can compute this from data common to other shapes
  int get_radius ();
};

enum shape_type {triangular, circular};  // plus others in package

// extend the provided shape class to do what we want
// note that the redefined methods do not need to be virtual, as the
// call to the contained object will be via the virtual function table
class gen_shape
{public:
  gen_shape (int length, int width, int height, int x_pos, int y_pos);
  void draw () {the_shape->draw ();}
// plus the same for the rest of the class .....
// added for circles
  int get_radius ();
protected:
  shape *the_shape;
  shape_type kind;
};

// and here's how radius is implemented
int gen_shape :: get_radius ()
{  if (kind == circular)
     return ((circle*) the_shape)->get_radius ();
   else return the_shape->get_height () / 2;  // or whatever makes sense
}
--
This is slightly clumsy, and requires merging some of the supplied library
header file with your own code, but perhaps not as bad as one might expect. The
"default" case is still handled simply (using inheritance), and only the
"exception" requires a patchy workaround.

I think this is in the spirit of good language design: handle the common case
well.

Could you do better in Ada?

Philip Machanick

dlw@odi.com (Dan Weinreb) (04/28/91)

In article <jls.672698352@rutabaga> jls@rutabaga.Rational.COM (Jim Showalter) writes:


   I did no such thing. I merely asked how to do a simple thing in C++.
   The answer, so far, is "Uh, well, that's not one of the things you
   can do easily in C++." Considering that my original question was
   motivated by Bjarne Stroustrup's somewhat self-serving comparison
   of the ease with which he can express inheritance in C++ vs Ada,
   I think pointing out that I can find a large number of quite simple
   couterexamples in which it is no cleaner in C++ than Ada (and Ada
   doesn't HAVE inheritance...) is fair.

It's true that this is something that isn't supported in a good way in
C++.  You have now demonstrated one (not "a large number of") example
of a problem with C++.

In fact, in my discussions with other people, like myself, who have
used both Flavors/CLOS and C++, the example that you brought up is the
single typical thing that we most often point to when we want to show
something that Flavors/CLOS does better than C++.  We have not found
"a large number" of things of this type.  There are, of course, many
variations on your example, that all boil down to the same problem.

(By the way, the way that this is solved in Flavors/CLOS does not in
any way depend on "type tags", on "what is my class" methods, or
anything like that.  The key to the solution in Flavors/CLOS is that
you can add a "radius-of" method to the class from which all classes
inherit, dynamically, at any time, without having the sources of the
shape library and without needing to compile it.  I am not an expert
on Smalltalk but I believe that the solution would be the same in
Smalltalk as well.  Note that the real problem here has to do with the
question of which sources need to be modified and recompiled.  C++, as
language, can do what we want; the real problem arises when you try to
distribute binaries without sources.  So I would argue that this is
more an issue of implementation and/or programming environment.  Those
are very important things, to be sure.)

So, what you have shown is that C++'s OOP is not perfect.  It's still
a whole lot better than not having OOP at all.  You have not
demonstrated that "Ada's OOP is just as good as C++'s" or anything
like that.

   Frankly, yes. since I was responding to Bjarne Stroustrup's claim
   that inheritance was a paradigm not well-supported by Ada, in contrast
   to C++. So it seems QUITE fair to me to come up with what I consider
   simple examples of places where C++ support for inheritance degenerates
   into the exact same solution you'd use in a language that doesn't even
   claim to HAVE inheritance. Does this seem unfair to you? If so, WHY?

So you have shown that C++'s OOPS doesn't do everything that it ought
to be able to do in all cases.  It's still true that C++ supports
inheritance far more than Ada does.

   >I don't have time for writing examples tens of thousands of lines long.

   Right, and neither, I've noticed, does anybody else. What they do
   instead is write dinky toy examples a few hundred lines long, if that,
   write a book, and sell snake oil to the masses. People who actually
   then try to take the mechanism demonstrated in the toy example and
   apply it to the real world soon discover that things are a lot nastier
   than they'd been led to believe.

The company I work for produces and sells an object-oriented database
system written in C++.  I just counted the amount of code in it.  This
is a based on a Unix "wc" count of only those files that we wrote from
scratch in C++, not counting any code we got from anywhere else even
if we modified it.  The database system is currently at least 185,700
lines of code, and the user interface tools and our C++-coded portable
user interface layer are about another 65,600 lines.  I am personally
responsible for about 33,000 of those lines.

Naturally, at this point we have a great deal of experience with C++.
On the whole, while it has a number of problems, it has satisfied our
needs well.  The problem that you're talking about hasn't arisen; in
fact, we don't have any binary-only C++ libraries at all, so it's not
surprising that the problem hasn't come up in our particular
circumstance.  But your claim that nobody has written large programs
successfully in C++ will not stand up to real world evidence.

We do use object-oriented programming and virtual functions and
inheritance, quite a lot.  It's very useful in practice, all over the
software system in many different ways.  If we didn't have it, we'd
certainly need to write it out "longhand" in order to obtain the same
effect, which would make our code a lot harder to read and maintain,
if nothing else.  I would really hate to write these programs in a
language that didn't provide some kind of object-oriented programming.
C++ certainly isn't perfect, and I've certainly spent time being
miffed at its limitations now and then, but in most cases it does the
job just fine.

jls@rutabaga.Rational.COM (Jim Showalter) (04/30/91)

]>The original problem was to iterate over a list of generic shapes and
]>print out the radii of those that had them.  The way to do this is to
]>keep a separate list of objects with a radius and walk down that.  You
]>do NOT start sticking in CASE statements on type tags.

This merely pushes the problem down one level. I now ask: how does one
OBTAIN the separate list of objects with a radius?

]>Later in the article the point was made that "all" languages except
]>C++ contain a way of finding out what type you are dealing with.

I did not make this claim--how the hell should I know what all languages
offer. I quite specifically said that C++ does not offer self-identifying
types, but that Smalltalk, Eiffel, and CLOS do (to the best of my knowledge).

]>If you find yourself using type tags or some similar dodge, you have
]>done it wrong and are just storing up trouble.

I agree in principle. But you still haven't shown me how to solve my
example in C++ WITHOUT using tags of some kind--which is my point.
--
* "Beyond 100,000 lines of code, you should probably be coding in Ada." *
*      - P.J. Plauger, Convener and Secretary of the ANSI C Committee   *
*                                                                       *
*                The opinions expressed herein are my own.              *

jls@rutabaga.Rational.COM (Jim Showalter) (04/30/91)

>The only
>measure of readability should be how the program text conveys its
>purpose to a person reasonably fluent in the language.

Strongly disagree, and you may have struck the heart of the
argument. It is pointless to test readability of language by
asking people who already CAN read the language (your "reasonably
fluent" programmers) to judge readability. There are people who
are "reasonably fluent" in FORTH. Does that make FORTH readable?
Hardly. Same goes for APL, and other such cryptic "languages"
(I put "languages" in quotes because at some point things get
so unreadable that comprehension diminishes to the point where
it is arguable that communication no longer takes place--at
which point it is arguable that language no longer exists).

To argue that a person can learn to read just about anything,
including Mayan petroglyphs and Egyptian hieroglyphs begs the
issue: consider the ramp-up time involved in doing so and then
ask if it makes BUSINESS sense to make it so difficult to
understand a language. The more time spent deciphering, the less
time spent designing and implementing. Seems simple to me.

The example I submitted in Ada was deliberately crafted to read
as much as possible like English. You can do that in Ada (without
expending much effort, I might add). You can
NOT do that in many other languages, and the 20:1 ratio of comments
to code in one of the C examples submitted is proof of that claim.

I have watched C programmers attempt to understand their OWN code--written
not more than a few weeks earlier--and fail. This strikes me as rather
damning. I can't think of an argument FOR such things--especially not
a business argument.
--
* "Beyond 100,000 lines of code, you should probably be coding in Ada." *
*      - P.J. Plauger, Convener and Secretary of the ANSI C Committee   *
*                                                                       *
*                The opinions expressed herein are my own.              *

jls@rutabaga.Rational.COM (Jim Showalter) (04/30/91)

]>As a bonus, I include an enumerated type giving names to
]>all the shape types, so I can distinguish between them.

I think another poster will take you to task for this.

Besides, this is exactly how I'd do it in Ada.
--
* "Beyond 100,000 lines of code, you should probably be coding in Ada." *
*      - P.J. Plauger, Convener and Secretary of the ANSI C Committee   *
*                                                                       *
*                The opinions expressed herein are my own.              *

jls@rutabaga.Rational.COM (Jim Showalter) (04/30/91)

]>It's true that this is something that isn't supported in a good way in
]>C++.  You have now demonstrated one (not "a large number of") example
]>of a problem with C++.

I have demonstrated one simple form of inheritance that is not handled
well by C++. The number of individual CASES that follow that form is
essentially infinite. Last time I looked this qualified as a "large
number".

]>So, what you have shown is that C++'s OOP is not perfect. It's still
]>a whole lot better than not having OOP at all.

Is having support for what I call "special-case inheritance" good
enough to get all hot and bothered about? I have no quarrel with the
notion of inheritance, and I have no argument with the assertion that
inheritance is useful. What I DO take exception to is the blind assumption
that just because a language labels itself an OOP language it must by
necessity then BE an OOP language. The support that C++ offers for
inheritance is such that it works non-clumsily ONLY when all operations
in derived classes are represented in the base class (although they
can certainly be overridden in the derived classes). It falls over
dead any time I want to subset or superset said operations, which turns
out to be rather often.

]>You have not
]>demonstrated that "Ada's OOP is just as good as C++'s" or anything
]>like that.

What I've demonstrated is that for a large number of inheritance
problems it is just as easy to use Ada--which never even claimed
to BE an OOP--as it is to use C++.

>So you have shown that C++'s OOPS doesn't do everything that it ought
>to be able to do in all cases.  It's still true that C++ supports
>inheritance far more than Ada does.

First of all, see above. Second of all, are you aware that Ada support
special-case inheritance directly via derived types? It may be slightly
clumsier than in C++, but the support IS there, and it never ceases
to amaze me that so many people have this notion that Ada has no such
support at all. It aint exactly FORTRAN, you know...

]>The company I work for produces and sells an object-oriented database
]>system written in C++.  I just counted the amount of code in it.  This
]>is a based on a Unix "wc" count of only those files that we wrote from
]>scratch in C++, not counting any code we got from anywhere else even
]>if we modified it.  The database system is currently at least 185,700
]>lines of code, and the user interface tools and our C++-coded portable
]>user interface layer are about another 65,600 lines.  I am personally
]>responsible for about 33,000 of those lines.

Fine. Now--of that code, how much exploits inheritance? How much actually
relies more on the strong typing, interface specification, and opaque
types of C++? How much use do you make of exceptions and templates? In
short, how much of the advantages you perceive from using C++ are
due to its support for inheritance, and how much is due to its support
for software engineering principles such as data abstraction and
information hiding--support that is also robustly available in Ada, with
far greater tool maturity?
--
* "Beyond 100,000 lines of code, you should probably be coding in Ada." *
*      - P.J. Plauger, Convener and Secretary of the ANSI C Committee   *
*                                                                       *
*                The opinions expressed herein are my own.              *

rockwell@socrates.umd.edu (Raul Rockwell) (04/30/91)

Unattributed:
? >The only measure of readability should be how the program text
? >conveys its purpose to a person reasonably fluent in the language.

Jim Showalter:
> Strongly disagree, and you may have struck the heart of the
> argument. It is pointless to test readability of language by asking
> people who already CAN read the language (your "reasonably fluent"
> programmers) to judge readability.

All languages, then, are unreadable.  Including English, including
(yeah, I know this is hard to believe) ADA.  Ask anyone (excluding
people who can already read the language, because that's pointless).

>  [lots of stuff saying why ADA is more readable than various other
>   languages because it obviously is]

Well folks, I guess it's obvious -- ADA is The One True Way.  And this
is because of GOOD BUISINESS REASONS.  Therefore, if you think
otherwise, You Are a Wimp.

And it's More Efficient too.

(now how do I declare an object to be sarcastic...
((in ADA, of course...
)))

Raul Rockwell

hinton@gca.UUCP (Edward Hinton) (04/30/91)

(My apologies if I make a mistake on this posting, it's my first.)

In article <PREECE.91Apr26171318@etude.urbana.mcd.mot.com>, preece@urbana.mcd.mot.com (Scott E. Preece) writes:
> >>It would be foolish to program for the reading level of the novice
> >>programmer
> >
> >Not really. The vast majority of work on software that goes on (like,
> >about 70%) is MAINTENANCE. This is often performed by underpaid,
> >underskilled, and overwhelmed junior programmers, many of whom are
> >unfamiliar with the original design, weren't around when "clever"
> >decisions were initially made, etc. To NOT program from the outset
> >with an eye toward making the job easier for the downstream maintainer
> >is criminal.
> ---
> I'm not talking about "clever" decisions or obscure programming tricks,
> I'm talking about idiomatic use of a programming language.  The only
> measure of readability should be how the program text conveys its
> purpose to a person reasonably fluent in the language.  Otherwise, if
> the goal is language-independent comprehension, we need to be talking in
> entirey different terms, like diagrams and English text...
> --

Ahhhhh, now we get to something that's actually important.  Any language
can be readable/maintainable to those 'fluent' in the language if the code
is written to that end AND THE READER UNDERSTANDS WHAT IT IS SUPPOSED TO DO.
Comprehension needs to start with documentation using diagrams and English text
and then be followed by code which cleanly implements what is described.
I know this may be foreign to those whose only goal is number of KLOC they
write (no flames, please), but I am foolish enough to believe what I was
taught in my MSE degree program (as opposed to MSCS).  That is, the real
documentation is NOT the code.  Given this axiom, it seems that the choice of
implementation language is best made according to the tools available and
the anticipated skils of those doing the programming.  Most often, the claim
that this consists of 'junior' programmers is the truth.  After all, how many
highly paid experienced programmers can a software manager expect to keep
if they are the ones doing software maintenance?  It's easy to say that they
should be the ones doing it, but after they all quit, I'm back to where we
started.  As a note from whence I speak: I manage a small group of software
engineers and have seen good as well as bad.  Less good than bad over the past
ten years as a software engineer.

By the way, since I have never written a single line of either C++ or Ada, I guess
I really am arguing for language-independent comprehension.  Now there's a
sidetrack people can hop on me for.

philip@pescadero.stanford.edu (Philip Machanick) (04/30/91)

In article <jls.672979879@rutabaga> jls@rutabaga.Rational.COM (Jim Showalter)
writes:
>What I've demonstrated is that for a large number of inheritance
>problems it is just as easy to use Ada--which never even claimed
>to BE an OOP--as it is to use C++.
No you haven't. You've demonstrated that C++ programmers have to think a bit to
solve a specific problem, and the solution is not quite as clean as one may
like it to be. The solution I posted nonetheless does use inheritance, and only
requires replacing it by a clumsier mechanism in one place. This does reveal a
weakness of C++, but not a fatal flaw. Nonetheless, it is at least partial
support for half your argument.

Now, to demonstrate the other half of your argument, you need to present the
Ada solution. I've thought about it a bit and, in terms of your original
problem statement, it isn't obvious to me. (How do you extend an enumerated
type in Ada without running into problems with code that's already compiled?)
I'm not an expert Ada programmer - maybe I'm missing something.

Maybe we can all learn something from this, instead of treating it as yet
another silly language war.

Philip Machanick

dlw@odi.com (Dan Weinreb) (05/01/91)

In article <jls.672979005@rutabaga> jls@rutabaga.Rational.COM (Jim Showalter) writes:

   >The only
   >measure of readability should be how the program text conveys its
   >purpose to a person reasonably fluent in the language.

   Strongly disagree, and you may have struck the heart of the
   argument. It is pointless to test readability of language by
   asking people who already CAN read the language (your "reasonably
   fluent" programmers) to judge readability. 

I see.  Then let's give your Ada program to someone who has never
programmed a computer in any language at all, and ask for an
interpretation.

Not fair?  OK, let's find someone who has only ever programmed in
assembly language.  Or someone who has only ever programmed in APL.
Do you think that it will be intuitively obvious which keywords
indicate iteration?  Is knowledge of Ada syntax something that human
being are born with?

   I have watched C programmers attempt to understand their OWN code--written
   not more than a few weeks earlier--and fail. This strikes me as rather
   damning. I can't think of an argument FOR such things--especially not
   a business argument.

Of course, it is fundamentally impossible for this to happen to Ada
programmers.

gtephx (Tim Barrios) (05/02/91)

In article <619@pickerel.UUCP>, hinton@gca.UUCP (Edward Hinton) writes:
> > >Not really. The vast majority of work on software that goes on (like,
> > >about 70%) is MAINTENANCE. This is often performed by underpaid,
> > >underskilled, and overwhelmed junior programmers, many of whom are
> > >unfamiliar with the original design, weren't around when "clever"
> > >decisions were initially made, etc. To NOT program from the outset
> > >with an eye toward making the job easier for the downstream maintainer
> > >is criminal.
> Ahhhhh, now we get to something that's actually important.  Any language
> can be readable/maintainable to those 'fluent' in the language if the code
> is written to that end AND THE READER UNDERSTANDS WHAT IT IS SUPPOSED TO DO.
> Comprehension needs to start with documentation using diagrams and English text
> and then be followed by code which cleanly implements what is described.
> I know this may be foreign to those whose only goal is number of KLOC they
> write (no flames, please), but I am foolish enough to believe what I was
> taught in my MSE degree program (as opposed to MSCS).  That is, the real
> documentation is NOT the code.  Given this axiom, it seems that the choice of
> implementation language is best made according to the tools available and
> the anticipated skils of those doing the programming.

yes, now we are getting somewhere!  i agree that in new development,
the language itself is not as important as things like design
representations but in software maintenance (70% of the picture),
usually, the only thing that is REALLY up to date is the code.  for
this reason, i would prefer CASE tools that keep a one-to-one mapping
between design deliverables and source code rather than generating
source code (specifications, .h files) from the design.  if we had to
go only in one direction, i'd prefer to generate the detailed design
from the source code (can you say Ada PDL?).  i understand that tools
like this are available in both languages (Ada: PDLs, Rational
environment; C++: Pilot (?)).

the real issue in this discussion is the software engineering which,
in the real world, is [at least] 70% maintenance.  the technologies
supporting software development (CASE, languages, OO*) should be
more concerned about software maintenance rather than the relatively
insignificant part (initial development).

-- 
Tim Barrios, AG Communication Systems, Phoenix, AZ
UUCP: ...!{ncar!noao!asuvax | uunet!zardoz!hrc | att}!gtephx!barriost
Internet: gtephx!barriost@asuvax.eas.asu.edu
voice: (602) 582-7101        fax:   (602) 581-4022

jeff@hilbert.uucp (Jeff Freedman) (05/04/91)

>In article <jls.672698352@rutabaga> jls@rutabaga.Rational.COM (Jim Showalter)
>writes:
>   Consider the shapes example. Suppose that the given example goes
>   out in binary form, and arrives at my site, and I want to add a new
>   shape. Suppose that the initial shapes were limited to triangles and
>   squares. Suppose that I now add a Circle, which has a new method
>   defined for it that does NOT apply to triangles or squares and which
>   was never previously defined in the base class for shapes--radius.
>   This method returns the radius as some floating point number from 0
>   to whatever.
>(I used integers to save typing - ignore the details).
>   Now, I want to take a heterogeneous list of shapes, including triangles,
>   circles, and squares, and I want to iterate over the list and print
>   out all of the radii.

This is easy under the library which we're using.  I would be able to write,
without any changes to the Shapes class or methods, the following:

class Circle : public Shape
	{
	float radius;
public:
	..... (constructor, destructor, etc.)
	float get_radius() { return radius; }
	};

/*
 *	Print the radii of all the circles in a list of shapes.
 */

void Print_radii(Shape_list *shapes)
	{
					// Iterate through list.
	Shape_list_iterator next(shapes);
	Shape *each;
	while (next(each))
		{
					// A circle?  Print radius if so.
		Circle *circle = AS_A(each, Circle);
		if (circle)
			cout << circle->get_radius() << '\n';
		}
	}

Our library and environment were designed in-house, but I believe that ET++
and NIH provide similar facilities.

jls@netcom.COM (Jim Showalter) (05/09/91)

]>   >The only
]>   >measure of readability should be how the program text conveys its
]>   >purpose to a person reasonably fluent in the language.

]>   Strongly disagree, and you may have struck the heart of the
]>   argument. It is pointless to test readability of language by
]>   asking people who already CAN read the language (your "reasonably
]>   fluent" programmers) to judge readability. 

]>I see.  Then let's give your Ada program to someone who has never
]>programmed a computer in any language at all, and ask for an
]>interpretation.

I think the relevant question to ask is: given two pieces of code that
are functionally-equivalent (they both compute Julian dates, for example),
one written in C, the other written in Ada, both written for maximum
readability by equally expert programmers, how much training does someone
need in order to understand the C version, and how does this contrast with
the amount of training required to understand the Ada version? I argue
that less training is required to achieve comprehension in Ada, and that
this is an inherent advantage of Ada over C. Presuming this claim is true,
Ada's lower training-to-comprehension overhead translates into lowered
maintenance costs, since a person doesn't have to be a rocket scientist
to fiddle with the code.

]>Not fair?  OK, let's find someone who has only ever programmed in
]>assembly language.  Or someone who has only ever programmed in APL.
]>Do you think that it will be intuitively obvious which keywords
]>indicate iteration?  Is knowledge of Ada syntax something that human
]>being are born with?

Well, this is certainly a fair question, and I had to think about it
for a while to decide what my response is. I guess the answer is that,
yes, I DO believe that the keywords that indicate iteration are
intuitively obvious, and that Ada syntax IS something relatively
natural for humans. After all, one of the goals of the Ada language
designers was to make it regular, readable, intelligible, easy to
learn, etc. To the degree that they succeeded, I would expect it to
feel fairly natural to human beings. I've certainly been running on
the assumption that Ada is readable. Is it not? Is it gibberish to
the uninitiated? Is there an assembly language or APL programmer out
there who truly doesn't know anything about Ada who would care
to comment on whether the following code snippets make any sense?:

    for Current_Day in Monday..Friday loop
        Go_To_Work;
    end loop;
    
    while Batter_Remains_In_Bowl and People_Are_Still_Hungry loop
        Make_More_Pancakes;
    end loop;

    if You_Are_Sleepy then
        Go_To_Sleep;
    end if;

rockwell@socrates.umd.edu (Raul Rockwell) (05/10/91)

Jim Showalter:
>  Is there an assembly language or APL programmer out there who truly
>  doesn't know anything about Ada who would care to comment on
>  whether the following code snippets make any sense?:

Sure, I think I can qualify as an ignorant fool...   :-)

Qualifications: lotsa assembly work and machine language, I get paid
to work in APL, and I've not learned ADA.  (downside: I know C, LISP,
used to know PASCAL, and a handful of other languages...)

>      for Current_Day in Monday..Friday loop
>          Go_To_Work;
>      end loop;

Well, my first reaction is "that's pretty understandable".  Second is
"wait a minute, what data does "Go_To_Work" use???

Third reaction is that the "for" keyword only makes sense because I've
seen it so much.

Thinking about that a little more, I'd say that this is a trivial
operation.  I don't see that it is significantly clearer (or even
significantly more compact) than the following:

   Go_To_Work;     /* Monday    */
   Go_To_Work;     /* Tuesday   */
   Go_To_Work;     /* Wednesday */
   Go_To_Work;     /* Thursday  */
   Go_To_Work;     /* Friday    */

Or, if you like:

 /* Monday       Tuesday      Wednesday    Thursday     Friday     */
    Go_To_Work;  Go_To_Work;  Go_To_Work;  Go_To_Work;  Go_To_Work; 

Trivial problems are simple in any language.  If I had to maintain
that code, I'd be worried about side effects and what assumptions the
person who wrote it made about when it was going to be used.  (Since
it is clearly side effect driven code.)  (Assuming that it is part of
some large body of code.)


>      while Batter_Remains_In_Bowl and People_Are_Still_Hungry loop
>          Make_More_Pancakes;
>      end loop;

Well, this is better, because there is some presumably non-trivial
test.  There is still no hint of data flow, and unless I could speak
to the person who wrote it, I'm going to have to do global analysis on
the body of code (and the application environment) before I'd be
willing to touch that thing.

Also, "while" is more English like, to me, than "for".  Presumably the
point is how much like natural language this is.  However, I don't
care a whit how much like natural language the control structures are
-- I care about how easy it is to understand their implications.  I
haven't the slightest idea how "Batter_Remains_In_Bowl" is supposed to
tie in with anything else (though I suppose I ought to assume it's
some pre-initialized global variable...).

Of course, part of the problem here is that this is just a code
fragment, not a complete program.


>      if You_Are_Sleepy then
>          Go_To_Sleep;
>      end if;

Again, at first glance this communicates an understandable idea.
However this tells me nothing about the action of going to sleep (how
long?  Some hard coded constant?  Until the user selects "wake up"
with a mouse?  Until hell freezes over??)  (Well, it doesn't tell me a
lot of other things either, but that's what bothers me most.)


To attempt to summarize my feelings: the assembly language programmer
in me crys out "How is the data represented???" while the APL
programmer in me crys out "What is the data???"  And the systems
analyst part of me wonders "What is the context?"

Is this post in any way useful to anyone?  Or was I just answering a
rhetorical question?

Raul Rockwell, fool at large

Chris.Holt@newcastle.ac.uk (Chris Holt) (05/13/91)

Re Jim Showalter's simple Ada examples: Raul Rockwell made some
comments that make sense, but I'd add a couple more.  Qualifications:
Haven't looked at Ada for a decade, have been immersed in mathematics
and visual languages.

>>      for Current_Day in Monday..Friday loop
>>          Go_To_Work;
>>      end loop;

Hmm... A loop is an optimized tail recursion, it seems to take
an implicit argument Current_Day, drawn from a set.  Except that
the set is really a sequence.  I assume ForAll is not intended,
so a parallel implementation would be inappropriate?  Because
I'd want either
        ForAll Current_Day in {Monday..Friday} . Go_To_Work
or
        Current_Day := Monday;
        Loop:: If Current_Day > Friday Then skip
               Else Begin Go_To_Work; Next(Current_Day); Loop End;
these are both easier to verify from where I'm sitting.

>>      while Batter_Remains_In_Bowl and People_Are_Still_Hungry loop
>>          Make_More_Pancakes;
>>      end loop;

Assuming nothing stupid like side-effects, my only problem would
be how the functions in the condition are represented; people being
hungry would seem to require a fuzzy domain, and the batter might
be enough for a small but not a large pancake.  In particular, the
case of a person saying, "Well, okay, I'll have the last one just
to use up the batter" seems hard to treat.  This is not trivial;
interactions between conditions like that can often arise.

>>      if You_Are_Sleepy then
>>          Go_To_Sleep;
>>      end if;

This is a very imperative approach; presumably, if not sleepy then
fall through the command to whatever follows.  But putting it in
other terms, we have
        (You_Are_Sleepy => Go_To_Sleep; continuation) or
        (not(You_Are_Sleepy) => continuation)
and a proof of correctness would prefer this.

So my summary would be, how easy is it to prove that these constructs
satisfy their specifications?  I accept that this was not the question
being addressed, but even when minor notational changes are allowed
for, it has an effect on readability.
-----------------------------------------------------------------------------
 Chris.Holt@newcastle.ac.uk      Computing Lab, U of Newcastle upon Tyne, UK
-----------------------------------------------------------------------------
 "And when they die by thousands why, he laughs like anything." G Chesterton

dlw@odi.com (Dan Weinreb) (05/13/91)

In article <1991May9.040947.28053@netcom.COM> jls@netcom.COM (Jim Showalter) writes:

   I think the relevant question to ask is: given two pieces of code that
   are functionally-equivalent (they both compute Julian dates, for example),
   one written in C, the other written in Ada, both written for maximum
   readability by equally expert programmers, how much training does someone
   need in order to understand the C version, and how does this contrast with
   the amount of training required to understand the Ada version? 

OK, that's a good well-defined sort of question.  I should point out
that it depends on the program.  Ada is a larger, richer, more complex
language than C.  If a program used all of Ada's features, you'd have
more to learn to understand it.  The corresonding C program would
presumably require less learning, but would be more verbose, having to
do things "manually" that are built-in in Ada.  Ada's full abilities,
including some very elaborate kinds of overloading, and generics, and
so on, can take a while to learn.  But for simpler programs, I think
you could make a case that's pretty plausible, although not really easy
to convincingly demonstrate.  It's just a hard sort of thing to prove.

								  I argue
   that less training is required to achieve comprehension in Ada, and that
   this is an inherent advantage of Ada over C. Presuming this claim is true,
   Ada's lower training-to-comprehension overhead translates into lowered
   maintenance costs, since a person doesn't have to be a rocket scientist
   to fiddle with the code.

Maybe.  I know some people who are Ada experts (people who have built
parts of Ada compilers) who tell me that some of Ada's advanced
constructs can be rather confusing.  A change to the program in one
place can make a certain statement, far away, suddenly become
ambiguous, because of Ada's flexible and liberal overloading rules.  I
don't know Ada well enough to provide a good example (so I apologize
for making this non-backed-up claim).

   Well, this is certainly a fair question, and I had to think about it
   for a while to decide what my response is. I guess the answer is that,
   yes, I DO believe that the keywords that indicate iteration are
   intuitively obvious, and that Ada syntax IS something relatively
   natural for humans. After all, one of the goals of the Ada language
   designers was to make it regular, readable, intelligible, easy to
   learn, etc. To the degree that they succeeded, I would expect it to
   feel fairly natural to human beings. 

Well, the way you've phrased this, it's a circular argument.  "To the
degree that they succeeded" is the question we're discussing.

					I've certainly been running on
   the assumption that Ada is readable. Is it not? Is it gibberish to
   the uninitiated? Is there an assembly language or APL programmer out
   there who truly doesn't know anything about Ada who would care
   to comment on whether the following code snippets make any sense?:

No, I certainly would not say that.  The question was how it compares
with C.  Note that Ada and C both use "for" and "while".

       while Batter_Remains_In_Bowl and People_Are_Still_Hungry loop
	   Make_More_Pancakes;
       end loop;

In C, 

   while (Batter_Remains_In_Bowl && People_Are_Still_Hungry)
       Make_More_Pancakes;

It's not really that different.  The "and" probably is a bit more
obvious to the uninitiated than "&&", but this srikes me as a small
point.

If the body had more than one statement, it would be grouped in
braces.  It's not obvious to me that someone who had never seen the
convention before would realize that "end loop" is supposed to be a
closing delimiter that matches "loop", although it probably would not
be hard to figure out.  The fact that the braces are intended to match
is probably a bit more obvious.

       if You_Are_Sleepy then
	   Go_To_Sleep;
       end if;

In C, this is

   if (You_Are_Sleepy) {
       Go_To_Sleep;
   }

I used the braces this time.  I think it would be hard to say that
either is much more obvious than the other.

       for Current_Day in Monday..Friday loop
	   Go_To_Work;
       end loop;

I agree that Ada handles this more clearly because it has a more
explicit concept of enumerated types.  That the ".." means a range,
though, is not something that's obvious to people who have never seen
Pascal or Ada before; I know this from seeing people learn Pascal.  In
this particular example, it's obvious from context, because everybody
knows that when you see Monday and Friday together, it means "from
Monday to Friday".  If I tell you that the French words for these days
are Lundi and Vendredi, I bet you can guess what "de Lundi a Vendredi"
means.  (I apologize if I got the Francais wrong, but you get the
idea.)  In another example, the ".." might be less obvious.

Anyway, this has probably gotten to the point where it has very
limited relevance to object-oriented programming.  And I certainly
don't intend to be a defender of C, which is hardly a language I have
great love for.  And I don't even know much about Ada.  So I won't
post further on this subject.  Thank you for taking the trouble
to restate your claim so much more clearly.

jja@wsl.ie (John Allen on wsl) (05/13/91)

In article <1991May9.040947.28053@netcom.COM> jls@netcom.COM (Jim Showalter) writes:
>
>    for Current_Day in Monday..Friday loop
>        Go_To_Work;
>    end loop;
>    
>    while Batter_Remains_In_Bowl and People_Are_Still_Hungry loop
>        Make_More_Pancakes;
>    end loop;
>
>    if You_Are_Sleepy then
>        Go_To_Sleep;
>    end if;
>

Similarly find an ADA programmer who definitely cannot understand this.

'C'

    for (Current_Day=Monday; Current_Day <= Friday; Current_Day = Current_Day + 1)
        {
		Go_To_Work();
		}
    
    while (Batter_Remains_In_Bowl && People_Are_Still_Hungry)
        {
		Make_More_Pancakes();
		}

    if (You_Are_Sleepy)
        {
		Go_To_Sleep();
		}


'Pascal'

    for Current_Day:=Monday TO Friday DO
		Go_To_Work;
    
    while Batter_Remains_In_Bowl AND People_Are_Still_Hungry DO
		Make_More_Pancakes;

    if You_Are_Sleepy THEN
		Go_To_Sleep;

-- 
People that don't know want to know from the people that do know and if the 
poeple that do know don't tell the people that don't know then the people
that don't know still won't know.
				   "Don't quote me on any issue whatsoever."