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.
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.