Question

How you can have a different behaviour if a variable is defined or not in racket language?

Was it helpful?

Solution

There are several ways to do this. But I suspect that none of these is what you want, so I'll only provide pointers to the functions (and explain the problems with each one):

  1. namespace-variable-value is a function that retrieves the value of a toplevel variable from some namespace. This is useful only with REPL interaction and REPL code though, since code that is defined in a module is not going to use these things anyway. In other words, you can use this function (and the corresponding namespace-set-variable-value!) to get values (if any) and set them, but the only use of these values is in code that is not itself in a module. To put this differently, using this facility is as good as keeping a hash table that maps symbols to values, only it's slightly more convenient at the REPL since you just type names...

  2. More likely, these kind of things are done in macros. The first way to do this is to use the special #%top macro. This macro gets inserted automatically for all names in a module that are not known to be bound. The usual thing that this macro does is throw an error, but you can redefine it in your code (or make up your own language that redefines it) that does something else with these unknown names.

  3. A slightly more sophisticated way to do this is to use the identifier-binding function -- again, in a macro, not at runtime -- and use it to get information about some name that is given to the macro and decide what to expand to based on that name.

The last two options are the more useful ones, but they're not the newbie-level kind of macros, which is why I suspect that you're asking the wrong question. To clarify, you can use them to write a kind of a defined? special form that checks whether some name is defined, but that question is one that would be answered by a macro, based on the rest of the code, so it's not really useful to ask it. If you want something like that that can enable the kind of code in other dynamic languages where you use such a predicate, then the best way to go about this is to redefine #%top to do some kind of a lookup (hashtable or global namespace) instead of throwing a compilation error -- but again, the difference between that and using a hash table explicitly is mostly cosmetic (and again, this is not a newbie thing).

OTHER TIPS

First, read Eli's answer. Then, based on Eli's answer, you can implement the defined? macro this way:

#lang racket

; The macro
(define-syntax (defined? stx)
  (syntax-case stx ()
    [(_ id)
     (with-syntax ([v (identifier-binding #'id)])
       #''v)]))

; Tests
(define x 3)
(if (defined? x) 'defined 'not-defined) ; -> defined

(let ([y 4])
   (if (defined? y) 'defined 'not-defined)) ; -> defined

(if (defined? z) 'defined 'not-defined) ; -> not-defined

It works for this basic case, but it has a problem: if z is undefined, the branch of the if that considers that it is defined and uses its value will raise a compile-time error, because the normal if checks its condition value at run-time (dynamically):

; This doesn't work because z in `(list z)' is undefined:
(if (defined? z) (list z) 'not-defined) 

So what you probably want is a if-defined macro, that tells at compile-time (instead of at run-time) what branch of the if to take:

#lang racket

; The macro
(define-syntax (if-defined stx)
  (syntax-case stx ()
    [(_ id iftrue iffalse)
     (let ([where (identifier-binding #'id)])
       (if where #'iftrue #'iffalse))]))

; Tests
(if-defined z (list z) 'not-defined) ; -> not-defined

(if-defined t (void) (define t 5))
t ; -> 5

(define x 3)
(if-defined x (void) (define x 6))
x ; -> 3
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top