Working Through Exercises in HtDP 2nd Edition

Not that I think this is anything particularly noteworthy or special but I've been working through the exercises in HtDP 2nd Edition and posting my answers up to my blog lately. I decided to try to do the exercises with Typed Racket and RackUnit (rather than using the BSL language).

I'm mentioning this here mainly so that if anyone cares to comment ("Hey Onorio--here's a better way to do that!") they can do so!

Since you are using “adult Racket”, consider using (module+ test …) to set tests apart from code.

2 Likes

2 thoughts—

  1. Idiomatic Racket puts test code in a test submodule. So (module+ test …), and you can (require rackunit) inside that module, define your examples and checks there, etc.
  2. You probably already know this, but time is hard. HTDP's version may be convenient for an exercise, but don't use anything like it for production code :wink: in particular, leap seconds will screw up the calculation, and there's probably other things I'm missing.
2 Likes

Thanks @EmEf and @benknoble for the suggestions!

When you put tests inside a submodule is the source code left in the same file? For instance on this example:

; 5.8 Exercise 81
;time-since-midnight is (time-since-midnight Number Number Number)
(struct time-since-midnight ([hour : Number] [minute : Number] [second : Number]))

; Take TIME-SINCE-MIDNIGHT and return equivalent number of seconds
; time-since-midnight -> seconds
;
(: time->seconds (-> time-since-midnight Number))
(define (time->seconds time)
  (+ (* (time-since-midnight-hour time) 3600)
     (* (time-since-midnight-minute time) 60)
     (time-since-midnight-second time)))

(check-equal? (time->seconds (time-since-midnight 0 23 30)) 1410)
(check-equal? (time->seconds (time-since-midnight 10 12 25)) 36745)

Would I modify it to something like this?

; 5.8 Exercise 81
;time-since-midnight is (time-since-midnight Number Number Number)
(struct time-since-midnight ([hour : Number] [minute : Number] [second : Number]))

; Take TIME-SINCE-MIDNIGHT and return equivalent number of seconds
; time-since-midnight -> seconds
;
(: time->seconds (-> time-since-midnight Number))
(define (time->seconds time)
  (+ (* (time-since-midnight-hour time) 3600)
     (* (time-since-midnight-minute time) 60)
     (time-since-midnight-second time)))

(module+ test
(check-equal? (time->seconds (time-since-midnight 0 23 30)) 1410)
(check-equal? (time->seconds (time-since-midnight 10 12 25)) 36745))

I hate to impose on your generous help but could you point me to an example of what you're talking about?

I like to keep the dependency on rack unit also in a submodule, like this:

#lang typed/racket

(module+ test
  (require typed/rackunit))

(struct time-since-midnight [{hours : Natural}] #:type-name TimeSinceMidnight)

(: since (TimeSinceMidnight -> Natural))
;; determine the number of minutes since midnight from `this` TimeSinceMidnight
(define (since tsm)
  (* 60 (time-since-midnight-hours tsm)))


(module+ test
  (check-equal? (since (time-since-midnight 1)) 60))

I also like to name the Type something different.

1 Like

Thanks again for sharing!

Since you've been so kind as to share your know-how so far--is it idiomatic to keep the tests in the same source file as the code or are you simply putting them all in the same file just for purposes of illustration?

And that tip about #:type-name is very welcome as well. Some of the names I come up with are pretty clumsy; I don't think #:type-name will help with that but it's a step in the right direction.

My own preference is to keep tests inside the same file for my course projects. (When you start writing large programs — like the ones I used to assign in our Fundamentals 1 — consider reading https://felleisen.org/matthias/Thoughts/Modular_Programming.html for this kind of programing.)

The Racket standard is to split packages into three parts: code, tests, docs so that people allergic to receiving more than absolutely necessary can just ge the code.

1 Like

Thank you!