关键词 > CS17

CS17 Fall ’25 Lab 7 Debugging

发布时间:2025-10-25

Hello, dear friend, you can consult us at any time if you have any questions, add WeChat: daixieit

Lab 7 | Debugging

CS17 Fall ’25

October 19-20, 2025

Objectives

By the end of this lab, you will know:

• how to identify bad pattern matching and correct it.

• how to identify and correct syntax errors.

• the kinds of type errors, and how to debug them.

1 Discussion

Later this week, the Rackette project will be released, where you will be asked to create an interpreter for a smaller version of Racket using ReasonML, essentially processing Racket programs into strings.

In this discussion section, you will think about the decisions involved in designing programming languages and their implications on a global scale. If you go on to work at a big tech company or pursue computer science research, the decisions you make will likely have a global effect!

In groups of 3-4, work through the following discussion questions and tasks. At the end of this section, one group member must fill out a Google Form about your key takeaways. You should focus on your discussion, but take some notes to help you fill out the form. You will be checked off for this portion of the lab after you submit the form.

Discuss: What knowledge did you need to have before you started coding in Racket?

Think about linguistic skills or skills from other classes that you applied while learning Racket/programming.

Task: Read the following facts.

There are approximately 6.84 billion smartphones (equal to 85 percent of the world’s population).

Global Smartphone market share from 2021 to 2023. In 2023, the shares are: 20% Samsung (South Korea), 17% Apple (USA), 12% Xiaomi (China), 10% OPPO (China), 8% vivo (China)

Google search result for most popular programming languages in the world (Java, Python, JavaScript, Ruby, PHP, Swift, C++, SQL, Kotlin).

Global tech industry distribution (35% North America, 32% Asia, 22% Europe, 6% Latin America, 5% Africa).

The 10 most spoken languages in the world. English: 1.132 billion speakers, 33% are native speakers, Mandarin Chinese: 1.117 billion speakers, 82% are native speakers, Hindi: 615 million speakers, 55% are native speakers, Spanish: 534 million speakers, 86% are native speakers, French: 280 million speakers, 28% are native, Standard Arabic: 274 million speakers, Bengali: 265 million speakers, 86% are native, Russian: 258 million speakers, 60% are native, Portuguese: 234 million, 94% are native, Indonesian: 199 million, 22% are native.

Global developer population and demographic study, 2018. There are 27.7 million developers in the world, with most being in the United States. India’s developer population will overtake the US by 2023. The top nation for growth is China.

Discuss: The most commonly used programming languages are in English. Have you thought about or noticed this before? Given the data above, what are possible pros and cons to this?

Discuss: Most popular coding languages are in English and most of the tech industry is in North America and Europe. How does this affect everyday users and programmers?

An example to get you started: someone whose first language is Arabic may run into problems when using software. If they open a PDF in Arabic, they can’t always use CTRL+F to search for words, because many apps like Chrome’s PDF reader or Apple’s Books app don’t fully support Arabic character recognition.

Task: Read the following background information.

There are several reasons why modern programming languages are mainly in English. The immediately obvious reason is that English is widely used in the global economy, and is the Lingua Franca for coding. This is essentially a network effect.

Moreover, modern programming languages are the offspring of older programming languages, the first of which were mainly developed in the United States in the mid-20th century. Because they were designed by English speakers, early programming languages were English-based, and therefore their modern offspring are also English-based.

But why was the United States a pioneer in computer science in the first place, and why is English so popular globally?

Part of it goes back to European colonialism and exploitation of the Global South following the Industrial Revolution. The distribution of resources and technologies in the world today is largely result of European colonization.

That being said, more contemporary factors also play a role (i.e. geopolitical effects of WWII and the industrial structure of the US). If this interests you, we would encourage further research.

Task: Read this short article about the relationship between the Industrial Revolution and European imperialism.

Discuss: On a global scale, what are the implications (social, political, economic, technological, etc.) of the majority of the technology sector being concentrated in North America and Europe?

If you are stuck, consider global communication systems, diplomatic and economic relationships between countries, or the historical dominance of Western countries.

