4

I am using the famous book How to Design Programs. More specifically, the first edition (I have the physical one).

In the 6th chapter, there are some exercises with Structures. In one of them, you need to simulate traffic lights and use effects (mutation) to change them.

I am referring to the exercise Exercise 6.2.5 about the function next which is suppose to give you the next color of the traffic lights.

The answer sheet provided by the book is:

(start 50 160)
(draw-solid-disk (make-posn 25 30) 20 'red)
(draw-circle (make-posn 25 80) 20 'yellow)
(draw-circle (make-posn 25 130) 20 'green)

; -------------------------------------------------------------------------

;; clear-bulb : symbol -> true
;; to clear one of the traffic bulbs
(define (clear-bulb color)
  (cond
    [(symbol=? color 'red) 
     (and (clear-solid-disk (make-posn 25 30) 20)
          (draw-circle (make-posn 25 30) 20 'red))]
    [(symbol=? color 'yellow) 
     (and (clear-solid-disk (make-posn 25 80) 20)
          (draw-circle (make-posn 25 80) 20 'yellow))]
    [(symbol=? color 'green)
     (and (clear-solid-disk (make-posn 25 130) 20)
          (draw-circle (make-posn 25 130) 20 'green))]))

;; tests
(clear-bulb 'red)

; -------------------------------------------------------------------------

;; draw-bulb : symbol -> true
;; to draw a bulb on the traffic light
(define (draw-bulb color)
  (cond
    [(symbol=? color 'red) 
     (draw-solid-disk (make-posn 25 30) 20 'red)]
    [(symbol=? color 'yellow) 
     (draw-solid-disk (make-posn 25 80) 20 'yellow)]
    [(symbol=? color 'green)
     (draw-solid-disk (make-posn 25 130) 20 'green)]))

;; tests
(draw-bulb 'green)

; -------------------------------------------------------------------------

;; switch : symbol symbol -> true
;; to switch the traffic light from one color to the next
(define (switch from to)
  (and (clear-bulb from)
       (draw-bulb to)))

;; tests
(switch 'green 'yellow)
(switch 'yellow 'red)

; -------------------------------------------------------------------------

;; next : symbol -> symbol
;; to switch a traffic light's current color and to return the next one  
(define (next current-color) 
  (cond 
    [(and (symbol=? current-color 'red) (switch 'red 'green)) 
     'green] 
    [(and (symbol=? current-color 'yellow) (switch 'yellow 'red)) 
     'red] 
    [(and (symbol=? current-color 'green) (switch 'green 'yellow)) 
     'yellow]))

(next 'red)
(next 'green)
(next 'yellow)
(next 'red)

On the next function, I did a similar thing that achieved the same results on the tests provided:

(define (next current-color) 
  (cond 
    [(symbol=? current-color 'red) (switch 'red 'green)] 
    [(symbol=? current-color 'yellow) (switch 'yellow 'red)] 
    [(symbol=? current-color 'green) (switch 'green 'yellow)]))

Differently from the book's answer, my code does not use and and does not let a loose single symbol (e.g. 'red).

This difference intrigues me since the book has a strong emphasis on teaching you how to design code. The point that intrigues me is the fact that the original solution uses an and (to combine succeeding effects) which seems to be unnecessary besides using a "lonely" 'red, 'yellow or 'green by the end of each conditional statement.

I do not get the purpose of this last symbol statement or the and.

Is there some stylistically or conceptual reason for having this approach that seems to be more verbose and less clear?

I am reading this book exactly to improve the way I write code.

Pedro Delfino
  • 2,421
  • 1
  • 15
  • 30
  • 1
    This use of `and` to sequence side-effects is a flaw that the second edition fixed by changing to the more functional and more flexible "universe" teachpack. It's a much cleaner design, and I recommend at least looking at the second edition to see how it's done there. – Ryan Culpepper Mar 31 '21 at 17:39

1 Answers1

5

Racket, being a kind of Scheme, is an expression-oriented language. That means that the last expression in a compound expression is that whole expression's value.

This includes a quoted symbol. Its value, the symbol, is the returned value.

The function call (next current-color) switches the traffic light's color and returns a symbol indicating the new color of the traffic light:

;; next : symbol -> symbol

Your code switches the color and returns true (according to the specification for switch):

;; switch : symbol symbol -> true
;; your-next : symbol -> true

This changes how the function next can be used. With the book's design we can write

....
   (let loop ( ... )
       .....
       (let ((current-color (next current-color)))
           ......
           ))
....

With your design this kind of natural style of looping code is impossible.

A general remark: those specifications are known as types, and we let the types guide our use of functions in our code. They help us see what goes in, and what goes out, so we can connect the matching wires, so to speak.

Will Ness
  • 70,110
  • 9
  • 98
  • 181
  • Great. This was out of sight for me when I asked. It makes total sense. What you said is going to be particularly useful while using recursive functions. Thanks. – Pedro Delfino Mar 31 '21 at 16:20
  • 1
    glad to be of help. those specifications are known as *types* BTW, and we let the types guide our use of functions in our code. they help us see what goes in, and what goes out, so we can connect the matching wires so to speak. :) happy trails, – Will Ness Mar 31 '21 at 16:21
  • 1
    yeah. I used to worry with types only when using statically typed languages like Standard ML. Since Racket is dynamically typed, I was not really paying attention. But there is a reason for the answer sheet to use the commentaries to express the types I/O. I will start to use comments before function as a good practice. – Pedro Delfino Mar 31 '21 at 16:25