(python->plt notes) Danny Yoo (dyoo@hkn.eecs.berkeley.edu) This is a set of notes that I'm taking for myself while learning PLT Scheme. I'll try to explain to myself, a Python programmer, some of the things that I needed to learn about PLT scheme. ###################################################################### Note to self: cull through the archives in: http://list.cs.brown.edu/pipermail/plt-scheme/ http://www.cs.utah.edu/plt/mailarch/ There's a lot of material in there, and it's probably worthwhile for someone to organize the pearls. ###################################################################### Simulating Python's 'if __name__ == '__main__': idiom mzscheme provides a command line flag '-C' that, when called on a program, will execute its 'main' function with a given set of args. So we can easily write something like this: ;; square.ss (define (square x) (* x x)) (define (main args) (for-each (lambda (arg) (display (square (string->number arg))) (newline)) (cdr args))) ###################################################################### How to use errortrace to get a program profile. The easiest mistake here is calling PROFILING-ENABLED too late. The issue is that the profiler needs to be loaded and enabled before we import any other code, so put it way up in the program. That is, if we try something like this: (require (prefix p: "some-program-i-want-to-test.ss")) (require (lib "errortrace.ss" "errortrace")) (profiling-enabled #t) (p:run-program) (output-profile-results #t #t) it's way too late, and the profiler won't be able to capture information. So bring errortrace up earlier: (require (lib "errortrace.ss" "errortrace")) (profiling-enabled #t) (require (prefix p: "some-program-i-want-to-test.ss")) (p:run-program) (output-profile-results #t #t) ###################################################################### Notes on Modules ================ [Relocated: I broke this out into its own separate file 'modules.text'] ###################################################################### Notes on PLT papers ============================================ There are a set of really useful papers by the PLT folks that make PLT Scheme less inscrutable. The unfortunate issue is that these research papers appear, at first glance, to be strictly for academics; professional programmers might assume that these papers aren't the right place to look for learning MzScheme. But a lot of the motivation for how MzScheme works live in these dense papers, so we can't just skip them! So as I read through the papers, I'll try to distill what I can. Composable and Compilable Macros -------------------------------- Matthew Flatt's "Composable and Compilable Macros" is about MzScheme's macro system, but it also has a rudimentary tutorial on MzScheme's module system. Much of the design of the module system is motivated by MzScheme's need to get macros right, and the two are tightly intertwined. One problem is that this intertwining means that, to fully understand why MzScheme module act the way they do, we have to learn both modules and macros. And both are big topics. Units: Cool Modules for HOT Languages ------------------------------------- Slightly racy title, but oh well. [Add more here about how this paper is actually useful to really understanding what units try to solve.] [One other major issue is that many of the papers automatically assume that the reader has read all of the previous literature from the past few years. This makes it difficult to jump into the thick of things. Perhaps it is always this way with research. *sigh*] [Must refer to David Parnas, since it seems that his stuff on encapsulation and modules is deeply influential to PLT research, even though many practical programmers of my generation have no clue who he is.] ###################################################################### PLT's unit system appears to present modules as first-class objects, but I have to admit that I don't understand them well enough to write about them. I'm reading about them now: http://download.plt-scheme.org/doc/209/html/mzlib/mzlib-Z-H-41.html ###################################################################### How do we use classes? ###################################################################### How do we use Hashtables? ## fill me in ###################################################################### Pitfalls with error messages: It's very easy to get confused by Scheme's error messages, particularly because of the way syntax objects work. For example, let's say that we have the following example: ;;;;;; (require (lib "unitsig.ss")) (define-signature pair^ (cons car cdr)) (define regular-pair@ (unit-sig pair^ ; ... rest of definition ;;;;;; If we try running this, we'll get an error message regarding syntax: ;;; STDIN::385: pair^: illegal use of syntax in: pair^ ;;; What the system is really trying to say is that pair^ is misplaced, and that's because 'unit-sig' here should really be 'unit/sig': ;;;;;; (require (lib "unitsig.ss")) (define-signature pair^ (cons car cdr)) (define regular-pair@ (unit/sig pair^ ; ... rest of definition ;;;;;; So whenever we see errors that deal with syntax, we really have to look through the code with a fine comb. 'pair^' here is a piece of syntax that depends on another piece of syntax called 'unit/sig', so if either of them are messed up, we'll get errors at compile-time. The page: http://download.plt-scheme.org/chronology/ defines all the changes that have been made between releases. ###################################################################### Playing with structures Structures in PLT Scheme allow us to define our own data types. Let's think of a pair class in Python, for example: class Pair: def __init__(self, x, y): self.x, self.y = x, y Mzscheme allows us to define structures: (define-struct pair (x y)) This automatically defines a few things for us: MAKE-PAIR SET-PAIR-X! SET-PAIR-Y! PAIR-X PAIR-Y which act as our constructors, selectors, and mutators. However, from the interactive prompt, it's not so nice to inspect these things: ###### > (define-struct pair (x y)) > (make-pair 2 17) # ###### A structure by definition is a bit opaque at the toplevel; it wold be nice if displaying the value would show us what's inside. In Python, we'd go ahead and define our own __repr__(); in Mzscheme, there's already a structure printer available, although we have to do two things to make it work automatically: o Turn on PRINT-STRUCT (print-struct #t) http://download.plt-scheme.org/doc/299.100/html/mzscheme/mzscheme-Z-H-14.html#node_sec_14.4 o Allow inspection of a structure This isn't so obvious, because there's the concept of an "inspector" that makes things a little more subtle: the structure must be under the control of an inspector that's subserviant to the current one. http://www.cs.utah.edu/plt/mailarch/plt-scheme-2001/msg00889.html http://download.plt-scheme.org/doc/299.100/html/mzscheme/mzscheme-Z-H-4.html#node_sec_4.5 For example: ###### > (define-struct pair (x y) (make-inspector)) > (make-pair 3 4) #3(struct:pair 3 4) ###### I don't quite understand inspectors yet, and this will be a point I'll have to revisit when I have time. Anyway, this allows us to view structures as unlabeled vectors. That's not so nice. What is nice is that this, in combination with the pconvert standard library module: http://download.plt-scheme.org/doc/299.100/html/mzlib/mzlib-Z-H-28.html#node_chap_28 can make things nicer: > (require (lib "pconvert.ss")) > (define-struct pair (x y) (make-inspector)) > (define converted-list (print-convert (make-pair 7 17))) > converted-list (make-pair 7 17) which is very much what __repr__ does in Python. ###################################################################### What do all of those funny suffixes mean? In lieu of having explicit types associated to names, some Scheme code will take the "hungarian notation" route and add suffixes to make it easier to remember what type a thing is. Here are a common list of those suffixes: % => class <%> => interface @ => unit, especially a signed unit ^ => unit signature ? => boolean ! => procedure with a side effect other than I/O (but not a method, because too many methods have side effects to make the convention useful) *name* => global name (from http://list.cs.brown.edu/pipermail/plt-scheme/2003-September/003431.html) As a few examples: (define person% (class object% (public say-hello!) (define (say-hello!) (printf "hello world~%")) (super-new))) (define (even? x) (= (remainder x 2) 0)) (define (incr-box! box) (set-box! box (+ 1 (unbox box)))) ###################################################################### Questions to myself: o What's the fundamental difference between *namespace-variable-bind* and *define*?