What Is "JJ"?
Sidebar: Name That Programming Language
JJ is a programming language and environment designed for learning Java. Although a subset of Java, JJ includes advanced programming features, such as support for Design by Contract, complete with a class invariant, preconditions, and postconditions. We developed JJ after witnessing the rapid move to Java in introductory computer-science courses. It seemed that if an appropriate compiler existed with educational-based error messages, a subset of Java would more easily help programmers get started with Java. Since we also felt that the ideal educational language would not necessarily look like Java, we settled on a subset that uses a different syntax. After all, even though a C++/C-like syntax was selected for Java (primarily to attract experienced programmers), Java could just as easily have had a syntax similar to Pascal, Eiffel, or Basic. In a nutshell, JJ is based on Java, has some of Eiffel's features, and sports a syntax designed for ease of learning.
One reason behind this approach is that Java has features typically not taught to novice programmers. For example, JJ does not support inheritance, because most introductory programming courses do not get into inheritance. JJ does support classes as a mechanism for data encapsulation, but students should move from JJ to Java when they're ready for inheritance.
Another goal of JJ is to remove features that may lead to poor programming habits. For example, allowing modification of a public class data from another class is prohibited in JJ, as in Eiffel. Another way to describe this restriction is that public data can be seen, but not modified.
The JJ Language Definition
JJ consists of about three dozen commands, each beginning with a reserved word that must be the first word on a line. There is no need for a semicolon to end a command. For each construct, there is a unique End command. There is no need for curly brackets, or the dangling else problem (something found in C, C++, Java, and even Pascal). Mismatched constructs can be reported accurately, which can save beginning programmers hours of debugging.
It may be difficult for experienced programmers to remember how frequently beginners tend to misplace semicolons and curly braces. Even worse, it is truly impossible to predict the resulting error message. The error messages for missing or extra "{," "}," or ";" are confusing because compilers are built for experienced programmers and tend to report on "what was expected" instead of "what is likely wrong." For example, if you place an extra "}" at the end of a Java class, the javac compiler reports, "Class or interface declaration expected." Hopefully, Java beginners will pay attention to the line number and figure out that there is an extra ending "}." Unfortunately for beginners who actually read the error message, the word "interface" makes no sense, and the word "Class" in the error message might suggest changing the (previously correct) word "class" to "Class."
Should you make the edit from "c" to "C" in "Class," the next javac compile reports three errors, with the final one (and the one beginners tend to read first) suggesting that the name of the file should be Class.java. Java beginners who change the name of the file to Class.java may never find and fix the extra character -- the "}" that caused a major change.
Another problem with the overloaded "}" character in C, C++, and Java is the difficulty for compiler writers to accurately discover which "}" is missing. Is it the match for the "class," "function," "for," or the "if"? To report an accurate message using JJ, each construct has the unique End command: Class-EndClass, Routine-EndRoutine, If-EndIf, Repeat-EndRepeat, Constructor-EndConstructor, and Invariant-EndInvariant. Table 1 lists the JJ commands, along with their BNFs. This BNF snippet is, of course, incomplete without the rest of the production rules. The full JJ BNF is at http://jj.caltech.edu/doc/jjbnf.html.
If you have experience with multiple languages, you can probably grasp most of JJ from Table 1. Besides being visually apparent, each JJ command translates directly to one line of Java code. The full JJ language definition consists of the JJ BNF, used as a definition of the JJ syntax, and the JJ-to-Java translation definition, used as a definition of the JJ semantics. The JJ-to-Java translation definition is at http://jj.caltech.edu/doc/jj2java.html.
Initially, students typically focus on simple things such as variables and control structures. JJ's features include declarations (called Boxes), assignment (Set), Input and Output, conditionals (If, ElseIf, Else, EndIf), and loops (Repeat, ExitOn, EndRepeat). If there are no functions and routines, the commands are assumed to be in a "default" routine in a "default" class. Once students have these concepts under their belt, they typically focus on routines and functions. If functions and routines are defined without a class, they are assumed to be in a default class. Finally, new programmers write JJ code that contains classes, routines, and functions.
Example 1 presents three JJ versions of the classic "Hello, World" program. In fact, Example 1(a) might be the shortest "Hello, World" you have ever seen. Initially, the student does not have to see any classes, routines, or functions. Thus, JJ is both a procedural and a class-based language. Instructors can use this to their advantage by introducing only the concepts necessary and sufficient for each new step in learning to program.
Example 1(b) shows an optional middle stage that gives instructors the opportunity to introduce routines and functions without mentioning classes. If initial examples and labs necessitated the declaration of a single (useless) class, novice Java programmers might wonder about the class instruction's purpose. For many beginners, understanding routines and functions can be a large leap from simply listing commands. Not requiring all routines and functions to be in a class goes along with the goal of minimizing the number of times a Java beginner is told to just do something (put everything inside a class) without being given an explanation.
Example 1(c) is a JJ class. A JJ class could have class boxes (class variables/attributes), a class invariant that describes what is true about any object of the class when it is in a stable state, and a constructor to initialize these class boxes and satisfy the invariant. Listing One is a typical class -- a savings bank account. The invariant is optional, but the constructor is required since there is a public box named "identity."
The JJ Online Environment
From the outset, JJ was designed as an online learning environment for Java. That is, the JJ language is small enough that the JJ compiler runs in a browser. There is no need to download and install anything -- you simply get a JJ account, login, and begin programming. (JJ accounts are free for noncommercial educational use at http:// jj.caltech.edu/. Additionally, a guest account for DDJ readers to use at no charge is available at http://www.publicstaticvoidmain .com/ for a limited time. Go to "create account" and use the case-sensitive code of ddjguest.) Students enter JJ code in a panel and press the CheckIt button to compile. If there are no syntax errors, the equivalent line-by-line Java code is shown in an adjacent panel, side-by-side with the JJ code. Then, by pressing the CheckItOut button, the JJ (Java) program begins running as an applet in a new browser.
There are numerous benefits to an online strategy. First, there is no need for students to submit lab solutions as printouts or on diskettes. Instead, they press a button and submit the solution directly to the instructor via e-mail. Another benefit is that all compiled JJ code is stored on the server. JJ compiler developers and testers can review what students tried, the resulting error message, and what the student tried next (and when). JJ students can use a Back and Next button to traverse through their JJ code, including previous sessions (all the way back to their first JJ compile). Likewise, JJ instructors can use the Back button while helping students.
Additionally, JJ compiler error messages can be translated from English into native languages. All the errors have unique error codes that are used as tags in a web page, and instructors can set the URL of this web page. Thus, if instructors want to translate the error page, their students will be able to see errors in their native language.
Figure 1 shows the result of compiling a JJ Tic-Tac-Toe program, and Figure 2 shows the result of pressing the CheckItOut button and running this program. Listing Two is the Tic-Tac-Toe program. A larger version of this program that does not use arrays is at http:// jj.caltech.edu/examples/tictactoenoarrays.jj.
The Tic-Tac-Toe program highlights an advanced JJ feature -- the simplified version of Java's GUI definition (which is also available in Java). Another advanced feature available in the JJ language is support for Design by Contract (DbC), which is not directly supported in Java.
Design by Contract in JJ
How can Design by Contract be introduced in JJ for beginning programmers, and in such a way that is easily transferable to Java? Is it possible that, even though DbC is not always taught in college and appears to be an advanced feature only fully supported in Eiffel, DbC is quite natural if presented as a requirement of classes, routines, and functions?
Design by Contract, a phrase attributed to Bertrand Meyer (designer of Eiffel), is a programming methodology that includes three fundamental concepts (adopted from Hoare logic): preconditions, postconditions, and a class invariant. We derived a few simple language rules that cleared the way for an elegant JJ DbC definition.
Preconditions and Postconditions
A precondition is a requirement that a method (routine or function) has on the values supplied for its slots (parameters). This requirement is checked before the code of the method begins executing. A postcondition is a requirement that a method has on its result. This requirement is checked after the code of the method has completed. In JJ, preconditions are supported by the PreCheck command and postconditions are supported with PostCheck.
For example, assuming the routine "credit" expects a nonnegative amount, Example 2(a) shows how a PreCheck command is used to verify the value supplied for the slot named "amount." If the caller supplies a negative value, the author obviously did not understand (or check) the requirement. Essentially, the contract between the credit routine and the caller of the credit routine has been broken. If this happens, in DbC, "all bets are off." For JJ, "all bets are off" means (language rule #1) the error message is reported (along with the class name, method name, and line number) and the program halts.
The postcondition is slightly different. If a PostCheck bounces, the method did not produce the expected answer. The result is the same -- a contract is broken. In this case, the broken contract is localized to the author of the method. Recall that if a PreCheck bounces, some user of the class was not careful. This is an important distinction: A PostCheck should not bounce in a polished, published class, but it is likely that a PreCheck could bounce as a result of careless programming.
These concepts are valuable for beginners to grasp. They help teach new programmers the importance of precision for correctness and code reuse. JJ does not require the use of DbC, but instructors who like to emphasize the reusability of code (classes) can emphasize the responsibilities of specifying exactly what a method expects and is willing to return.
How are preconditions and postconditions implemented? In JJ, PreCheck and PostCheck commands are simply translated into if statements, and the bounce message is passed to a halt routine. For example, the PreCheck in Example 2(b) is translated to Java as Example 2(c).
If you are wondering about the value of DbC preconditions and postconditions compared to adding a few if statements, the idea is to accurately communicate a method's interaction with the caller. Unlike if statements, preconditions and postconditions are part of the public definition (interface) of a method. It is difficult for a tool to extract this public signature of a method if it has to automatically examine code to find the appropriate if statements. The preconditions and postconditions, however, are easy to locate and parse.
Another difference between pre- and postconditions and if statements is that the pre- and postconditions may or may not be compiled or executed at all. In other words, pre- and postconditions can be left on during testing, but turned off when code is deployed. This feature is usually a compile option. Typically, after testing, all postconditions are disabled. Leaving preconditions on is a good idea while integrating code, but should efficiency be an issue, they could be turned off after integration testing. Some would argue, on the other hand, that postconditions should also be left on.
Class Invariant
DbC is not complete without the contract for the class -- the class invariant. It is indeed valuable to have contracts on methods, but claiming support for DbC without a class invariant is like asking only two out of three passengers to fasten their seat belts.
Our definition of a JJ class invariant is "a set of rules that hold when an object is in a stable state." By "stable state" we mean (language rule #2) "immediately after the creation of the object (the call to the constructor) and when returning from any call from a public method (routine or function) of that object."
The class invariant is a list of requirements on the data in a class. Any instance of the class must meet these requirements except during a call to one of its public methods. A public method, while running, could change data in such a way that the invariant does not hold, but it has to restore the class invariant before completing.
The class invariant, like preconditions and postconditions, is part of a public description of a class. Users of a class benefit from knowing the requirements of the public entities of the class. For example, the author of a Person class at a phone company can communicate to the users of the class detailed information about a Person with a class invariant. If a Person consists of a firstName, lastName, and phoneNumber, the class invariant could, for example, specify one requirement -- every Person has a lastName (for example, the lastName is not null or an empty string).
At first, this class invariant may not appear valuable. But notice that the class invariant does not specify anything about the firstName attribute. The conclusion is that a Person must have a last name, but is not required to have a first name. The author of the Person class accepts "Pele," without requiring a first name. There is a similar conclusion with the phone number. Say that "Pele" has moved, and no longer has a phone number. Allowing a phone number to be null means that customers remain part of the program, possibly because they are expected to eventually get a new phone number.
Without a class invariant, this sort of information would have to be supplied in documentation. Documentation is notoriously hard to validate. A class invariant, however, is executable code and thus must also be kept up-to-date. As with the precondition and postcondition, the class invariant could be turned off should efficiency be an issue. Unlike preconditions and postconditions, which could be implemented with if statements, the class invariant does not have an easy implementation -- or does it?
Considering that the class invariant must hold after the constructor and after any public methods, it appears that simply calling the invariant as the last statement in the constructor and in any public methods obtains the goal. Unfortunately, the class invariant is a list of requirements, which means conditions, which means a potential for function calls. Might there be an infinite recursion problem should a condition in the class invariant reference a public function? Does this problem not also exist in the use of pre- and postconditions?
A second problem occurs when a public method modifies data, temporarily breaking the class invariant, with every intention of setting it back, and then references another method in the same class. Should the calling method have to reset the data to satisfy the class invariant before referencing another method? Does it matter if the other method is public or private? What happens if the method references a public method of another class that happens to (indirect) recursively call back?
Computer scientists have more than one answer to these questions, depending upon the context of the problem. It does seem unreasonable to require that a method satisfy the class invariant before referencing other methods, but this will break the simplistic implementation mentioned earlier, that of calling the class invariant at the end of every public method.
After giving you the key to supporting a class invariant in JJ, it may appear obvious and trivial.
- Functions must be pure; that is, free of side effects (language rule #3).
- Private methods cannot call public methods in the same class (language rule #4).
Pure Functions
Functions that are "free of side effects" are essentially functions that compute a result based on supplied values, if any. A function cannot change class data. Simple tests exist to determine if a function is free of side effects. For example, if the function foo() returns an int, the expression foo()+foo() must have the same effect as 2*foo(). A common phrase used to support this notion of a function is "Asking the questions should not change the answer."
One controversial issue with this rule is output. Some programmers use output statements for debugging, and this would break the aforementioned 2*foo() rule, since the output is part of the program's state. In JJ, a function cannot contain any Output and Outputln commands, but a function can contain Debug and Debugln commands that write to the Java console instead of to the output panel. The debug channel is, by definition, not part of the program's state and is thus writable.
How does requiring pure functions help support DbC in JJ? Recall that a problem for DbC language designers is handling a call to a function in the expression of a DbC precondition, postcondition, or class invariant. There are no recursion problems with pure functions. Thus, because functions cannot have side effects, there is no reason to verify the class invariant at the end of a public function.
Is it too restrictive to require functions to be free of side effects? That depends on the definition of side effects. For JJ, an educational language, modifying class data is considered a side effect. For Eiffel, modifying class data is allowed as long as the value is returned before the function completes. The Eiffel definition is, of course, extremely difficult for a compiler to check.
What about functions that return objects, something other than an int, real, bool, or Str? Creating the object requires a call to the constructor for that object. Calling a routine is not allowed in a function (since routines are supposed to have side effects). Is calling a constructor a side effect? Our conclusion is that calling a constructor is not a problem in JJ as (language rule #5) constructors are only allowed to initialize class data.
Privates Cannot Call Publics
Finally, (language rule #6) a private method of a class is not allowed to call a public method in that same class. Because of this rule, a public method does not have to satisfy the class invariant until it ends. This nicely solves another recursion problem mentioned earlier for DbC language designers: Public methods can call private methods without concern that there is an indirect recursive call back.
There are pedagogical reasons for this rule regardless of DbC considerations. A student first learning about methods may simply consider them as "common blocks of code." The influence of this rule hopefully puts the focus on determining the primary features of a class and writing the appropriate public methods. Should these public methods require utilities that do not belong as features of the class, these utilities become private methods.
Acknowledgment
Thanks to Bertrand Meyer for Eiffel, for Object-Oriented Software Construction, Second Edition (Prentice Hall, 1998), and for a brief review of JJ's DbC definition.
DDJ
Listing One
Import JJIO Class Account -- Name Ann Onymous -- Does provide a simple savings bank account -- Shows much of the programming language JJ -- Shows PbC, Programming by Contract -- Data attributes, fields Box balance ofType real is private Box identity ofClass Str is public Invariant Check (balance >= 0.00) bounce "Negative balance!" Check (identity != null) bounce "Null id!" EndInvariant Constructor Account (b, i) is public Slot b ofType real -- account balance Slot i ofClass Str -- identification -- Does initialize or open the account PreCheck (b >= 0.00) bounce "Negative initial balance!" PreCheck (i != null) bounce "Null initial identity!" Set balance = b Set identity = i EndConstructor Account Routine setBal (amount) is public Slot amount ofType real -- Does set or reset the amount of balance PreCheck (amount >= 0.00) bounce "Setting negative balance!" Set balance = amount EndRoutine setBal Function getBal (none) ofType real is public Box result ofType real -- Does return balance in account Set result = balance EndFunction getBal Routine credit (amount) is public Slot amount ofType real -- Does deposit an amount into account PreCheck (amount >= 0.00) bounce "Negative deposit!" Set balance = balance + amount EndRoutine credit Routine debit (amount) is public Slot amount ofType real -- Does withdraw an amount from account PreCheck (amount >= 0.00) bounce "Negative debit!" PreCheck (amount <= balance) bounce "Insufficient balance!" Set balance = balance - amount EndRoutine debit Function canCover (amount) ofType bool is public Slot amount ofType real Box result ofType bool -- Does check if balance covers amount Set result = (balance >= amount) EndFunction canCover Routine compound (percent, duration) is public Slot percent ofType real -- percent rate Slot duration ofType int -- time duration Box rate ofType real -- interest rate -- Does compound balance at a rate for a time PreCheck (duration >= 1) bounce "Negative duration!" Set rate = percent / 100.0 Repeat ExitOn (duration == 0) Set balance = balance + balance * rate Dec duration by 1 EndRepeat EndRoutine compound Routine getFrom (account, amount) is public Slot account ofClass Account Slot amount ofType real -- Does transfer from one account to this PreCheck (account.balance >= amount) bounce "Insufficient balance!" Call account.debit with (amount) Call credit with (amount) EndRoutine getFrom Routine show (none) is public -- Does display the account Output "Account " + identity + " has balance " Outputln balance EndRoutine show Routine test (none) is private Box his ofClass Account Box her ofClass Account Box amount ofType real -- Does test Account class Start New his ofClass Account with (100.0, "Dad") New her ofClass Account with ( 0.0, "Mom") Call his.credit with (200.00) Call his.compound with (10.0, 8) Call his.show Output "Enter her deposit " Input amount Outputln amount -- echo Call her.setBal with (amount) If his.canCover (600.00) then Call her.getFrom with (his, 600.00) Else Outputln "No transfer possible" EndIf Call her.debit with (300.00) Output "Her balance is " Outputln her.getBal() EndRoutine test EndClass Account
Listing Two
Import JJGui Class TicTacToe --Name: ?? (replace '??' to match your login name) Box buttons ofClass JJButton[] is private Box ta ofClass JJTextArea is private Box gp ofClass JJGridPanel is private Box bp ofClass JJBorderPanel is private Box whoseTurn ofClass Str is private Constructor TicTacToe(none) is public Box i ofType int --allocate an array and loop thru New buttons NewArray buttons ofClass JJButton[9] Set i = 0 Repeat New buttons[i] ofClass JJButton with ("") Inc i by 1 ExitOn (i == 9) EndRepeat New ta ofClass JJTextArea with ("X's turn: To Start, click a button") New gp ofClass JJGridPanel with (3,3) New bp ofClass JJBorderPanel Set whoseTurn = "X" EndConstructor TicTacToe Routine ActsAsMain(none) is public Box i ofType int Start -- tells JJ that this is the "main" --loop thru adding buttons to panel Set i = 0 Repeat Call gp.add with (buttons[i]) Inc i by 1 ExitOn (i == 9) EndRepeat Call bp.add with ("Center", gp) Call bp.add with ("South", ta) -- The next call is needed exactly once. Call bp.jjShowAsMainPanel EndRoutine ActsAsMain -- when a button is pressed, jjHandleButtonPress is -- automatically called with the button that was pressed. Routine jjHandleButtonPress(b) is public Slot b ofClass JJButton Box ok ofType bool Box i ofType int Box buttonLabel ofClass Str Box bCurr ofClass JJButton Set ok = false --loop thru buttons until finding the pressed one Set i = 0 Repeat Set bCurr = buttons[i] If (b == bCurr) then Set buttonLabel = bCurr.getLabel() If (buttonLabel.length() == 0) then Set ok = true Call bCurr.setLabel with (whoseTurn) Else Call ta.setText with ("*** ERROR *** Button already pressed") EndIf EndIf Inc i by 1 ExitOn (i == 9) EndRepeat If (ok) then If whoseTurn.equals("X") then Set whoseTurn = "O" Call ta.setText with ("O's next: Please click a button") Else Set whoseTurn = "X" Call ta.setText with ("X's next: Please click a button") EndIf EndIf EndRoutine jjHandleButtonPress EndClass TicTacToe