[racket] Basic Racket Question

From: Stephen Bloch (sbloch at adelphi.edu)
Date: Wed Dec 22 10:05:22 EST 2010

On Dec 21, 2010, at 5:43 PM, Sayth Renshaw wrote:

> Do i need to define the gross function in my netpay function?

No, you don't.  You've already defined it by saying
	(define (gross hours)
		(* hours pay-rate))

> If I have defined gross function as suggested earlier why can't my netpay function use it?

Because by writing
	(define (netpay gross tax-rate) ...)
you've invented a NEW thing named "gross", which takes precedence over the previously-defined thing by the same name.

> (define tax-rate 0.15)
> (define pay-rate 12)
> (define (gross hours)
>   (* hours pay-rate))
> (define (netpay gross tax-rate)
>     (- gross 0 (* gross tax-rate)))
> > (netpay (gross 20)tax-rate)
> 204

Yes, I guess this works but it's way more complicated and confusing than it needs to be, for several reasons.

1) The definition of "netpay" doesn't need to mention "tax-rate" as a parameter, and the call to "netpay" doesn't need to pass in "tax-rate" as an argument; "tax-rate" has been defined globally, so it can be used inside the function anyway (as you did with "pay-rate" in the definition of "gross").

2) You have two different things named "gross": a function that takes a number of hours and returns a number of dollars, and a (presumably numeric) parameter to "netpay".  There's nothing illegal about this, but it tends to cause confusion.

3) In programming, there's always a conflict between making things easier to write and making them easier to use.  "netpay" would be easier to use if I could just give it a number of hours, e.g.
	(netpay 20)
You've decided to make it harder to use by expecting the user to write
	(netpay (gross 20))
In other words, the user won't get the right answer unless the user does an extra bit of work before calling netpay.

> Indeed I know what you are referencing. 
> [snipped quotes about area-of-ring from the book]

OK, I'm convinced that you "know what we are referencing," but why aren't you DOING it?

1) Write down a contract for "netpay".  In this step, don't even think about how to solve the problem.  Think ONLY about how "netpay" will be used: should it be given a number of hours, or a number of dollars, or both, or a number of hours and a tax rate, or a number of dollars and a tax rate, or what?  What should it return?

2) Then write down two or more examples for "netpay".  Each example should be a complete function call, with specific arguments, and be accompanied by the "right answer" (worked out by hand or on a calculator), so you can easily tell whether the actual answer matched what you said the answer should be.  I strongly recommend using "check-expect" for this.
"check-expect" isn't discussed in _How to Design Programs_, because it hadn't been invented yet when that book was written, but we've all been using it for years, and it makes your life much easier because DrRacket will check all your test cases for you and tell you which ones passed, which ones failed, and HOW they failed.

3) Then write a function skeleton (see my previous message).

4) Then write a function inventory (see my previous message).

5) Then fill in the body; this is the hardest part, but doing the other steps first makes it easier.

6) Then run the test cases and see if they pass.

Here's another example of how this design recipe works.  Suppose I had been asked to write a "cube" function.
; cube : number -> number
(check-expect (cube 0) 0)
(check-expect (cube 3) 27)
(check-expect (cube -6) -216)
(check-expect (cube 2/3) 8/27)
(define (cube num)
   ; num               a number
   (* num num num))

Once all of the above is in the Definitions pane, you can hit the "Run" button and DrRacket will check all your test cases; they should all pass.
To see what happens when a program is buggy, try changing the definition of "cube" so it's wrong:
(define (cube num)
   ; num          a number
   (* num num 3))
and hit "Run" again.  It should still pass the first two test cases, but fail the third and fourth, and DrRacket should show you both the predicted "right answer" and the actual answer for each failed test cases.

Take a look at http://www.picturingprograms.org/pdf , which is the first few chapters of a more up-to-date book based on _How to Design Programs_.  It discusses (in chapter 5) this design recipe in considerable detail, including how to use check-expect.

Stephen Bloch
sbloch at adelphi.edu

Posted on the users mailing list.