CSCI 1730 · Fall 2026
5 Calc Fields
5.1 Introduction
The other place your editor offers completions is after a dot: you have written r. and it lists the fields of r. This assignment is Calc Vars again, for fields. The same two things can go wrong: offering a field that the record does not have, or leaving out one it does. Again, we don’t want either to happen.
5.2 Assignment
This assignment uses the two languages of Calc Vars with records added. The hole is now no longer an expression. Rather, it is in the field name of a lookup, (lookup r @), and it can’t appear anywhere else. You will write a function
calc_fields :: Expr -> Listof(Symbol) |
which takes a program containing exactly one hole and returns the field names that could fill it. It should not make either error, and it should always terminate. If you run into difficulty achieving all these goals, post on EdStem telling us why and we’ll try to help you out.
The two languages are Paret with Records and Holes, in paret_records_holes.rhm, and Typed Paret with Records and Holes, in tparet_records_holes.rhm. Each is arranged as in Calc Vars: a Surface type with a parser, a core Expr type, and your own desugarer in between, now extended with records as well. In the typed language you will also need to know the type of the record being looked up: copy your type checker from Type Checker into your file and extend it with records.
5.3 Grammar
5.3.1 Paret with Records and Holes
<expr> ::= ... everything in Paret ... |
| (record (<var> <expr>) ...) |
| (lookup <expr> <var>) |
| (lookup <expr> @) |
(record (a 1) (b 2)) is a record with fields a and b; (lookup r a) is the value of field a of the record r. The provided parser rejects a record with two fields of the same name, and a program with @ anywhere other than the field position of a lookup.
5.3.2 Typed Paret with Records and Holes
The same three forms are added to Typed Paret, and record types are added to its types:
<type> ::= ... everything in Typed Paret ... |
| (Record (<var> <type>) ...) |
Two record types are the same if they have the same fields at the same types, in any order.
5.4 Abstract Syntax
5.4.1 Paret with Records and Holes
The field position of a lookup is either a name or the hole. Like Op, this type is shared by Surface and Expr:
type Lookup |
| l_hole() |
| l_name(name :: Symbol) |
Surface is the surface type of Paret plus
| s_record(fields :: Listof(SField)) |
| s_lookup(rec :: Surface, field :: Lookup) |
|
type SField |
| sfield(name :: Symbol, val :: Surface) |
and Expr is the core type of Paret plus
| e_record(fields :: Listof(Field)) |
| e_lookup(rec :: Expr, field :: Lookup) |
|
type Field |
| field(name :: Symbol, val :: Expr) |
The hole is part of both syntaxes, but remember, it is not an expression: it stands for a field name, so it is a case of Lookup, l_hole(), which both s_lookup and e_lookup use. (Contrast Calc Vars, where the hole stands for an expression and so is s_hole() and e_hole().)
5.4.2 Typed Paret with Records and Holes
The same additions are made to the surface and core types of Typed Paret, and Type gains
| t_record(fields :: Listof(TField)) |
|
type TField |
| tfield(name :: Symbol, ty :: Type) |
5.5 What “In Scope” Means
At (lookup r @), the names in scope are the fields of the record r. For example, in both
(lookup (record (a 1) (b 2)) @) |
and
(let (rec : (Record (a Num) (b Num)) (record (a 1) (b 2))) (lookup rec @)) |
the names in scope at the hole are a and b.
5.6 Testing
As in Calc Vars: results are compared with same_set, and the provided parser rejects a program without exactly one hole. In the typed language, if the record expression of the lookup with the hole is ill-typed, in the environment given by the annotations around it, calc_fields raises a type error as in Type Checker, matched by "type_error". Type errors elsewhere in the program do not matter.
check: |
same_set(calc_fields(desugar(parse_surface_str("(lookup (record (a 1) (b 2)) @)"))), [#'a, #'b]) |
~is #true |
5.7 Starter Code
You are provided with:
calc_fields.rhm and typed_calc_fields.rhm, the stencils for the two languages, each with a place for your desugarer, your type checker where needed, and your calc_fields.
calc_fields_tests.rhm and typed_calc_fields_tests.rhm, the stencils for your tests.
paret_records_holes.rhm and tparet_records_holes.rhm, the two languages, with sexp.rhm and sexp.rkt, which they use to read S-expressions.
5.8 What To Hand In
You will submit to four Gradescope drops, one for each file:
calc_fields.rhm, to the “Untyped: Code” drop.
calc_fields_tests.rhm, to the “Untyped: Tests” drop.
typed_calc_fields.rhm, to the “Typed: Code” drop.
typed_calc_fields_tests.rhm, to the “Typed: Tests” drop.