Learning Objectives
With this assignment you will demonstrate:
- The ability to write and test functions and user-defined types in Java, Kotlin, and Swift. (I will supply the unit tests for you, but you will need to understand them and wire them into to your application.)
- The ability to apply concepts such as abstraction, objects, encapsulation, information hiding, inheritance, dynamic polymorphism, streams, abstract types, records, and sealed classes.
Readings and Videos
Please:
- Read Chapters 7, 8, and 14 of Programming Language Explorations, 2nd edition.
- Locate and have handy reference material for Java, Kotlin, and Swift. I recommend dev.java, the official online docs for Kotlin, and the Swift Book.
- Review the course notes for Java, Swift, Object Orientation, Classes, and Modules.
Instructions
Work in teams of 1 to 4 students.
Do all work in the GitHub repository you created in the previous assignment.
Remember: There must be only one official repository per group.
Repository
Add files according to the folder structure below. You will be implementing the assigned functions and classes from scratch. The test files have been written for you; follow the links to obtain them.
.
├── code/
│ ├── java/
│ │ ├── Exercises.java
│ │ └── ExercisesTest.java
│ ├── kotlin/
│ │ ├── Exercises.kt
│ │ └── ExercisesTest.kt
│ ├── swift/
│ │ ├── exercises.swift
│ │ └── main.swift
Code
Implement—for Java, Kotlin, and Swift—modules with the following functions and types.
- A function that when given an array of strings and a predicate on a string, returns the lowercased version of the first element of the array that satisfies the predicate. You must return an optional since there may be no element satisfying the predicate. (Note that each language specifies optionals in slightly different ways: as always, follow best practices.) In each language, your language must demonstrate parametric polymorphism.
- Java requirement: You must use streams.
- Kotlin requirement: Use the
?. operator.
- Swift requirements: Use argument labels
of for the array and satisfying for the predicate. Also, use the ?. operator.
- A “function” that joins the arguments of successive “calls” into a string each separated by a single space. Unlike previous assignments that perform this operation through currying, work with objects that expose a method (
and) for “adding” a word to the chain and a read-only property (phrase) that returns the accumulated string so far. Please see the accompanying unit tests for examples of how this should work for each language. Roughly you will have:
say("Hello").and("my").and("name").and("is").and("Colette").phrase
"Hello my name is Colette"
say("hello").phrase
"hello"
say("🐤🦇").and("$🦊👏🏽").and("!").phrase
"🐤🦇 $🦊👏🏽 !"
- A function that accepts a filename and return the number of text lines in the file that are neither (1) empty, nor (2) made up entirely of whitespace, nor (3) whose first nonwhitespace character is
#. For Java and Kotlin, propagate any IOExceptions that may occur, such as would occur if there were no file with the given file name. For Swift, instead of throwing, return a suitable Result object, in accordance with the supplied unit tests.
- Java requirement: Use a
BufferedReader and a try-with-resources block. Process the file using the lines() method to produce a stream.
- Kotlin requirement: Use the
use function on a BufferedReader to properly manage your file resource.
- Swift requirement: Use
for try await to read the file. Your function will be asynchronous.
As always, be sure that to close any file that you open! If the language has a mechanism to “auto-close” a file, use it. If not, do your best to make sure the close operation cannot be bypassed.
- A user-defined datatype (Java record, a Kotlin data class, Swift struct) for Quaternions. All objects created of these types should be completely immutable and all internal details hidden. In addition to a constructor, support addition, multiplication, getting an array of coefficients, getting the conjugate of a quaternion, a mechanism for value-based equality (which you should get for free if you do things in accordance with best practices), and a function to return the string representation (in accordance with language conventions). Also include static constant members,
ZERO, I, J, and K, whose meanings should be self-explanatory. If your language allows it, implement addition and multiplication with operator overloading. As always, see the unit tests for details.
Note: we can’t really provide unit tests to ensure immutability, since Java, Kotlin, and Swift detect attempts to mutate such objects as compile time errors. Therefore, the teaching staff will be inspecting the code to ensure your implementation does produce immutable objects.
- A generic, persistent, binary search tree of strings, supporting an element count, insertion and lookup (no deletion). The trees are to be completely immutable and persistent. You must use a sealed class or interface for Java and Kotlin, and you must implement your tree with an indirect enum in Swift. In addition, use the language’s convention for string formatting to generate string representations of your trees. As always, the unit tests are your guide for implementation.
var t = (new Empty()).insert("B").insert("A").insert("C")
t.toString()
"((A)B(C))"
t.contains("A")
true
t.size()
3