Discuss: As a programmer, how do you perceive your role in this global system? It’s okay to not know the answer to this question!

Some ideas to think about: If you end up working as a software engineer or product manager in a US-based big tech company, should you have a responsibility to enact change? What role do programmers in the West play in upholding Western dominance in tech?

Task: Fill out this Google Form to get checked off (only one group member needs to do this).

2 Recursion Practice

Now that you’ve seen a few more examples of list recursion procedures in class, we want you to practice some on your own! As we’ve seen, here is the template for writing procedures that recur on lists in ReasonML:

let rec proc : list ('a ) = > < return -type > =

alod = >

switch ( alod ) {

| [] = > ...

| [ head, ... tail ] = > ... head ... ( proc ( tail ) ) ...

};

You should write each of these procedures in VSCode. If you don’t remember how to run Reason code in VSCode, check out this Ed post if you’re using Docker or our setup guide if you installed Reason locally.

Note: If you try to call a procedure directly in VSCode, such as memberP([1, 2, 3, 4], 2) , you’ll probably get the error saying that “Toplevel expression is expected to have unit type.” To get around this, we’ve provided you with the file CS17SetupLab7.re, which will contain various checkExpect s. For a refresher on how to use these in Reason, check out Lab 6! In this lab, you should never directly call procedures while running them in VSCode; if you want to see a procedure’s output, you should either run it in RePlay or write a checkExpect . To use the procedures in CS17SetupLab07.re in the procedures you write, you must include the following at the top of each file:

open CS17SetupLab07 ;

2.1 Practice with Pattern Matching

In class, we’ve written the Racket procedure contains17? , which consumes a list, and outputs a boolean representing whether or not there is a 17 somewhere in that list. Now, we would like you to write the equivalent in Reason! As a reminder, the Racket version looks like this:

( define ( contains17? alon )

( cons

[( empty? alon ) false ]

[( cons? alon ) (or ( = 17 ( first alon ) )

( contains17? ( rest alon ) ) ) ]) )

Note: In Reason, you cannot use ? to distinguish predicates, because the ? character is reserved for another use. As a refresher, a predicate is a one-argument procedure that returns a boolean, such as even? or empty? in Racket. An alternative is to append a P to the end of the predicate’s name: e.g., contains17P . The P stands for “predicate.”

Task: Write the procedure contains17P: list(int) => bool in Reason! One way to do this would be using List.mem , but we ask that you take advantage of pattern matching instead. Additionally, unlike in the Racket version, you should write the procedure without using any boolean operators (such as || ) or if-expressions.

Hint: Consider how you could utilize pattern matching to solve this problem instead of an if or || ! Keep in mind that the pattern

| [1 , ... tl] = > ...

matches on any list where 1 is the first element.

Now, we’d like you to solve a slightly harder problem! Instead of looking for a single 17 in a list, we now care about finding two 17s in a row. Once again, you should take advantage of pattern matching.

Task: Write the procedure containsTwo17sP: list(int) => bool , which consumes a list of integers, and outputs a boolean representing whether or not there are at least two contiguous 17s anywhere in the list. For example:

containsTwo17sP ([1 , 2 , 3 , 4 , 5])

= > false

containsTwo17sP ([17 , 2 , 17 , 4 , 17])

= > false

containsTwo17sP ([17 , 17])

= > true

containsTwo17sP ([ -5 , -4 , -3 , -2 , 17 , 17 , -1 , 0])

= > true

You should write this procedure without using List.mem , boolean operators, and if-expressions. Addition-ally, you should not use contains17P as a helper.

✜ You’ve reached a checkpoint! Please call over a lab TA to review your work.

2.2 Bad Lists

In CS17, we believe that all lists, no matter their length or type, are created equal. However, for this problem, we’re going to say that some lists are “bad”, and some lists are “good”.

Good lists are those where all the elements in the list are the same, and bad lists are those where two or more elements are distinct (hence all zero- and one-element lists are good). Here are a couple examples of good and bad lists:

/* Good lists ! */

[]

[1]

[" hello "]

[ -3 , -3 , -3]

[4.2 , 4.2]

[ None, None, None, None, None, None ]

/* Bad lists :( */

[" hello ", "bye"]

[ -3 , -3 , -3 , 1]

[4.2 , 8.4 , 16.8 ]

[ true, false ]

[ None, Some ("A") , None, Some ("B") , None, Some ("C")]

Task: Write a procedure, firstBad , which takes in an 'a list , and returns an option('a) which represents the first “bad” value in the list, or None if the list is “good”. The first “bad” value essentially refers to the first value in the list which is different from the previous values in the list. Here are a few examples:

