On this page:
5.1 Introduction
5.2 Assignment
5.3 Grammar
5.3.1 Paret with Records and Holes
5.3.2 Typed Paret with Records and Holes
5.4 Abstract Syntax
5.4.1 Paret with Records and Holes
5.4.2 Typed Paret with Records and Holes
5.5 What “In Scope” Means
5.6 Testing
5.7 Starter Code
5.8 What To Hand In

CSCI 1730 · Fall 2026

5 Calc Fields🔗

    5.1 Introduction

    5.2 Assignment

    5.3 Grammar

      5.3.1 Paret with Records and Holes

      5.3.2 Typed Paret with Records and Holes

    5.4 Abstract Syntax

      5.4.1 Paret with Records and Holes

      5.4.2 Typed Paret with Records and Holes

    5.5 What “In Scope” Means

    5.6 Testing

    5.7 Starter Code

    5.8 What To Hand In

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:

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.