[plt-scheme] mutable strings

From: Eli Barzilay (eli at barzilay.org)
Date: Mon Aug 21 19:36:19 EDT 2006

On Aug 21, Carl Eastlund wrote:
> 
> You can put mutable objects (including strings) in syntax objects,
> but I don't think this will give you the behavior you want.  I don't
> believe the same object will be returned to the code when it is run.
> You can, however, define a language with a new #%datum transformer
> that produces mutable strings.  The naive approach will produce a
> new mutable copy of the literal string at each evaluation; with a
> little more work I think you could fix it to construct a single
> mutable string once and return a reference to it at each
> evalutation.  [...]

This is easy to do using `begin-lifted'.


On Aug 21, Robby Findler wrote:
> 
> I think he's probably referring to the fact that read produces
> immutable strings:
> 
>   > (immutable? (read (open-input-string "\"abc\"")))
>   #t
> [...]

Or more directly:

  > (define a "foo")
  > (immutable? a)
  #t

and even more directly:

  > (immutable? "foo")
  #t


Something like what Carl suggests can help, writing a new #%datum
syntax that will wrap string literals in something that makes them
mutable:

  > (module foo mzscheme
      (define (mutable s) (printf "making a mutable ~s\n" s) (string-copy s))
      (provide foo)
      (define (foo) (list (mutable "foo") (mutable "bar"))))

The problem with this is that every call to `foo' will make a new
copy:

  > (require foo)
  > (foo)
  making a mutable "foo"
  making a mutable "bar"
  ("foo" "bar")
  > (foo)
  making a mutable "foo"
  making a mutable "bar"
  ("foo" "bar")

The `begin-lifted' construct from etc.ss is a useful tool for doing
such things once.  It is a little similar to using #. in common lisp
(but it is more organized -- it's effect is to add a new binding for
the contents and using it once).  Here's how it can be used:

  > (module foo mzscheme
      (require (lib "etc.ss"))
      (define-syntax mutable
        (syntax-rules ()
          [(_ s) (begin-lifted (printf "making a mutable ~s\n" s)
                               (string-copy s))]))
      (provide foo)
      (define (foo) (list (mutable "foo") (mutable "bar"))))
  > (require foo)
  making a mutable "bar"
  making a mutable "foo"
  > (foo)
  ("foo" "bar")
  > (string-set! (car (foo)) 2 #\i)
  > (foo)
  ("foi" "bar")

Finally, here's how to plug this as a new #%datum transformer:

  > (module mstrings mzscheme
      (require (lib "etc.ss"))
      ;; make this a language module -- provide everything mzscheme
      ;; does, except for a modified #%datum
      (provide (all-from-except mzscheme #%datum)
               (rename my-datum #%datum))
      ;; the modified #%datum puts the copying code in when it
      ;; encounters a string literal, uses `begin-lifted' to do it
      ;; once
      (define-syntax (my-datum stx)
        ;; we need to use `syntax-case' to be able to distinguish uses
        ;; that contain a string literal
        (syntax-case stx ()
          [(_ . s)
           (string? (syntax-e #'s))
           #'(begin-lifted (string-copy (#%datum . s)))]
          [(_ . x) #'(#%datum . x)])))
  > (module foo mstrings
      (provide foo)
      (define (foo) (list "foo" "bar")))
  > (require foo)
  > (foo)
  ("foo" "bar")
  > (string-set! (car (foo)) 2 #\i)
  > (foo)
  ("foi" "bar")

-- 
          ((lambda (x) (x x)) (lambda (x) (x x)))          Eli Barzilay:
                  http://www.barzilay.org/                 Maze is Life!


Posted on the users mailing list.