On this page:
2.1 Introduction
2.2 Reading
2.3 Assignment
2.3.1 Python if
2.3.2 Loops
2.3.3 Automata
2.3.4 Note on Checking Equality
2.4 Starter Code
2.5 What To Hand In
2 Macros

    2.1 Introduction

    2.2 Reading

    2.3 Assignment

      2.3.1 Python if

      2.3.2 Loops

      2.3.3 Automata

      2.3.4 Note on Checking Equality

    2.4 Starter Code

    2.5 What To Hand In

2.1 Introduction

These problems get you warmed up writing your own macros in Racket.

2.2 Reading

Read Chapter 13 of PLAI 2/e. You won’t need all of it now, but you may find it useful to return to this periodically.

2.3 Assignment
2.3.1 Python if

Look up the truthy-falsy behavior of Python 3, and implement a python-if macro that has the same syntax as Racket’s but implements Python’s truthy-falsy behavior. There are lots of corner cases; be careful and test extensively!
Since the number of types of values is very large, we’re going to scope the problem by limiting it to the set of types you can meaningfully use in smol/hof. In other words: numbers, strings, symbols, Booleans, characters, pairs, lists, and functions.
Note as a language design issue that the more types there are in the language, the more a non-trivial truthy/falsy system has to consider. Think of this as a virtue of the systems in languages that are either Boolean-only (à la OCaml) or extremely simple (à la Racket: false is false, everything else is true).

2.3.2 Loops

Recall the different looping behavior we saw in the Loops assignment? You must implement two “loop” macros, one corresponding to each of Python’s and Racket’s behaviors. We will call these for-wrong and for-right, respectively. Both have the same syntactic structure:

(for-right id L B)

(for-wrong id L B)

where id is an identifier (the “loop variable”), L is an expression that evaluates to a list of values, and B is the body. The result in both cases is a list of the values produced by evaluating B with id bound to each of the values in L. Thus:

#lang racket

 

(require smol/hof/compat)

 

(define L1 (for-right x (list 1 2 4) (* x x)))

(define L2 (for-wrong x (list 1 2 4) (* x x)))

 

(test L1 '(1 4 16))

(test L2 '(1 4 16))

In the above example, both “loop” macros produce the same result. However, if you instead build a closure in the loop body and evaluate that after the loop is over, you will get the behavior we saw in Loops.
You are welcome to use any of Racket’s constructs, such as for.
If you have the basic idea sketched out but need help with Racket constructs or with macro use syntax, by all means ask!

2.3.3 Automata

Finally, we strongly urge you to read this paper for another example of macros.
There is no work to turn in associated with this, but we trust you to read it anyway—we’ll refer to it in class and possibly on future homeworks. As you’re reading it, see whether you can spot the error before the paper provides the fix.

2.3.4 Note on Checking Equality

Racket provides a number of different ways to check for equality. For assignments in this class, you should use equal? rather than eq?. You can read the official Racket documentation on the differences between equal? and eq? here.
(To compare numbers, you can use =.)

2.4 Starter Code

We’ve provided starter code for your implementation at loops.rkt and python-if.rkt.

2.5 What To Hand In

You will submit two files for this assignment: loops.rkt and python-if.rkt. These should be uploaded to the “Code” drop on Gradescope.
There are no test suites to submit for this assignment on Gradescope.
You can update your submissions as many times as you want before the deadline.