[racket] user's guide feedback/questions as I prepare for my PL course
[long message, all skippable, and you can skim sections of less interest]
Background, part 1:
This Fall I'll be teaching "my" undergrad PL course for the first time
since Spring 2008. I'll be using Racket for 2-3 weeks of the course,
both as an excellent example of a modern, dynamically typed,
functional language with a trivial syntax, and as a vehicle for
several specific topics, including various scope definitions, dynamic
"vs" static typing, generative structs, very basic macros, some module
stuff (particularly getting ADTs by putting a struct definition behind
the interface), streams, and memoization (using mutation, likely
mcons, for those last two). The course also uses SML (before) and
Ruby (after).
The course is ten weeks long (quarter system) and lower-level (after
two quarters of introductory programming in Java). I quickly retreat
to these facts when justifying why many great, crucial topics don't
fit in the course. :) My most recent offering is here:
http://www.cs.washington.edu/education/courses/cse341/08sp/
Usually other instructors teach the course. Only one follows my
approach; for the rest we have this rough "peace agreement" (needs
light updating, basically to reflect Racket != Scheme):
http://www.cs.washington.edu/education/syllabi/341.html
As a lower-level course, it's mostly "programming" rather than
"languages" if you will.
Background, part 2:
Much has changed and improved in Racket since 2008, so I've been
reading the User's Guide in preparation. I am very, very happy, and
in fact plan to use the User's Guide as the primary resource for
students beyond my own lecture notes/readings. I'll be updating all
my materials to bring them up-to-speed to "modern racket", though as a
lower-level course, most of "modern racket" isn't relevant. I
submitted a "bug report" about something yesterday that quickly turned
into a fantastic, interesting discussion that more properly belongs on
this list. So I'm going to summarize the discussion so far before
bringing up a couple new topics.
Summary of topics so far:
1. My initial complaint was that andmap and ormap are misnamed because
they are [short-circuiting] folds, not maps. Yet the User's Guide
does not justify their names and the Reference Guide goes further and
says they are "like map". But after further discussion, I concede
that they are "like map" in a way that could be included in the docs:
ignoring short-circuiting they are (list (f elt1) ... (f eltn)) where
list is replaced by and or or. Another way to think about it is, they
are /not/ maps, but the name andmap is saying "apply and to the map of
f to the elements", and I simply wasn't parsing the identifier that
way.
2. "Whatever you don't like you can fix by making your own language
for the course:" This is indeed a fantastic feature of Racket, but one
I plan not to use. There are advantages of sticking to full Racket,
showing them a modern, well-documented language, without me tweaking
it.
3. "You should really show them some typed racket."
a. "It avoids the common misconception that static typing must
mean Hindley-Milner" -- if only that were the misconception I'm
working on, rather than "static typing must mean int[] and C or Java"
:) In all seriousness, even if I left them with the impression that
Racket was like its fully dynamic predecessors with no notion of a
typed language or a contract system, you shouldn't be (too) mad at me.
I surely leave them with equally egregious misimpressions of SML and
Ruby since I'm not trying to "teach them the languages", though I am
imparting the skill of being able to do something interesting and
useful in a language you had never seen a week ago.
b. "you can show them an untyped/typed interaction that is
eye-opening for the better students and shows them something truly on
the cutting edge of PL research" -- now this is very intriguing and
sounds like a great addition to the lecture on static/dynamic typing.
Hopefully Shriram (to call him out by name) will reply to the list
with a fleshed out description of the example he uses for everyone's
benefit -- I can probably connect the dots from what he said, but
maybe not as well.
4. "Instead of streams, you should give them a proper sense of
programming with laziness and lazy Racket is the way to do this": I've
made a conscious decision not to teach them programming with laziness.
I do teach idioms for delaying evaluation and memoization, and how
using these correctly usually requires purity, but I couch it all in a
strict evaluation model. Standard excuse: I have only 10 weeks,
including their first taste of functional programming.
5. "Use boxes instead of mcons, they're more like SML references
anyway": Or I might use mutable structs instead of mcons. We do so
little with SML references (one call-back example) they won't see the
connection either way. The point is I need to do some "safe updates"
for things like memoization. Mutable pairs work pretty well there.
New topics (more later as I'm not done reading):
6. In reading the User's Guide, I was pretty surprised that first
(unlike car) raises an error on a non-list. I eventually looked over
at the Reference Guide where I learned that list? is O(1) and I was
able to connect the dots -- "aha, now /this/ is a pretty cool reason
to make cons cells immutable beyond the basic 'set-cdr! is nasty' ".
I recommend the User's Guide include a comment or footnote that first
is an O(1) operation -- you can just point to the Reference Guide for
any details.
7. I'm having trouble understanding modules and the rules about
re-definitions. I need to re-read Chapter 6 for a third time, cook up
from examples, and post a full message just on this topic. I think
the User's Guide docs are partly to blame for my confusion. To pick
on two examples I'm ready to discuss:
a. Section 4.2 reports, "A module-level define can bind only
identifiers that are not already bound within the module. For example,
(define cons 1) is a syntax error in a racket module, since cons is
provided by racket." Whether this is true perhaps depends on what you
mean by "a racket module". A module written in the racket language
seems to have no trouble re-defining cons internally at its top-level:
#lang racket
(define cons +)
(cons 1 2) ; 3
b. The contracts chapter reports, "To experiment with multiple modules
within a single module or within DrRacket’s definitions area, use the
racket/load language. The contents of such a module can be other
modules (and require statements), using the longhand parenthesized
syntax for a module (see The module Form)." This would have been very
good to know in the Modules chapter, or at the very least an explicit
comment that racket modules can't contain other modules. (Sorry if I
missed it.) That said, for simplicity, I'm tempted for my class to
stick to racket and simply use multiple files for my modules examples
-- if nothing else, it helps indicate that Racket isn't a toy language
where everything goes in one file.
--Dan