firstBad ([]) = > None

firstBad ([1]) = > None

firstBad ([1 , 2 , 3 , 4]) = > Some (2)

firstBad (["A", "A", "A", "A", "A"]) = > None

firstBad ([1 , 1 , 1 , -1 , 1]) = > Some ( -1)

Note that you’re allowed to use an if-expression for this one!

✜ You’ve reached a checkpoint! Please call over a lab TA to review your work.

3 Debugging

The next section of this lab is dedicated to debugging. We’ll be showing you some programs with common Reason errors that you may soon encounter, and your job is to identify what’s wrong with each program and correct it.

For each problem, we ask that you make a good-faith attempt at fixing the code before calling over a TA. But please don’t hesitate to ask for help if you’re really stuck! We understand that you haven’t worked much with Reason yet, and our intent here is not to trick you, but rather to get you familiar with the types of error messages that you might see so that you can fix your own code in the future!

3.1 Pattern Matching Errors

In the first section of this lab, we saw some examples of how to use pattern matching well. Now, we’re going to give you some examples of what we would consider “bad pattern matching.” Each of these procedures will produce incorrect outputs due to issues with pattern matching. For each procedure, you should tell us what the problem is, and how to fix it!

Try running each procedure in both RePlay and VSCode, and make a note of what kinds of error messages each one produces. For each procedure, which environment has more helpful error messages, and why?

For convenience, we’ve also provided each of these procedures in a file called PatternMatching.re, which you can find on the course website. We recommend that you work on debugging these procedures one at a time, and comment out the procedures that you haven’t gotten to yet! As a reminder, to comment out blocks of code, you can use ctrl + / on Windows or cmd + / for Mac.

3.1.1    fibonacci

In lab last week, you wrote the procedure fibonacci . Here’s a solution that we wrote:

/* Input : a natural number, x

Output : the xth Fibonacci number, where 0 is considered the 0th

Fibonacci number */

let rec fibonacci : int = > int = x = >

switch ( x ) {

| 0 = > 1

| 1 = > 1

| x when x > 2 = > fibonacci ( x - 1) + fibonacci ( x - 2)

};

Task: Identify the issue with this broken fibonacci procedure, and correct it. You should tell us

1. What error or warning you got when you ran the code, if any;

2. What input(s) the procedure breaks on, if any; and

3. How you fixed the code!

3.1.2    rightGreater

Here we have a version of a procedure, rightGreater, which takes in a list of ints and produces a list of bools. It doesn’t work!

/* Input : a list of int, aloi

Output : a list of booleans indicating whether each number in the input

list is greater than the next item in the list (the item to the right )

or not. */

let rec rightGreater : list ( int ) = > list ( bool ) = aloi = >

switch ( aloi ) {

| [] = > []

| [hd, ... tl] = >

let rcc = rightGreater ( tl )

[( hd > List . hd ( tl ) ) , ... rcc]

};

Task: Identify the issue with this broken rightGreater procedure, and correct it.

3.1.3    memberP

Here’s an example of a memberP proceure that doesn’t work!

/* Input : a list of data, alod and an item, item

Output : a boolean indicating if item is in the list */

