[plt-scheme] Was HTDP 21.1.2 - Why I "don't" use the design recipe

From: Felix Klock's PLT scheme proxy (pltscheme at pnkfx.org)
Date: Thu Jul 13 13:50:33 EDT 2006


On Jul 13, 2006, at 5:31 AM, wooks . wrote:

> I am extremely appreciative of all the help I have gotten here but  
> I wish to say something that  I think applies generally to people  
> of my background.
> [[snip]]

I feel the design recipe is targeted at teaching new programmers how  
to write maintainable code, not to teach them how to maintain code  
that has been developed in the wild.

I agree that being able to maintain undomesticated code is a very  
useful skill.  However, I have encountered very few ways to actually  
teach such skills, because undomesticated code is often by its nature  
unmaintainable by any pre-established method.

>> But since I want to actually put content in this post, and since  
>> you  could find this information from Figure 57, I'll fill in the  
>> blank,  using the argument order you gave in your post.
>> ;; fold : X (X Y -> Y) [Listof X] -> Y
>> Now, if you look at these two contracts, they certainly look very   
>> different.  map takes two arguments, fold takes three; 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.

Its an exercise.  But its also something that comes up in practice,  
this act of building a simple function like map on top of a general  
function like fold.

My feeling about this particular exercise is that its meant to test  
your ability to use tools like fold to their full power.  Once you  
see how to implement map (and how incredibly simple it actually  
*is*), hopefully you'll see this "summarizing" power of fold is much  
more general than you might have realized.

>> ...
>> Likewise,
>> ;; fold : forall X, Y . Y (X Y -> Y) [Listof X] -> Y
> Well I had a more conservative
> ;; fold : X (X X -> X) (listof X)  ->  X

This is *too* conservative.  If this were the contract for fold, you  
would not be able to implement map using it.

Not only that, but you would not be able to implement append either,  
if this were the most general contract for fold.

This makes me very curious to see how you chose to implement append,  
and how you decided to instantiate the parameter X to accomplish that  

>> 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 like so:  X:=Num Y:=Num to yield
>> ;; fold : Num (Num Num -> Num) [Listof Num] -> Num
>> 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.
>> This makes it crystal clear that the parameters for fold are  
>> totally  different from the ones for map.
> 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.

I'm not sure what "this" is in the above sentence, so I'll take some  
guesses and answer each one.

* You want to implement map in terms of fold.  Why?  (1) Because its  
a good pedagogical exercise to help you see the power of functions  
like fold.  (2) Because oftentimes you will need to implement  
function A and you really have to layer it on top of function B, for  
whatever reason, and understanding how to adapt B to fit the needs of  
A is important. (3) Because we're testing to see if you understand  
how to work with parametric contracts such as those for map and fold.

* You want to instantiate the parameters in contracts.  Why?  Well,  
this is just my view on things, not official HtDP philosophy, but my  
attitude is that the parameters in these contracts exist solely to be  
instantiated to more specific types of data.  We have no class of  
data named X or S or T; we have things like String and Number and  
[Listof Symbol].  So if you want to use functions like map or fold,  
you need to first take their general contracts and make them more  
specific by plugging in the class of data that you happen to care  
about at the point of invocation (of map or fold).  You don't always  
have to plug in a completely concrete class; you could plug in  
[Listof X] for S, or even just X for S.  The idea is to adapt the  
function you're given (fold) to the needs of the function you are  
trying to write (map).

>> Now my question for you, wooks, is this:
>> We have:
>> ;; map :  (X -> Y) [Listof X] -> [Listof Y]
>> ;; fold : T (S T -> T) [Listof S] -> T
>> So, what class of data could you plug in for the contract  
>> parameter T  to make fold return the same class of data as map?
> T would have to be a list.

"list" is not a class of data.  What kind of list?  A [Listof  
String]?  (Probably not, since the word String doesn't appear  
anywhere in the contract for map...)

>> When you do this  plugging in, what is the resulting instantiated  
>> contract for fold?
> ;fold : list (S list of S -> list of S)

I can't read this.  Either you have the parentheses mixed up in your  
contract, or you're telling me in some manner that fold is not a  
function, but instead a list of functions, which doesn't make sense  
for fold.


Posted on the users mailing list.