lazy string-append and HTML generation oleg@xxxxxx (09 Feb 2000 20:50 UTC)
|
lazy string-append and HTML generation
Shriram Krishnamurthi
(09 Feb 2000 20:59 UTC)
|
lazy string-append and HTML generation oleg@xxxxxx 09 Feb 2000 20:47 UTC
Indeed, instead of applying string-append to a list of strings, one can "assume" string-append and leave the list as it is. If we need to append more strings, we can concatenate the corresponding lists. Better yet, rather than (append l1 l2) we can (list l1 l2), thus building a tree. When we're completely done, chances are we don't need any string-append. We can walk the whole tree and 'display' in-order. The following is an elaboration of this idea (using Shriram's example and making it more contrived): (SRV:send-reply (p (table (tr (td "Talks ") (td " = ") (td " slides + transitions")) newline ; note, this is a procedure! (and (positive? no-slides) ; Note, #f is allowed and ignored (tr (td) (td "there are " no-slides " slides") (td))) (list) ; an empty list is allowed and ignored ; Note how Scheme comments are allowed in this ; "markup" ))) where: (define (p . x) (list "<P>" x "</P>")) (define (table . x) (list "<TABLE>" x "</TABLE>")) (define (tr . x) (list "<TR>" x "</TR>")) (define (td . x) (list "<TD>" x "</TD>")) the definitions above are so regular that they positively invite macrofication. By default, SRV:send-reply (see appendix below) writes onto the standard input. However, if the current output port happens to be a string port, SRV:send-reply will act as a string-append, or string-concat. However, unlike regular string-append, SRV:send-reply handles a tree rather than a flat list. It allows and forces "promises" (procedural values), it deals intelligently with #f and '(), it handles characters and numbers in addition to strings. This suggests that proposals to extend string-append to take characters, for example, might not be necessary. I use the style above often in my code. Sometimes the whole module is a single application of SRV:send-reply to a large collection of lists, strings, procedure invocations, procedures, etc. This is very efficient as each atom is touched exactly twice (once when it is being put into a cell and another when it is being pulled for display). All actions (displaying, appending, splicing, flattening) are being delayed till the very end (when many of them may turn unnecessary). There is no intermediate garbage created. This is very similar to a monadic style typical of Haskell. One can consider SRV:send-reply being "main", a 'list' a monadic constructor that _denotes_ a string-append action (without actually executing it), and "bind" is, well, 'list' again. As we're dealing with output, all the monads in question are of a type IO (). Definitions of procedures 'p', 'table', etc. can _easily_ be generalized to accept attributes. One can implement any style one prefers: (p (('align "CENTER")) "text" text") or (p (attr align "CENTER") (attr class "myclass") "text" text") or (p align: "CENTER" class: "myclass" "text" text") I'm afraid this really has nothing to do with SRFI-13. I'm grateful to Olin that he tolerated this side discussion for as long as he did. If someone is interested, we can continue on comp.lang.scheme or privately. As to SRFI-13: no, I don't propose to add SRV:send-reply. However, its mere existence and simplicity shows that one doesn't probably need string-concat or generalized string-append. SRFI-13 looks great as it is. SRV:send-reply _might_ be generalized to a "string output" SRFI (similar to a future string input SRFI Olin has alluded to). We can collaborate on elaborating this style (or elaborate on collaborating). Also, W3C has just recommended XHTML, HTML 4.0 expressed in XML. As many people are doing their own HTML processing in Scheme, I wonder if there might be an interest in a unified "(HTML)" specification. Appendix: ; Output the 'fragments' ; The fragments are a list of strings, characters, ; numbers, thunks, #f -- and other fragments. ; The function traverses the tree inorder, writes out ; strings and characters, executes thunks, and ignores ; #f and '() ; The function returns #t if anything was written at all; ; otherwise the result is #f (define (SRV:send-reply . fragments) (let loop ((fragments fragments) (result #f)) (cond ((null? fragments) result) ((not (car fragments)) (loop (cdr fragments) result)) ((null? (car fragments)) (loop (cdr fragments) result)) ((pair? (car fragments)) (loop (cdr fragments) (loop (car fragments) result))) ((procedure? (car fragments)) ((car fragments)) (loop (cdr fragments) #t)) (else (display (car fragments)) (loop (cdr fragments) #t)))))