var t = BinarySearchTree.Empty.insert("B").insert("A").insert("C")
"$t"
"((A)B(C))"
t.contains("A")
true
t.size()
3

var t = BinarySearchTree.empty.insert("B").insert("A").insert("C")
"\(t)"
"((A)B(C))"
t.contains("A")
true
t.size
3

- Java requirement:
BinarySearchTree must be a sealed interface with the two implementations not nested, but rather top-level classes.
- Kotlin requirement:
BinarySearchTree must be a sealed interface with the two implementations nested inside. The empty “variant” should be an object, while the node variant should be a data class.
- Swift requirement: Use an indirect enum for the type. Make
size a computed property.
Strive to use the most idiomatic constructs of each language in your solutions.
Generative AI is permitted for this assignment; however, there is no guarantee that it will produce code that meets all of the requirements. Do not try to prompt your way to working solutions. Make sure to thoroughly review and test any code produced by AI tools.
To use the supplied unit tests, make sure you’ve created the test-data folder and its associated files as described in the instructions for Homework 2. Run the tests in their corresponding code folders, with the following commands:
javac *.java && java ExercisesTest
kotlinc *.kt -include-runtime -d test.jar && java -jar test.jar
swiftc -o main exercises.swift main.swift && ./main
Exercises
In the file exercises/hw4.md, provide solutions to the following problems. You will need to do research, as not all of these topics were covered in class. For exercises that ask you for prose answers, please be comprehensive and demonstrate a broad understanding. But avoid long-winded, silly gen-ai answers. Expect deductions for verbatim copies of AI slop. Be precise and concise.
- In Java, which keywords (if any) are used to indicate a class may have (a) no instances, (b) only a fixed number of instances, (c) no subclasses, (d) only a fixed number of subclasses.
- Very briefly, list the four main differences between a Swift class and a Swift struct.
- Does Swift have null references? If so, show an example. If not, how exactly did they prevent this billion dollar mistake?
- Assuming
Dog is a subclass of Animal, should you be able to assign an expression of List<Dog> to a variable type constrained as List<Animal> in its declaration? Answer not in terms of what some languages do, but what makes the most sense in terms of type safety.
- Why is Swift’s
Void type weirdly named? What is their “excuse” for using that term for what is essentially a unit type?
- What is the type of a supplier in Swift?
- Why did Yegor Bugayenko think Alan Kay was wrong about being wrong about using the term “object” when he coined the term “object-oriented programming”?
- What is the difference between class-based and prototype-based OOP?
- List all the things that a Java record automatically generates.
- Java does not (yet?) have companion objects like Kotlin. What do Java programmers have to use instead?
Please use professional and well-structured Markdown formatting for your answers. Take pride in your work. Show you care.
Submission
To submit your work, choose one and only one team member to submit to BrightSpace (1) the names of all team members, (2) the URL of your private repository, and (3) an affidavit for each team member stating that they have done the assigned book readings and reviewed the aforementioned course notes pages.
Teamwork
Remember that, when working in teams, all team members should participate in generating solutions and are responsible for understanding all submitted answers. Ideally, each team member should produce individual answers for all exercises, and the team should combine them to produce the ultimate submissions.
Grading
You will be graded both on the correctness of your solutions, adherence to each language’s conventions, the following of the assignment instructions, and the cleanliness of the repository. Feedback will be in the form of deductions for such things as:
- Not following the spacing, indentation, and capitalization conventions of each language
- Poor names (note that even if the exercise descriptions above use short names, your solutions should use readable names where appropriate)
- Naming an entity in a way that has nothing to do with the variable’s purpose
- Defining variables that are never used
- Unnecessary comments
- Missing necessary comments
- Data that is mutable when immutability is called for
- Lack of information hiding (to the extent allowed by the language)
- Failing to meet any explicitly specified implementation requirement (not every requirement is captured by the unit tests)
- Code that is needlessly inefficient
- Code that does not properly manage resources, such as that which fails to release allocated memory or close open file handles
- Code that is too complex and fails to take advantage of the language features covered in class that are designed to facilitate the assigned task (deductions will be small here: you should, when unsure of the “best” solution, to at least give a solution, since a working substandard solution is better than no solution at all)
- A repository structure that varies from the specified layout
- Extra files in the GitHub repository
- Not following submission instructions; for example, not supplying the names of every group member or not providing the URL of your repository or not including reading affidavits for each group member
This list is not exhaustive, but should help in getting you used to paying attention to your submissions and taking pride in your work. Note that most code editors have plugins that will auto-format your code and even look for suspicious coding practices. You are strongly encouraged to use them.
Your homework submission will be the state of your repository on your main branch, at 18:00 in the America/Los Angeles time zone on the due date.