From www@deja.com Tue Jan 16 22:48:52 2001 Message-ID: <943fq5$8qa$1@nnrp1.deja.com> From: oleg@pobox.com Subject: Re: XML as Scheme, Scheme as XML; XEXPR considered invalid Date: Wed, 17 Jan 2001 06:58:14 GMT Reply-To: oleg@pobox.com Newsgroups: comp.lang.scheme,comp.text.xml References: <9360rf$o8i$1@nnrp1.deja.com> X-Article-Creation-Date: Wed Jan 17 06:58:14 2001 GMT Status: OR This article will consider XML syntax for Scheme and compare it with XEXPR. The latter is defined in a W3C technical note. The comparison is not fair as XEXPR is an invalid proposal: e.g., it contradicts its own DTD. As it turns out, an SXML pretty-printer is general enough to permit a well-defined XML syntax for Scheme. This is not trivial as the following Scheme expression may show: (begin (append a (x y) '(b) '(c "d" 1 (f g) '(h i) `g () '() (()) j))) It exhibits a great deal of context-sensitivity. What looks like an application may in reality be something else, if it is preceded by a quote. A quote within a quote looses its special meaning. An XML syntax should be able to capture these nuances. In article , Ray Blaak wrote: > In general, I suggest to not define new XML elements "on the fly", > but rather have a static set of primitive Scheme XML elements > (perhaps following the scheme grammar), with new user > definitions being the data of the appropriate element. > Eg. the above example could maybe give: > > > string-join > foobarbaz > : > > The following procedure does exactly as you wish: (SRV:send-reply (post-order (with-input-from-string "(begin (string-join '(\"foo\" \"bar\" \"baz\") \":\"))" read) bind4)) where bind4 is defined in Appendix and the SXML pretty-printer is defined in an earlier message in this thread. A few other examples follow. As before, we use a 'post-order' function with 'bind4' bindings as defined in the appendix to convert from a Scheme expression to XML. We use a SSAX parser to turn XML into SXML. Scheme: (begin (string-join '() ":" 'suffix)) XML: string-join :suffix SXML: (begin (apply (var "string-join") (list) (str ":") (qsymb "suffix"))) This expression can indeed be evaluated in Scheme if we define str as an identity function, qsymb as string->symbol, and var be a special form that looks up the value of its argument in the current lexical environment: (define-macro (var arg) (string->symbol arg)). We also assume a little bit extended syntax of apply (as some implementations support). Scheme: (begin (append a (x y) '(b) '(c "d" 1 (f g) '(h i) `g () '() (()) j))) XML: append axy b c d1f g quoteh i quasiquote g quote j Note that "" represents a list made of an empty list. SXML: (begin (apply (var "append") (var "a") (apply (var "x") (var "y")) (list (qsymb "b")) (list (qsymb "c") (str "d") (num "1") (list (qsymb "f") (qsymb "g")) (list (qsymb "quote") (list (qsymb "h") (qsymb "i"))) (list (qsymb "quasiquote") (qsymb "g")) (list) (list (qsymb "quote") (list)) (list (list)) (str "j")))) Again, this is something that we can evaluate. Scheme: (begin (((x 1) (y 2)) #f)) XML: x1 y2 SXML: (begin (apply (apply (apply (var "x") (num "1")) (apply (var "y") (num "2"))) (false))) Finally, consider a factorial function: (begin (define fact (lambda (n) (if (positive? n) (* n (fact (- n 1))) 1))) (fact 5)) Here's one possible expression of this function in XML. fact n positive?n *n fact -n1 1 fact5 It must be stressed that unlike XEXPR, the above XML code can indeed be validated to a great extent. For example, an element always contains either two or three children elements. This fact can be written in DTD. In contrast, XEXPR defines 'if' as . This welcomes a user to apply 'if' to as many children elements as he likes, and no XML parser can detect an error. As a matter of fact, XEXPR can hardly be validated at all. XEXPR examples given in the XEXPR technical note are invalid. For instance, XEXPR note mentions "1 2 3". OTH, XEXPR DTD defines The 'add' element is defined to have the element content. Appearance of character data in "1 2 3" is a gross violation (see Section 3, Validity Constraint "Element Valid" and Section 3.2.1 of the XML Recommendation). It is sad that W3C lets an individual publish XML proposals that violate their own DTD. XEXPR has graver problems: it is not formally defined. For example, the body of the proposal evades scoping issues. According to XEXPR, it is possible to define functions within functions and return nested functions as the result of an outer function evaluation. What is the environment for binding free variables in such a return value? Is it lexical or dynamic? The XEXPR proposal says nothing on this subject, which makes the proposal ill-defined. In a stark contrast, XML snippets considered in this and earlier article are another syntactical form for Scheme -- the language that is well-defined in R5RS. Therefore, if one insists on processing XML in XML, the present article offers _a_ well-specified way. The language and the syntax are rigorously defined, the resulting XML documents can be validated by a parser. We can transform the SSAX parser into the XML form by pretty-printing its code with the procedures outlined above. Thus we will truly get an XML code that can parse and transform itself. Appendix ; shallow unpromise (works only one level deep) (define (unpromise arg) (cond ((procedure? arg) (arg)) ((pair? arg) (map (lambda (arg) (if (procedure? arg) (arg) arg)) arg)) (else arg))) (define (do-eval arg) (cond ((procedure? arg) (arg)) ((not (pair? arg)) arg) ((pair? (car arg)) (cons (list "" (do-eval (car arg)) "\n") (do-eval (cdr arg)))) (else (cons (do-eval (car arg)) (do-eval (cdr arg)))))) ; A compiler from a subset of Scheme into XML (define bind4 `( ; first handle special forms (quote ((*default* ; re-define the default binding . ,(lambda (trigger . value) (cons (lambda () (list "" trigger "\n")) value))) (quote ; quote in quote loses its meaning . ,(lambda (trigger . value) (cons (lambda () "quote") value)))) . ,(lambda (trigger . value) (letrec ((listify (lambda (val) (cond ((pair? val) (lambda () (list "" (unpromise (map listify val)) "\n"))) ((null? val) (lambda () "")) ((procedure? val) val) (else (error "wrong value: " val)))))) (apply listify value)))) (begin . ,(lambda (trigger . value) (list "" (do-eval value) ""))) (if . ,(lambda (trigger . values) (lambda () (list "" (do-eval values) "\n")))) (lambda . ,(lambda (trigger . values) (lambda () (list "\n" (unpromise (car values)) "\n" (do-eval (cdr values)) "\n")))) (define . ,(lambda (trigger . values) (lambda () (list "" (unpromise (car values)) (do-eval (cdr values)) "\n")))) (*text* . ,(lambda (trigger value) (lambda () (cond ((number? value) (list "" value "")) ((eq? value #t) (list "")) ((eq? value #f) (list "")) (else (list "" value "")))))) (*default* . ,(lambda (trigger . values) ;(cerr "trigger: " trigger (if (procedure? values) "proc" values) nl) (cons (lambda () (list "" trigger "")) values))) ))