I'm glad to see you doing this! FWIW, I would have gone with 1. I would have explained it to the students by saying that an earlier stage of the compiler must do this and now primitives are a different kind of things than other functions.
But not really have tried it, I agree 3 is the next best thing. Robby On Wed, Jan 4, 2012 at 8:55 PM, Jay McCarthy <jay.mccar...@gmail.com> wrote: > > I've just pushed a new version of the GC language for PLAI that > requires students to manually manage their closures, rather than > treating them as "flat" values. > > The code is tested (the entire normal GC test suite is ported) but I > have not yet pushed the documentation (to discourage early use and > confusion from students before we've figured out the best way to > present the issues.) > > *** Why *** > > In the first language, closures are treated as "flat" values, except > that they aren't really flat so students had to use the > 'procedure-roots' function to discover any locations that the closure > held---i.e. its free identifiers. I have always found this really > cheap and I find that students are very confused by when they need to > use this function because it comes out of left field. > > In this language, there are a few new functions the student must > implement: > > gc:closure : code-ptr (vectorof location) -> location > gc:closure-code-ptr : location -> code-ptr > gc:closure-env-ref : location integer -> location > gc:closure? : location -> boolean > > *** Implications *** > > Closures represent a totally different kind of data structure for > students. Flats are fixed size with no pointers inside. Conses are > fixed sized with pointers inside. Closures are variable size with > pointers inside. A representation on the heap is obvious to me: > > 'closure code-ptr n free-var_0 ... free-var_n > > but I have no idea how this will affect how students perform on the > assignment. > > One benefit is that lots of lame M&S allocation routines simply won't > work and students will have to have something more robust. > > *** Issues *** > > There is a bit of trouble in the implementation with regards to > primitive functions like add1, sub1. Here are the approaches I thought > to try: > > 1. Syntactically restrict them to first-order uses. > > (map add1 l) must become (map (lambda (x) (add1 x)) l) > > Pros: No influence on collector/mutator > > Cons: Hacky?, Weird language. > > 2. Allocate all of them with 'native' code pointers and no free-vars > at startup > > Pros: Not hacky, each primitive only counts once > > Cons: Every mutator has a very large minimum heap size > > 3. Allocate them at each use with 'native' code pointers ... > > Pros: A little hacky, Small minimum heap sizes, Pay for what you need > > Cons: Multiple uses count multiple times, Hard to predict minimum > heap sizes > > 4. Allocate each a single time but only if they are used > > Pros: Same as 3 > > Cons: None of 3, but the implementation is not obvious > > Currently I have gone with 3 after doing 1 and finding it > distasteful. Are there options I haven't thought of? What should I > choose? Anyone know how to do 4? > > Jay > > -- > Jay McCarthy <jay.mccar...@gmail.com> > Assistant Professor / Brigham Young University > http://faculty.cs.byu.edu/~jay > > "The glory of God is Intelligence" - D&C 93 _________________________ Racket Developers list: http://lists.racket-lang.org/dev