[plt-scheme] mutable strings
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!