How to make a "define" that accepts string in the first parameter in SISC Scheme?

StackOverflow https://stackoverflow.com/questions/23305997

  •  09-07-2023
  •  | 
  •  

Question

Let's call this function "dynamic-define".

Basically I want to write a macro or lambda that works like this:

$ (dynamic-define "variable" 123)
$ variable
$ => 123

I tried it:

(define-syntax dynamic-define
    (syntax-rules ()
      ((_ string <body>)
       (eval `(define ,(string->symbol string) <body>)))))

and it works as expected but doesn't seem to be a good solution.

I tried to use without eval like this:

(define-syntax dynamic-define                                           
    (syntax-rules ()                                                            
      ((_ string <body>)                                                        
       (define (string->symbol string) <body>))))

But when I try to use I get this error:

Error: invalid syntax (define (string->symbol "variable") 123)

What should I do?

(PLEASE: edit this question to fit native English and erase this line please)

Was it helpful?

Solution

You're running against a fundamental problem: you cannot do a real dynamic define in a "clean" way, hence your attempt using eval. Note that the two solutions above are both not dynamic -- all they give you is the ability to write "foo" instead of foo. As I said elsewhere, using eval is usually bad, and in this case it's worse since it's eval that is used from a macro which makes it very weird. This is in addition to having an implicit quasi-quote in your macro that makes things even more confusing. You could just as well define the whole thing as a plain function:

(define (dynamic-define string <body>)
  (eval `(define ,(string->symbol string) ,<body>)))

If you really need this to work, then it's best to take a step back and think about what it is that you need, exactly. On one hand, the above solutions are not dynamic since they require using the macro with a string syntax

(dynamic-define "foo" 123) ; instead of (define foo 123)

but how would it look to have a real dynamic definition that takes in a string? You'd probably expect this to define b:

(define a "b")
(dynamic-define a 123)

but this only becomes more confusing when you consider how it interacts with other bindings. For example:

(define y 123)
(define (foo s)
  (define x 456)
  (dynamic-define s 789)
  (+ x y))

Now, assuming that dynamic-define does what you want it to do, think about what you'd get with (foo "x") and (foo "y"). Worse, consider (foo "s") and (foo "+"). And even worse, what about

(foo (random-element '("x" "y" "s" "+" "define")))

? Clearly, if this dynamic-define really does some dynamic definition, then there is no sense that you can make of this code without knowing ahead of time what name foo will be called with. Without being able to make such sense, compilation goes out the window -- but much more importantly, correctness (or generally, the meaning of your code) dies.

In my experience, this kind of pattern is much better handled with hash tables and similar devices.

OTHER TIPS

using define-macro

#> (define-macro (dynamic-define varstr val)
      `(define ,(string->symbol varstr) ,val))

#> (dynamic-define "variable" 123)
#> variable
123

using syntax-case

#> (define-syntax dynamic-define
     (lambda (stx)
       (syntax-case stx ()
         ((k varstr val)
          (with-syntax ([var (datum->syntax-object 
                              #'k
                              (string->symbol (syntax-object->datum #'varstr)))])
            #'(define var val))))))

#> (dynamic-define "variable" 123)
#> variable
123
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top