For this assignment, you should work alone, although you should feel free to discuss the problems with other students in the class.
Remember to follow the pledge you read and signed at the beginning of the semester. For this assignment, you may consult any outside resources, including books, papers, web sites and people, you wish except for materials from previous cs1120, cs150, and cs200 courses. If you use resources other than the class materials, lectures and course staff, explain what you used in a comment at the top of your submission or next to the relevant question.
As always, you are strongly encouraged to take advantage of the scheduled help hours and office hours for this course.
The main purpose of this assignment is to give students enough experience programming in Java to be well prepared to take cs2110 in the Spring. Part 1 (due Tuesday, 22 November) introduces Java with some exercises and then asks you to extend the interpreter to support mutation. Part 2 (due Monday, 5 December) extends the interpreter to include manifest types and static type checking. In addition to providing experience with Java and more experience with interpreters, this assignment should give you a good understanding of how static type checking works and why it is useful.
The most important difference between Java and Python (or Scheme) is that Java uses static type checking. All of the language features we have seen so far were designed to increase the expressiveness of a language: although the small initial subset of Scheme we saw in Class 2 is sufficient to express every possible program, adding lists, begin, let, and mutation, to the language makes it easier to express certain programs concisely. Static type checking serves the opposite purpose: it makes a language less expressive.
There are two reasons we might want to reduce the expressiveness of a programming language. The first one is that sometimes it can make the language implementation simpler and more efficient. For example, the memoizing Charme from Problem Set 7 could only be done because Charme did not provide support for mutation and any side effects. Otherwise, it would not be safe to assume the result of applying the same function to the same inputs will always produce the same output.
The other reason for reducing expressiveness is that it can contribute to the goal of preventing programmers from expressing programs that will crash or produce unexpected results when they are executed. Such languages sacrifice expressiveness for the sake of truthiness — increasing the likelihood that a program means what its programmer intends.
A high level of truthiness is important when software is used to control a physical device whose correct and continued function is essential to safety such as software controlling a nuclear power plant (note, however, that Java's license agreement disallows its use for this purpose!), anti-lock brakes, or aircraft avionics. In such cases, it is much better to reduce the expressiveness of your programming language and get more errors when the software is developed, than to get unexpected behaviors when the software is running.
For this assignment, you will modify a provided Java implementation of an interpreter for a language similar to Charme from Problem Set 7 to provide static type checking. The new language is called Aazda, after a kind of Python found on the island of Java.
The tool we will use to create Java programs is the Eclipse Integrated Development Environment (IDE). Eclipse is similar to DrRacket (for Scheme) and IDLE (Python). It provides an editor for editing Java code. Unlike, Scheme and Python, though, the Java programming language is implemented using a compiler, not an interpreter. This means instead of being able to directly evaluate expressions in an interactions window, we need to first compile our program into an executable, and then run that executable.
Eclipse is a huge and extensible platform, with support for lots of different languages and tools. This makes it difficult to get started with, but once you get used to it, it is a powerful development environment.
Download the Eclipse IDE for Java Developers. It is available for Mac OS X, Windows, and Linux (as well as source code to build for other platforms). The download is a zip file containing an eclipse directory. There is nothing to "install", just extract this directory to wherever you want (for example, your Program Files directory in Windows). The program eclipse.exe (Windows) in this directory is the main Eclipse executable. Double-click on the eclipse logo
to start running Eclipse.
The first time you run Eclipse, it will ask you to select a workspace. Create a new directory in your cs1120 directory, and select this as your workspace. (If you check the Use this as the default box, it won't ask you again.) Then, you'll see a welcome screen. It includes Samples and Tutorials, or you can click "Workbench" (the arrow icon) to get started right away.
To start a new project, select File | New | Java Project. Enter the name of the project and select Finish (you probably don't need to change any of the other options).
For the first part of this assignment, you will create a new Java Project in Eclipse and get familiar with Java and Eclipse by making and running a simple Java program. This part of the assignment is not directly related to the second part of the assignment where you modify the Aazda interpreter.
Create a new project in Eclipse by selecting File | New | Java Project. Enter Warmup for the project name, and click Finish.
Then, select File | New | Class to create a class for your project. All code in a Java program must be defined in a class. In the Class dialog, enter warmup for the Package name and Warmup for the class name, and check the box for "Which method stubs would you like to create?" for public static void main(String[] arg). Click Finish to create the class.
You should now see a file Warmup.java in your editor window:
package warmup;
public class Warmup {
/**
* @param args
*/
public static void main(String[] args) {
// TODO Auto-generated method stub
}
}
The main method which was created as a stub is the method that will be called when the program is executed. So, you can experiment with Java by adding code to the main method. The declaration includes:
// TODO Auto-generated method stubline and replace it with your code.
You can try running your program (which won't do anything yet) by selecting Run | Run (or Ctrl-F11). The first time you do this you should see a Run As dialog. Select Java Application (the other choice, Java Applet, is used for Java programs that run inside web browsers). Then, you will see a dialog showing a list of the classes that provide static main methods. Select Warmup (which should be the only class listed there).
When it runs, you should see a Console window at the bottom of your workbench. You won't see anything there yet, since the main method is empty, but as you modify the code for the exercises will see the output there when your program runs.
public void println(String x)
provided by the java.io.PrintStream class. Note that unlike main above, this method is not declared using static! That means, it must be invoked on an Object. Try using just, println("Hello"); in Eclipse to see what happens.
There is a built-in PrintStream object that corresponds to the visible stream the user sees. It is called System.out. So, to print a message to the output console, you need to do something like:
System.out.println("Printing in Java is much harder than in Scheme or Python!");
Try this yourself first, but if you are stuck use the hints below.
Declaration of factorial method:
public static int factorial(int n) {
// YOUR CODE HERE
}
Test code in main:
public static void main(String[] args) {
for (int i = 1; i < 20; i++) {
System.out.println("factorial(" + i + ") = " + factorial(i));
}
}
Unlike in Scheme and Python where numbers are represented in a complex way to be able to hold very large values, in Java the int type is a fixed 32-bit value. It can only hold a fixed range of values (from -2147483648 to 2147483647). If the result exceeds 2147483648, Java just wraps around to the negative numbers without producing any error. So, the value of 2147483647 + 1 in Java is -2147483648.
Java provides several different ways for managing collections, but nothing that is very similar to Scheme or Python lists. The closest thing in Java to a mutable list is the ArrayList<E> class. To use the ArrayList class, you need to include:
import java.util.ArrayList;near the beginning of your Java file (if there is a package statement, it needs to be after that).
Because of explicit static typing in Java, we cannot have lists where the elements can be any type, as we do in Scheme and Java. Instead, we need to explicitly declare the type of each list element as a type parameter to the ArrayList. For example, ArrayList<String> denotes a list where each element is a String. The most general list is ArrayList<Object> where each element is an Object. All object classes in Java are subtypes of Object, so this is a list that can contain elements of any object type. But, not everything in Java is an object (for example, an int number is not an object), so the list elements still cannot be any value.
The ArrayList class provides a constructor for creating an empty list:
ArrayList<String> slist = new ArrayList<String>();
The add(Element Type) method appends to an ArrayList:
slist.add("first");
slist.add("second");
slist.add(3); // Compile-time type error: only String elements may be added to an ArrayList<String>
The get(int index) method is similar to slist[index] in Python:
String first = slist.get(0); // gets first element of list
String second = slist.get(1);
String missing = slist.get(3); // Run-time error: no element at index 3
The set(int index, element) method is similar slist[index] = element in Python:
slist.set(0, "primul");
slist.set(1, "ikinci");
slist.set(2, "terceiro"); // Run-time error: can only use set to update existing elements
The size() method returns the number of elements in the list:
int elements = slist.size();
ArrayList<Object> a = new ArrayList<Object>();
a.add("one");
a.add("two");
a.add("three");
listReverse(a);
System.out.println("After a = " + a);
Note that we can put a String object in our ArrayList<Object> since String is a subtype of Object. If S is a subtype of T we can use a value of type S anywhere a value of type T is expected.
For the rest of this assignment, you will modify the Aazda interpreter implemenetation we provide to implement some additional functionality and add static type checking. For Part 1 (due on Tuesday, 22 November), you modify the interpreter to add support for mutation (including the begin special form). For Part 2 (due on Monday, 5 December, the last day of class), you modify the interpreter to add static type checking.
/* Modified for Question [N] */ your changed code /* End Modifications for Question [N]*/
To start withing with Aazda, select File | Import in Eclipse. In the import dialog, select General | Existing Projects into Workspace. Then, Select Archive File and use Browse to find the ps8.zip file. Then, select Finish to import the project. After this, you should see the project aazda in your workspace.
You can try running the aazda interpreter by selecting the aazda project and Run | Run (or Ctrl-F11). The first time you do this you should see a Run As dialog. Select Java Application (the other choice, Java Applet, is used for Java programs that run inside web browsers). Then, you will see a dialog showing a list of the classes that provide static main methods. Select REPL for the read-eval-print loop class.
When it runs, you should see a Console window at the bottom of your workbench. The Hiss> prompt is for entering Aazda expressions or definitions to evaluate. Try evaluating a few expressions. You should be able to evaluate anything that you could evaluate in the Charme interpreter from PS7 (although the Aazda interpreter does not implement memoizing, so don't expect to be able to evaluate (fibo 60).)
The code is divided into separate .java files for each class. Expand the aazda project, src directory, and aazda package to see the Java files. The provided files include:
All of the changes you need to make for Problems 5 and 6 should be in Evaluator.java and Environment.java.
First, add set! to the Aazda interpreter. Note the set! must be a special form since the first operand is not evaluated normally: instead of obtaining its value, we need to use it to identify the place to update. It would be a good idea to make sure you understand how definitions are evaluated before implementing set!.
The Environment class uses HashMap<String, SVal> to represent a frame. A HashMap is similar to a Python dictionary (except that the key and value types must be specified explicitly). The put(key, value) method provides a way to add or update the value associated with a key in the HashMap.
Hiss> (define a 3) Hiss> (set! a (+ a 1)) Hiss> a 4Also, make sure you assignment still works when the variable is defined in a parent environment instead of the current execution environment:
Hiss> (define update-a (lambda () (set! a (+ a 1)))) Hiss> (update-a) Hiss> a 5
Hiss> (define counter 0) Hiss> (define update-counter! (lambda () (begin (set! counter (+ 1 counter)) counter))) Hiss> (update-counter!) 1 Hiss> (update-counter!) 2
You must be logged in to post a comment.
Professor Evans,
I finished the problem set and was trying to submit. However, when I clicked the submission link it was broken. It returned the error “URL not found on server”.
Sorry, it wasn’t up yet! It is up now. There are no test cases for this assignment, but use the form at https://church.cs.virginia.edu/cs1120/ps8j-part1.html to submit your code.
I’m sorry, I don’t really understand…So do we need to include all the code that we wrote for Warmup.java into the code for Evaluator.java?
No, this is a mistake (I think you mean the part where it says problems 2 and 3 should modify Evaluator.java and Environment.java). You should keep Warmup.java as it is, and just submit that as a separate file. Sorry for the confusion.