Homework 10
Last updated: Fri, 24 Apr 2026 12:48:42 -0400
Out: Tue Apr 14 2026, 11am EST
Due: Thu Apr 23 2026, 11am EST (Note the Thursday deadline)
Overview
In this assignment, we will add more features, i.e., variables, environments, and function application, to the high-level programming language we are creating.
This hw will be graded accordingly:
correctness (Autograded) (24 pts)
design recipe (16 pts)
testing (12 pts)
style (20 pts)
README (2 pt)
Setup
Create a new repository for this assignment by going to the CS450 Spring 2026 GitHub Organization and clicking "New".
Note: The CS450 Spring 2026 GitHub Organization must be the owner of the repository. Please do not create the repository in your own account.
On the "Create a new repository" screen:
Name the repository hw<X>-<LASTNAME>-<FIRSTNAME> where <X> is the current homework number.
For example, I would name my hw10 repository hw10-Chang-Stephen.
Mark the repository as Private.
Check "Add a README file".
Select the Racket template for the .gitignore.
Choose whatever you wish for the license.
When done click "Create repository".
Updating Racket450
Make sure you have the latest version of racket450.
To do this from DrRacket, go to File -> Package Manager -> Currently Installed, search for "racket450", and then click "Update".
Alternatively, if you prefer the command line, run:
raco pkg update racket450
Reading
Review Chapters 21 and 23 of the Textbook.
NOTE: The textbook will refer to "Student Languages" which we do not use in this course (and a "Stepper" that only works with the Student Languages). Instead, we use a version of Racket tailored for this course, which is invoked by putting #lang racket450 at the top of a file (see also Before Submitting).
Also, read any relevant sections of the The Design Recipe section of the course website (topics that will be covered in future lectures are marked as such).
Tasks
The main code should go in a file named hw10.rkt that uses #lang racket450, as described previously.
NOTE (new): To make this assignment self-contained, some parts of previous solutions may be given for this assignment (see below). Nonetheless, you must still write this assignment on your own from scratch. No credit will be given if you do not do this.
As usual, all submitted code must follow the The Design Recipe. This means that language features may only be used in the correct scenarios, as called for by The Design Recipe.
For example, set! and other "imperative" features are not allowed ever.
Conditionals such as if and cond are only to be used with the appropriate Data Definitions or in other appropriate scenarios described in class.
Signatures should use define/contract and the predicates defined in the Data Design Recipe step. In this assignment, you may use the listof contract constructor where appropriate.
For Examples and Tests, do not use check-expect from the Beginning Student Language (even though the textbook says to). Instead, use check-equal? or other testing forms from rackunit (which are built into racket450, so do not explicitly require rackunit).
Examples for a function definition should be put before the define in hw10.rkt.
Tests should be put a into hw10-tests.rkt file that uses #lang racket450/testing. Try to think about corner cases and code coverage.
NOTE, on one-line helper functions: If the name and description of a "helper" function clearly describe what it does, and it clearly follows some Data Definition and all other Design Recipe steps (the course staff is the final arbiter of this), it does not need to be submitted with Examples and Tests if they are covered by other tests. ("Helper" functions are defined as functions not described in the homework assignment description.) NOTE: This does not change the Design Recipe. It is only changing submission requirements. As usual, however, we will not be able to help debug code that does not follow the Design Recipe, so omit these steps at your own risk.
All other functions should have at minimum one Example and "sufficient" Tests.
Programming
Create your programming language by implementing parse and run functions that use the following data definitions.
Program Data Definitions
For each of these data definitions, you should complete them by writing the necessary predicates, constructors, etc.
A Variable (Var) is a Symbol
An Environment (Env) is a List<(list Var Result)> (a list of 2-element lists) Represents the runtime environment for in-scope variables and the values they stand for. Entries closer to the head of the list shadow those further in the back.
- A Program is one of:
`(arr ArraySyntax)
Variable
`(iffy ,Program ,Program ,Program)
(cons Program List<Program>)
Represents: the surface-level syntax of a new programming language (i.e., this is what programmers write). The ArraySyntax we will use is similar to the quote-and-square-bracket construction an Array from previous hws, except without the quote since the context of its use is already inside another quote or quasiquote. For example one would write the Program ’(arr [[1 2] [3 4]]), where the inner ArraySyntax has no additional quote.Note Since ArraySyntax is syntax and not a Program, there can be no nested Program data nested in ArraySyntax. This means that there can be no variables or other Program in ArraySyntax.
Note however that beginning in this assignment, ArraySyntax is different from Array. In particular, Array Result may contain NaN values as a result of computation.
- An AST is one of:
(mk-arr Array)
(mk-var Symbol) (do not name your struct var because that is a Racket match pattern)
(mk-ify AST AST AST)
(mk-appl AST List<AST>)
Represents: an abstract syntax tree data structure that is produced from parsing the surface programAn Array here is the Array value like in Homework 9 with bools and strings. It is a subset of the Array Result value below, which an include NaN.
- A Result is one of:
Array
NaN
(Racket) function
ErrorResult
Represents: possible results of running a program in our new language. - An ErrorResult is one of:
UNDEF-ERR (mk-undefined-var-err Var)
NOT-FN-ERR (mk-not-fn-err Result)
Represents: possible "error" results of running a program in our new language. The constructors are given as part of the racket450/hw10 library (see below).An "undefined" error should occur when trying to use an undefined variable. The constructor takes as input the undefined variable being referenced
A "not fn" error should occur if attempting to call a non-function. The constructor takes the non-function Result that the program attempted to apply
Some subtleties:If one of these errors occurs in an inner expression, it should propagate to be the final result of the whole expression, but "undefined" error should have precedence. For example, applying an undefined variable should produce "undef" error and not "not fn" error
UPDATE: If more than one error occurs at the same "level", e.g., in arguments of a function call, the error result should be determined using a left-to-right precedence.
Array and Data Definitions
- An Atom is one of:
Number
Bool
String
NaN
- An Array is one of:
Atom
ArrayList
An ArrayList is one of:Further, an Array has an additional invariant that it is "rectangular", meaning that along any one dimension, each element must have the same length. For example, in a 2d Array, all the rows must have the same length.
Functions (define and provide)
parse : takes a Program and produces an AST abstract syntax tree data value. When the input is not a valid Program, this function should call raise-syntax-error with an exn:fail:syntax:cs450 exception, which is given in racket450/hw10, and an appropriate error message. Be careful here because what is a valid/invalid Program has changed and may require more sophisticated parsing to determine.
run : takes an AST tree and "runs" it, to produce a Result "result"
As mentioned in lecture, operations on arrays, should follow the behavior of NumPy. When dealing with Atoms, operations should follow JavaScript semantics. We will use the repljs.com evaluator as the official specification for this behavior.
This run function should define an internal "run" function that uses an Environment accumulator, so make sure to follow all steps of the Accumulator Design Recipe. The "initial environment" should contain bindings for the * and === symbols, which should correspond to the appropriate array functions functions like "450times" shown in lecture. Note that these functions will be different from the "array-element" functions below, since they will need to handle errors and other Results.
The semantics of the elementwise operations should follow JavaScript semantics, where === corresponds to JavaScript’s strict equals. When in doubt, we will use the repljs.com evaluator as the official specification for the behavior of arithmetic on all kinds of values in our new programming language.
Some more specific semantic specifications:Arithmetic (e.g., plus or times) of strings has some interesting corner cases so you’ll want to have ample examples and tests here. Some examples to check: different combinations of data types, e.g., operations on strings and bools, etc.In particular, note that multiplication with different data types does not always have the same behavior as plus.
When attempting to perform an arithmetic operation that is unsupported, the result should be NaN, which is a constant given to you in racket450/hw10 (see below). Since the language can have nested operations, this means that the input to primtive functions can also be NaN and this needs to be handled.
The "test" part of an iffy expression should follow "truthy" semantics. This means that the result of the test may be a non-boolean which should be interpreted in the same way as JavaScript, and the appropriate branch should be taken in these cases. You should use a helper function to help convert non-booleans to booleans, when they are used as the test in an iffy expression
The === operator implements "equality". We will use the semantics of JavaScript’s === operator to dictate the behavior of our equality operator, so you should look into that.
array-element+ : takes two Atoms and produces the result of "adding" them together.
array-element* : takes two Atoms and produces the result of "multiplying" them together. As mentioned in lecture, "multiply" should follow JavaScript semantics. We will use the repljs.com evaluator as the official specification for this behavior.
The implicit type conversion should be performed by a separate function, and should not be embedded in this function
array-element= : takes two Atoms and returns true if they are "equal". As mentioned in lecture, "equality" should follow JavaScript === semantics. We will use the repljs.com evaluator as the official specification for this behavior.
result* : takes two Results and produces the result of "multiplying" them together.
result= : takes two Results and produces the result of comparing them for equality.
Library Functions
In order to be self-contained, a library racket450/hw10 will be available that contains some parts of solutions to previous assignments. (Note: This library will not be available until all students have submitted Homework 9.) Import this library by writing (require racket450/hw10). You may of course use your own implementations of the functions below as well.
The library will contain the following functions:
hw10-Atom? evaluates to true for Atom data values in this hw
hw10-Array? evaluates to true for Array data values in this hw
hw10-mk-array-op Takes as input an implementation of an elementwise function with signature (-> hw10-Atom? hw10-Atom? hw10-Atom?) and returns an array version of that function with signature (-> hw10-Array? hw10-Array? hw10-Array?). E.g., passing in array-element* and returns an implementation of array*, etc. The output function raises exn:fail:cs450:broadcast if the given inputs are not compatible.
exn:fail:cs450:broadcast? Evaluates to true if given a broadcast exception produced by the hw10-mk-array-op function above. May be useful for testing.
exn:fail:syntax:cs450 Exception for syntax errors produced by your parse function.
exn:fail:syntax:cs450? predicate for cs450 syntax error exceptions.
mk-undefined-var-err Takes a symbol and produces a value representing an "undef var" ErrorResult
undefined-var-err? Predicate for above ErrorResult value
mk-not-fn-err Takes a (non-function) Result and produces a value representing a "applied non-fn" ErrorResult
not-fn-err? Predicate for above ErrorResult value
hw10-ErrorResult? evaluates to true for the two ErrorResult values above
NaN "not a number" Result
NaN? predicate the returns true when given NaN
Before Submitting
Testing (and Autograders)
Before submitting, note:
Each programmer is solely responsible for testing their program to make sure it’s correct. Do not submit until all code has been has a "sufficient" number of Test cases that verify its correctness.
Note that there is no GradeScope "Autograder" available for students to use (an Autograder is not a software development/testing tool anyways, so it should not be used as one).
Thus, no questions mentioning an Autograder will be answered, e.g., posts asking "why is the Autograder giving an error?" are not allowed.
If you happen to find an Autograder and decide to look at its output despite this warning, please understand that it may be incorrect or incomplete, change at any time, or have random behavior, and that it in no way indicates the grade of the submitted hw.
Anyone that does get useful information from an Autograder, e.g., a failing test case or crashing code report, should treat it as bonus information (that you otherwise would not have had) that you and you alone must determine what to do with.
Regardless of what any Autograder might say, all code must still be independently tested to be correct before it is submitted.
The proper way to ask questions is with small code examples. This means that each question must include a small example code snippet along with what the "expected" result should be!
Further, any posted examples should contain the minimal amount of code needed to explain the question. Full file dumps or anything more than a few lines will not be accepted. More is not better. In fact it’s worse because it takes longer to read and is less likely to get a good answer.
Style
All code should follow proper Racket Style.
Also, the repository itself must follow proper style. Specifically, it must have appropriate commit messages. See How to Write a Git Commit Message if you are unsure how to write a commit message.
Note: Do not use the "file upload" feature on Github. The course staff may not accept hw uploaded in this way.
Files
A submission must have the following files in the repository root:
hw10.rkt: Contains the hw solution code.
The first line should be #lang racket450.
All defines should use the name specified in the exercise (ask if you are unsure).
hw10-tests.rkt: This file should use the #lang racket450/testing language.
It should also require hw10.rkt and define tests for it.
Specifically, it should contain "sufficient" Test cases (e.g., check-equal?, etc.) for each defined function.
README.md: Contains the required README information, including the GitHub repo url.
Submitting
When you are done, submit your work to Gradescope hw10. You must use the "GitHub" Submission Method and select your hw<X>-<LASTNAME>-<FIRSTNAME> repository.
Note that this is the only acceptable way to submit homework in this course. (Do not manually upload files and do not email files to the course staff. Homework submitted via any unapproved methods will not be graded.)

