It's Just Syntax |
|
|
|
|
I feel a little guilty about all of the Lisp code in the last entry.
I talked a lot about the benefits of object-oriented programming but I
showed you hardly a scrap of code from a language specifically
designed to support object-oriented programming such as Java or C++.
I know it's just syntax but I also know from experience that
struggling with unfamiliar syntax can be frustrating - hence my
reluctance to code in Java or C++ simply because I'm less familiar
with the syntax. Trading parentheses for curly brackets and
semicolons and different formatting and commenting conventions and
different names for often-used functionality is more than a little
disorienting and some times important concepts can get lost in
translation. I have so say in this case that the PLT Scheme
class.ss library did a very good job of capturing the
most important features of object-oriented programming by mapping
object-oriented-programming concepts directly onto appropriate Scheme
syntax.
Syntactic sugar refers to features added to a language that make it more palatable for humans but that don't affect the ability of the language to specify computations (such features neither increase nor decrease the expressiveness of the language). Syntactic sugar is meant to encourage programmers to write better code by hiding features that are seldom useful and might distract someone trying to make sense of the code or features that are difficult to use and can lead to bugs. Contrast syntactic sugar with syntactic salt which makes it more difficult to write good code and syntactic saccharin (also called syntactic syrup) which introduces gratuitous syntax that serves no useful purpose. Some programming language mavens believe that syntactic sugar can itself be distracting or that the need for it in a programming language provides evidence that the language was poorly designed ("Syntactic sugar causes cancer of the semicolon" from Alan Perlis's Epigrams in Programming). A programmer addicted to syntactic sugar is less likely to understand or use the features that the sugar sweetens by masking.
I mention syntactic sugar in preparation for my defense of introducing
object-oriented concepts using Scheme (I have an annoying tendency to
become defensive in anticipation of controversy even when the
prospects for controversy are remote). The PLT Scheme implementation
of classes does abstract away some important features of Java and C++,
specifically the requirement of declaring the types for all variables,
constants and return values of functions. But these features are not
pertinent to object-oriented programming per se and individual
preferences regarding parentheses versus curly brackets are similarly
beside the point. Therefore it's my feeling that any sugaring in the
notation introduced in class.ss is of the good sort.
However, I'm also sensitive to the criticism that syntactic sugar
constitutes more syntax and too much even of a good thing can
rot your stomach.
I've spent all this time justifying my approach and I'm still feeling
guilty and so I feel compelled to show you some "real" object-oriented
programming syntax. I spent an hour this morning translating the
classes that I designed using the class.ss library into
Java classes. I was pleased with how easily the translation went and
how directly the abstractions in class.ss mapped onto
standard Java abstractions. Showing off the code will require that I
use some tools from the Sun Java Development Kit (JDK) but we've never
shied from introducing new tools and inscrutable (and all too briefly
explained) notation in the past so I don't know why we should start
now. Also we'll be using a somewhat different style of developing and
executing programs than we're used to in interacting with a Lisp
interpreter.
To implement the necessary classes and demonstrate their use, I
created three files: one file for each of the Date and
JournalDate classes and one file for a class
called TestDate to demonstrate that the code actually works.
TestDate doesn't have any associated instances and exists
only so that I can call its main procedure to build some instances
of the Date and JournalDate classes and apply
methods to these instances to demonstrate that the classes work as
advertised.
% ls Date.java JournalDate.java TestDate.java
Here's what the code for the DateInterface interface and
Date class look like. Again we've kept it simple for
illustration purposes; the Java code implements the same behaviors as
the Scheme code. In Java Object is the base class. I've
added some navigation so you can jump back and forth between the
Scheme and Java implementations. I use the Unix cat
program to print the Date.java file to my terminal from
the shell. I won't explain the Java syntax in any detail except to
note that the DateInterface describes the interface that
the Date class implements and the Date
class defines four methods, year, month,
day and before, in addition to explicitly
defining the Date instantiator.
% cat Date.java
interface DateInterface {
public int year ();
public int month ();
public int day ();
public boolean before ( Date that );
}
class Date extends Object implements DateInterface {
protected int year, month, day;
public int year () { return year; }
public int month () { return month; }
public int day () { return day; }
public boolean before (Date that) {
if ( year > that.year ) return false ;
else if ( year < that.year ) return true ;
else if ( month > that.month ) return false ;
else if ( month < that.month ) return true ;
else return ( day > that.day ) ;
}
public Date (int y, int m, int d) {
year = y; month = m; day = d;
}
}
Next we have to compile the above code using javac, the
Java compiler, which converts the Java source code, e.g., the contents
of the Date.java file, into Java bytecodes. Most compilers produce
assembly code which is then converted by an assembler into machine
code to run on a particular machine. The Java compiler produces
bytecodes which constitute the machine code for the Java Virtual Machine (JVM). Java
bytecodes run on any machine that has an implementation of the JVM.
The JVM is a piece of software that has been ported to run on lots of
different machines and that serves to simulate a virtual or software
machine. This isn't much different from how the Scheme interpreter, a
piece of software that has to be written and compiled to run on a
particular machine, executes Scheme code. There are differences
between Scheme code and Java bytecodes but not enough to loose any
sleep over. I suppose it can be argued that Java bytecodes will run
in the JVM faster than (uncompiled) Scheme code will run in the Scheme
interpreter but there are so many nuances that it isn't worth worrying
too much about. Much ado about nothing as far as I'm concerned. Let's
invoke the compiler.
% javac Date.java
I won't bother to do a listing of the directory (ls) but
now there is a file called Date.class in the directory
along with Date.java. Date.class contains
the compiled version of the Date class. Now I'll show
you the file implementing the JournalDate.java class.
Note that it begins by declaring its dependence on the
Date class and then proceeds to extend
DateInterface before getting around to describing the
JournalDate class. Here as in the Scheme version I
invoke the instantiator, called super in Java, for
the superclass, Date.
% cat JournalDate.java
import Date;
interface JournalDateInterface extends DateInterface {
public void iso ( );
}
class JournalDate extends Date implements JournalDateInterface {
protected String yearString, monthString, dayString ;
public void iso () {
System.out.println( yearString + "-" +
monthString + "-" +
dayString );
}
public JournalDate (String y, String m, String d) {
super( Integer.parseInt( y ),
Integer.parseInt( m ),
Integer.parseInt( d ) );
yearString = y;
monthString = m;
dayString = d;
}
}
Compile the code in JournalDate.java.
% javac JournalDate.java
TestDate.java contains the description of a class,
TestDate, whose only purpose for being is to run its
main method to exercise the code in
Date.java and JournalDate.java. It's not
very fancy; it just creates an instance of JournalDate
and then invokes the iso method on the instance.
% cat TestDate.java
import JournalDate;
class TestDate {
public static void main(String[] args) {
JournalDate today = new JournalDate ( "2002", "08", "28" );
today.iso ();
}
}
It has to be compiled just like all the other classes.
% javac TestDate.java
Now we list the contents of the directory and notice compiled versions for all of the classes and interfaces.
% ls Date.class JournalDate.java Date.java JournalDateInterface.class DateInterface.class TestDate.java JournalDate.class TestDate.class
In order to run the program that we just compiled we use Java
interpreter which called simply java.
% java TestDate 2002-08-28
Not very interesting output but at least it appears that the
iso method defined in the JournalDate class
works according to our expectations. To give you a little more of an
idea of how objects are used in Java code, note that if in defining a
procedure I was to declare and instantiate two
JournalDate objects as follows:
JournalDate today = new JournalDate ( "2002", "08", "28" );
JournalDate yesterday = new JournalDate ( "2002", "08", "27" );
Then, later in same procedure, if I were to write
today.before(yesterday) and
yesterday.before(today), then these statements would
evaluate to false and true, respectively, thereby exercising the
(inherited) methods in the superclass Date of the
JournalDate class.
So that's about it: inheritance, interfaces, public and private methods; all good stuff from what I hear tell. But remember that a major part of what makes it all work is in your head, the discipline and good sense you bring to designing good code. No amount of fancy syntax, sweet, sour, salty or otherwise, is going to turn you into a good programmer if you don't have discipline and common sense in good measure. Java is a nicely designed language and I wouldn't be surprised if it was now my favorite language had it been the first language that I did any serious programming in. But it wasn't and it isn't and lucky for me my favorite language is alive and well, thankyou very much.
One of my students, Luke, on reading parts of the first installments of this journal remarked that he felt that each entry was like the forward to a textbook but less formal. In a forward, the author tries to convey his or her excitement and enthusiasm for the subject matter of the book, but, in the case of technical material, eventually has to get down to brass tacks and explain the details which can be, if not handled very skillfully, a little tedious. I get to skip blithely from subject to subject leaving the reader to track down the details or chose not to. I like Luke's characterization; it does a good job of capturing some of what I'm up to - the rest, I'm not sure of myself.