7.4.0.4

4 Lab Simple Macros

Jay McCarthy

Exercise 1. Write a macro that encapsulates the define-and-provide-contract-out pattern. Consider the idiomatic code:
(define (bigger-string x)
  (number->string (add1 x)))
(provide
 (contract-out
  [bigger-string
   (-> number? string?)]))
How could you write these conveniently with a macro?

Exercise 2. Write a macro that encapsulates the sequential function composition pattern. For example, (number->string (add1 x)) could be written as (fseq2 x add1 number->string). In your first version, only support two functions.

Exercise 3. Extend your fseq2 macro to support an arbitrary number of function steps. For example, (fseq x add1 number->string string-length) should work. Experiment with how you want to represent a computation like (number->string (foldr + 0 (map add1 (list 1 2)))).

Exercise 4. Write a version of our simple-for/list macro that accumulates the results using and rather than using cons. For example, it should be convenient to encode the computation (and (even? 2) (and (even? 4) (and (even? 6) #t))) as (simple-for/and ([x (list 2 4 6)]) (even? x)).

Exercise 5. Generalize simple-for/and into simple-for/fold where the macro application controls the accumulation inside the body of the macro. For example, we should be able to write (simple-for/fold ([acc #t]) ([x (list 2 4 6)]) (and (even? x) acc)) to get the same behavior as our simple-for/and macro.

Exercise 6. In Racket, the cond form requires its body to be entirely made of question and answer pairs:
(cond [question answer]
      ...)
This has the unfortunate consequence of requiring nested conds when an early questions allows the remaining questions to go deeper into a structure. For example, consider the definition of filter:
(define (filter ? l)
  (cond [(empty? l) empty]
        [else
         (define f (first l))
         (define fr (filter ? (rest l)))
         (cond [(? f) (cons f fr)]
               [else fr])]))
Define a new macro, condd, that allows this program to be written as
(define (filter ? l)
  (condd [(empty? l) empty]
         [#:def f (first l)]
         [#:def fr (filter ? (rest l))]
         [(? f) (cons f fr)]
         [#:else fr]))

Exercise 7. Some computations are like burritos that sequence operations, like the following:
(define ((sum-both return bind) mx my)
  (bind mx (λ (x)
   (bind my (λ (y)
    (return (+ x y)))))))

These burrito-like computations are abstracted over what it means to sequence operations. For example, we can use sum-both in two very different ways:
(map (sum-both (λ (x) x)
               (λ (ma f) (and ma (f ma))))
     (list 3 #f 5)
     (list #f 4 6))
; => (list #f #f 11)
((sum-both (λ (x) (list x))
           (λ (ma f) (append-map f ma)))
 (list 1 2 3)
 (list 3 4 5))
; => (list 4 5 6 5 6 7 6 7 8)

Define a macro for patterns like this, so sum-both can be written as
(define ((sum-both return bind) mx my)
  (monad-do return bind
            [x #:<- mx]
            [y #:<- my]
            [#:ret (+ x y)]))

Exercise 8. Write a macro to encode a DFA that accepts lists of Racket objects. For example, consider the following program:
(define (any? x) #t)
(define-dfa even-length
  #:start even
  (#:state even
   #:accepting
   [any? odd])
  (#:state odd
   #:rejecting
   [any? even]))
(even-length '()) ; => #t
(even-length '(0)) ; => #f
(even-length '(0 0)) ; => #t
(even-length '(1 1 1)) ; => #f
 
(define ((is? x) y) (equal? x y))
(define-dfa represents-odd
  #:start not-odd
  (#:state not-odd
   #:rejecting
   [(is? 0) not-odd]
   [(is? 1) odd])
  (#:state odd
   #:accepting
   [(is? 0) not-odd]
   [(is? 1) odd]))
(represents-odd '()) ; => #f
(represents-odd '(0)) ; => #f
(represents-odd '(0 1)) ; => #t
(represents-odd '(1 0 1)) ; => #t

Exercise 9. Modify your DFA macro so the states can accumulate a value from the start of the execution, and base the return value on it. For example, consider the following program:
(define-dfa-like length-of-odd
  #:start even
  #:accumulator len 0
  (#:state even
   #:value #f
   [any? (odd (add1 len))])
  (#:state odd
   #:value len
   [any? (even (add1 len))]))
(length-of-odd '()) ; => #f
(length-of-odd '(0)) ; => 1
(length-of-odd '(0 1)) ; => #f
(length-of-odd '(1 0 0)) ; => 3

Survey link: https://forms.gle/y6QPgnwsXiJv1reJA