This homework has two types of problems:
Self Check: You are strongly encouraged to think about and work through these questions, but you will not submit answers to them.
Problems: You will turn in answers to these questions.
Ask an AI tool (ChatGPT, Claude, Copilot, etc.) to generate a function in ML that computes the dot product of two integer lists. Compare the result to your own implementation from Lab 6. Note any differences in style, correctness, or use of language features.
The following ML code was generated by an AI tool in response to the prompt "write a function to flatten a nested list in ML."
fun flatten [] = []
| flatten (x::xs) = x @ flatten xs;
What is the type that ML infers for flatten?
A student wants to use this function to flatten
[[1,2],[3],[4,5,6]] into [1,2,3,4,5,6]. Will this work?
Explain.
Another student tries to flatten [1,[2,3],4]. Explain why
this doesn't even type-check in ML, and what this tells us about
the trade-offs between static typing and the kind of "flexible"
code an AI might generate for a dynamically-typed language like
Python.
A student asked an AI to "write Java code to evaluate and print arithmetic expression trees." The AI produced the following:
abstract class Expr {
abstract int eval();
abstract String prettyPrint();
}
class Num extends Expr {
int value;
Num(int v) { this.value = v; }
int eval() { return value; }
String prettyPrint() { return "" + value; }
}
class Add extends Expr {
Expr left, right;
Add(Expr l, Expr r) { left = l; right = r; }
int eval() { return left.eval() + right.eval(); }
String prettyPrint() {
return "(" + left.prettyPrint() + " + " + right.prettyPrint() + ")";
}
}
Is this using the Visitor pattern or the "standard" OOP approach from HW 6?
If you wanted to add a Multiply expression, how many classes
would need to change?
If you wanted to add a compile operation (to generate stack
instructions), how many classes would need to change?
Under what circumstances would you advise the student to refactor to the Visitor pattern instead? Relate your answer to the extensibility trade-offs we discussed in class.
An AI generated the following Scala code for a "copy all elements" function:
def copyAll[T](src: List[T], dest: MutableList[T]): Unit = {
for (elem <- src) {
dest += elem
}
}
A student has a List[ColorPoint] and a MutableList[Point]
and tries to call copyAll(colorPoints, pointList). Will this
compile? Explain why or why not.
Rewrite the signature of copyAll using bounded types so that
the call above would compile. Which bound (upper or lower)
do you need, and on which parameter?
Relate your answer to the Get and Put Principle, which says that when you pass a generic structure to a function:
[_ <: T]) when you only get values
out of the structure;[_ >: T]) when you only put values
into the structure;Is dest being used for "get" or "put"?
Consider the following prompt given to an AI:
"Write a function that removes duplicates from a list."
Give two different reasonable implementations that this prompt could produce --- one that preserves the original order of elements and one that does not.
Write a more precise specification (using types, preconditions, postconditions, or examples) that would disambiguate between these two interpretations.
How does a strong type system help or hinder an AI in producing correct code from ambiguous specifications? Give one example of each.
Submit your answers to the GradeScope assignment named, for example, "HW 0". It should:
You will be asked to resubmit homework not satisfying these requirements. Please select the pages for each question when you submit.