let rec memberP : ( list ('a ) , 'a ) = > bool = ( alod, item ) = >

switch ( alod ) {

| [ item, ... tl] = > true

| [_, ... tl] = > memberP ( tl, item )

};

Task: Identify the issue with this broken memberP procedure, and correct it.

Hint: Try writing some examples first to see what’s wrong with the procedure. In what situations is it producing an incorrect output?

3.1.4    equalsTwo

Here’s a simple procedure, equalsTwo , which produces a string indicating whether or not the input is 2.

/* Input : an int

Output : "not 2" if the input is not 2 and "2" if it is */

let equalsTwo : int = > string = ( num ) = >

switch ( num ) {

| k = > "not 2"

| 2 = > "2"

};

Task: Identify the issue with this broken equalsTwo procedure, and correct it.

✜ You’ve reached a checkpoint! Please call over a lab TA to review your work.

3.2 Syntax Errors

Sometimes there are syntax errors in our code which give us strange error messages or result in the code running differently from how we want it to. The next task shows you some common syntax errors. For each procedure, your job is to identify each error and correct it!

Note: Some procedures may contain multiple errors!

As with the previous section, we recommend that you run these procedures in both RePlay and VSCode. For convenience, we have also provided each of these procedures in the file Syntax.re.

3.2.1    numTwos

Here’s a simple procedure, numTwos , which takes in a list and counts how many 2s are in it. However, this procedure does not compile!

/* Input : aloi, a list of ints

Output : the number of elements in aloi that are 2s */

let rec numTwos : list ( int ) -> int = aloi ->

switch ( aloi ) {

| [] -> 0

| [2 , ... tl] -> 1 + numTwos ( tl )

| [_, ... tl] -> numTwos ( tl )

};

Task: Identify the issue with this broken numTwos procedure, and correct it.

3.2.2    sumList

Here’s an example of a sumList procedure that doesn’t work!

/* Input : aloi, a list of ints

Output : the sum of the elements of aloi */

let sumList : list ( int ) = > int = aloi = >

switch ( aloi ) {

| [] = > 0

| [hd, ... tl] = > hd + sumList ( tl )

};

Task: Identify the issue with this broken sumList procedure, and correct it.

3.2.3    funkyFresh

Here’s a nonsense procedure that we’ve written, funkyFresh , which doesn’t compile.

/* Input : aloi, a list of ints, and n, an int

Output : a list where each element is the corresponding element

of aloi multiplied by n, with any instances of 0 removed */

let rec funkyFresh : ( list ( int ) , int ) = > list ( int ) = ( aloi, n ) = >

switch ( aloi ) {

| [] = > empty

| [0 , ... tl] = > funkyFresh ( tl )

| [hd, ... tl] = > [hd * n, ... funkyFresh ( tl, n )]

};

Task: Identify the issue with this broken funkyFresh procedure, and correct it.

3.2.4    funkyFresher

Here’s a second, fresher, nonsense procedure that we’ve written, which also doesn’t compile.

/* Inputs : aloi, a list of ints

Output : a list where each element is the corresponding element

in aloi multiplied by the following element, with

the final element multiplied by 7 */

let rec funkyFresher : list ( int ) = > list ( int ) = aloi = >

switch ( aloi ) {

| [] = > []

| [hd] = > hd * 7

| [ hd1, hd2 ... tl] = > [hd1 * hd2, ... funkyFresher ([ hd2, ... tl])]

};

Task: Identify the issue with this broken funkyFresher procedure, and correct it.

✜ You’ve reached a checkpoint! Please call over a lab TA to review your work.

3.3 Type Errors

One of the great things about Reason is that we can create our own types! But if we make a mistake while creating these types, it might lead to other issues that could break our code. The next task asks you to identify type errors and correct them. Like before, you can use RePlay and VS Code to help you identify the problem.

Task: For each of the following type definitions, write in a text file whether the definition is valid or not, and if not, how to correct the definition.

type sports = baseball | basketball | hockey | soccer ;

type smurfs = Papa | Clumsy | Smurfette ;

type bignum = Zero | list ( int ) ;

type boolString = bool | string ;

type args =

| OneArg ( string )

| TupleArg (( string, string ) )

| TwoArg ( string, string ) ;

✜ You’ve reached a checkpoint! Please call over a lab TA to review your work.