[plt-scheme] Was HTDP 21.1.2 - Why I "don't" use the design recipe, etc.
At 10:31 AM +0100 7/13/06, wooks . wrote:
>If someone were to write the 10 commandments of professional
>software development 3 of them would be
>
>Thou shalt not rely on comments in the code
>Thou shalt only use comments where the code requires clarification
>There is no silver bullet development technique - Development
>standards are guidelines not mantras that must be slavishly followed.
>
>The design recipe says otherwise. It insists that I comment every
>function I write even if like sum, length or factorial it would
>appear that such comments are superfluous from the perspective of
>clarification. From what I have seen as far as I have progressed so
>far (chap 21 of HTDP) and as is being reinforced to me the intent is
>that the mantras are slavishly followed.
Let me point out a couple of things.
1) The design recipe in HtDP is written for beginning programmers.
We require beginning programmers to follow the recipe strictly and
slavishly in order to instill habits. (If you allow them to make
their own exceptions too early, they'll just ignore the design recipe
altogether.) Once those habits are instilled and the student has
developed some mature programmer judgment, one can be more flexible.
Indeed, I don't always follow all the steps of the design recipe in
my own programming... but when I get in trouble, and the program
starts to resemble the LaBrea tar pits, I start over with more
explicit attention to contracts, writing test cases first, etc. It
serves to clear my mind so I can resolve these fundamental issues
without worrying about code at the same time. And it serves the same
purpose for my beginning students.
2) I assume that by "comment every function" you mean writing
function contracts? PLT Scheme actually allows you to write function
contracts IN the language, not in comments, so they can be enforced
by the language. I don't teach that in a first-term course, because
it's more syntax to learn, and comment syntax is trivial. In
particular, one of the attractions of the HtDP design recipe for
beginning students is that the first step is non-intimidating -- you
won't get a syntax error from a contract comment. Anyway, since
you're experienced enough not to be intimidated by the syntax, open
the Help Desk and look up contract.ss.
Incidentally, I had a boss (when I was out in semi-private industry)
who actively opposed comments, saying "A comment is something that
the human reader can see and the computer can't, and hence a source
of misunderstanding between them." For that matter, my first-term
college CS prof, who usually required extensive commenting, gave us
one assignment on which we were forbidden to use any comments except
our names, to give us practice writing readable code without relying
on comments. There's a lot to be said for your first two
commandments. (The third too, but that's a separate topic.)
>>... plus, map returns a [Listof Y], while fold returns a Y. So
>>how could we hope to implement map in terms of fold, as you say?
>
>Maybe I should ask the question that struck me immediately I saw
>this problem. Why would I want to do this anyway. Map and fold are
>clearly intuitive one is a translation the other is a summarisation.
>I cannot intuitively see why I would want to implement a translation
>in terms of a summarisation.
Perhaps because you were in an environment that provided one and not
the other? That kind of thing certainly happens in the real world.
Of course, the real, pedagogical reason for the problem is to expand
the reader's conception of higher-order functions and their
applicability. To point out that "fold" CAN be usefully used with S
and T being different types -- exactly the lesson you're trying to
learn.
>>Once again, the X's and Y's in the contract for fold are
>>independent from the X's and Y's for map, and we could instantiate
>>it ...
>>...snip...
>>The names don't matter at all; in fact, we could instead use the
>>names S and T:
>>
>>;; fold : forall S, T . T (S T -> T) [Listof S] -> T
>
>I would more readily accept this with an example of a case where we
>summarised 2 different types of data - but lets see.
Well, "map" is a good example, but that's what you're having trouble
with already. Let's try a more prosaic example: use "fold" to write
a "highest-salary" function on a list of employees. The input is a
list of employees, so S must be "employee"; the output is a number,
so T must be "number".
>Hmmm well all the instantation and matching stuff looks a lot like
>prolog - but no its not crystal clear because the reason why I would
>want to do this is not intuitive.
Bingo! Given the contracts
>>;; map : (X -> Y) [Listof X] -> [Listof Y]
>>;; fold : T (S T -> T) [Listof S] -> T
we need to unify (in the Prolog sense) the inputs [Listof X] and
[Listof S], which is easy, and we need to unify the outputs [Listof
Y] with T. There's a unique solution to these unifications, and it
tells you a lot about how to solve the problem.
>>(Hint: go back write the instantiation of fold for sum, product,
>>_and_ append. Look at the resulting classes of data.)
>
>For sum S and product
> S is a number and T is a list of numbers.
Why is T a list of numbers? Last I checked, sum and product each
returned a number.
>For append S is a list so I suppose T is a list of list?
Why is T a list of lists? append returns a list.
I'm being a little overly simplistic here, insisting that the return
value from the function must unify with T; in practice, you could do
a "fold" and then do something else to the result. But let's start
with the simpler approach in which map (or highest-salary or sum or
product or append) IS merely a call to fold with carefully chosen
arguments; if that doesn't work, we can make things more complicated
later.
--
Stephen Bloch
sbloch at adelphi.edu