[plt-scheme] Tagging primitive types?

From: Erich Rast (erich at snafu.de)
Date: Thu Dec 1 07:41:36 EST 2005

Hi,

I have a somewhat strange question. Is there any way to tag any kind of 
scheme expression e (symbol, list, numbers, vectors, etc.) with a list 
of other scheme expressions, such that e behaves just like its untagged 
version, but the tags can be mutated and retrieved at any time? (Not 
exactly a *functional* programming idea, I guess)

Examples, where the printing of tags is omitted:

(define a (tag! '(1 2 3) 'my-tag))
a ==> (1 2 3)
(list? a) ==> #t
(car a) ==> 1
(cdr a) ==> (2 3)
(tagged? a) ==> #t
(tags a) ==> (my-tag)
(tag! a 'another-tag)
(tags a) ==> (my-tag another-tag)
(define b (tag a 'third-tag))
(tags b) ==> (my-tag another-tag third-tag)
(tags a) ==> (my-tag another-tag)
(equal? a b) ==> #t
(untag a) ==> (1 2 3)
(tagged? (untag a)) ==> #f

Is that possible?

Best regards,

Erich

------- Here is my 'normal' version of tagged data, but this changes 
every object into a structure, and thus requires me to rewrite every 
accessor procedure and functions like maptree, etc. -------

(module tagged-data mzscheme
   (require (lib "list.ss"))
    (require (lib "struct.ss"))
   (require (prefix srfi1- (lib "1.ss" "srfi"))) ; lists
   (require (prefix srfi13- (lib "13.ss" "srfi"))) ; strings
   (provide (all-defined))

   (define tagged-styled-printing
     (let ((styled? #t))
       (case-lambda
         [() styled?]
         [(bool) (set! styled? bool)])))

   (define (print-tagged e port write?)
     (unless (tagged-styled-printing) (when write? (write-string 
"#<tagged:" port)))
     (cond ((and (tagged? e) (pair? (tags e)))
            (if (tagged-styled-printing) (write-char #\[ port) 
(write-char #\( port))
            ((if write? write display) (car (tags e)) port)
            (for-each
             (lambda (tag)
               (write-char #\SPACE port)
               ((if write? write display) tag port)
               )
             (cdr (tags e)))
            (if (tagged-styled-printing) (write-string "]__" port) 
(write-char #\) port))
            ((if write? write display) (data e) port))
           (else ((if write? write display) (if (tagged? e) (data e) e) 
port)))
     (unless (tagged-styled-printing) (when write? (write-string ">" 
port)))
     )

   (define-struct/properties tagged (data tags)
     ([prop:custom-write print-tagged]))

   (define (tags e)
     (if (tagged? e)
         (tagged-tags e)
         '()))

   (define (data e)
     (if (tagged? e)
         (tagged-data e)
         e))

   (define (tags! e t)
     (if (tagged? e)
         (set-tagged-tags! e t)
         (make-tagged e t)))

   (define (data! e d)
     (if (tagged? e)
         (set-tagged-data! e d)
         (make-tagged e d '())))

   (define (externalize-tagged e)
     (if (tagged? e)
         (list 'tagged (tagged-data e) (tagged-tags e))
         e))

   (define (internalize-tagged e)
     (if (and (pair? e) (equal? (car e) 'tagged))
         (make-tagged (cadr e) (caddr e))
         e))

   ;;; attach tag t to expression e
   (define (tag e t)
     (cond ((tagged? e) (make-tagged (tagged-data e) (cons t 
(tagged-tags e))))
           (else (make-tagged e (list t)))))

   ;;; tag e with arbitrary many data given in rest
   ;;; e.g. (tag* 'test 'a 'b 'c 'd) ==> [a b c d]__test
   (define (tag* e . rest)
     (let loop ((li (reverse! rest))
                (result e))
       (cond ((null? li) result)
             (else (loop (cdr li) (tag result (car li)))))))

   (define (untag e)
     (if (tagged? e)
         (tagged-data e)
         e))

   (define (has-tag? e t)
     (if (tagged? e)
         (member t (tagged-tags e))
         #f))
   )



Posted on the users mailing list.