durandaltheta
durandaltheta

Reputation: 255

How to fix this Scheme coroutine code to run in Racket

I'm attempting to implement coroutines in Racket LISP for a personal learning project using the scheme implementation from this accepted answer. However, when loading my .rkt file into the racket repl I get the following error:

; 3.rkt:111:18: define: not allowed in an expression context
;   in: (define (run-handler) (make-generator (lambda (yield) (send
;     (get-dp-data-object key) run))))

It seems to be complaining about the define(s) in this section of the code:

108 (define-syntax (define-coroutine stx)                                                               
109   (syntax-case stx ()                                                           
110                ((_ (name . args) . body )                                       
111                 #`(define (name . args)                                         
112                     (make-generator                                             
113                       (lambda (#,(datum->syntax stx 'yield))                    
114                         . body))))))                                

According to the accepted answer here this exact error is not shared by Scheme and is unique to Racket when attempting defines in an expression.

The code calling (define-coroutine) seems to be:

518     ;; Qt-esque connect macro                                                   
519     (define-syntax connect-message                                              
520       (syntax-rules ()                                                          
521                     [(register src-obj-key msg-type dst-obj-key handler)        
522                      (register-message-handler                                  
523                        msg-type                                                 
524                        (begin                                                   
525                          (define-coroutine                                      
526                            (handler-accessor)                                                       
527                            (if (eqv? (get-dp-data-object dst-obj-key) #f)       
528                              #f                                                 
529                              (send                                              
530                                (get-dp-data-object dst-obj-key)                 
531                                handler                                          
532                                (get-field args msg))))                          
533                          handler-accessor))]))                                  

This is my first Racket project so I'm learning a lot as I go. The (begin) above is trying to define and return a coroutine that calls an object method. I'm sure that there's lots of problems with this code snippet, but the debugger is blocking me with the above issue, stopping me from getting later errors :)

I am not nearly skilled enough in Racket, Scheme, or LISP to fix this issue, I can hardly understand this error at the moment. Can someone break down the problem for me and hopefully correct the issue so I can get this coroutine code working in Racket?

Upvotes: 1

Views: 358

Answers (1)

Leif Andersen
Leif Andersen

Reputation: 22332

In racket, begin does not create a new scope.1 This means that anything you define inside of a begin form is still in scope outside of that form. It also means that begin doesn't change the context, and so if you are in an expression context, begin keeps that.

The define form has (roughly) the following grammar:

(define <id> <expr>)

Where <id> is the variable name, and <expr> is an expression. However, the define form itself is not an expression, so something like this is invalid:

(define x (define y 5))

And because begin doesn't change the context, this is invalid too:

(define x
  (begin
    (define y 5)
    y))

Instead, you can use let to create a new scope. And because let itself is an expression, you can put it in define. So you could write something like:

(define x
  (let ()
    (define y 5)
    y))

And now, x is bound to 5 as expected.

Taking this back to your original question, you had the code:

518     ;; Qt-esque connect macro                                                   
519     (define-syntax connect-message                                              
520       (syntax-rules ()                                                          
521                     [(register src-obj-key msg-type dst-obj-key handler)        
522                      (register-message-handler                                  
523                        msg-type                                                 
524                        (begin                                                   
525                          (define-coroutine                                      
526                            (handler-accessor)                                                       
527                            (if (eqv? (get-dp-data-object dst-obj-key) #f)       
528                              #f                                                 
529                              (send                                              
530                                (get-dp-data-object dst-obj-key)                 
531                                handler                                          
532                                (get-field args msg))))                          
533                          handler-accessor))]))                                  

I'm assuming register-message-handler is a function, and therefore requires an expression. But you have define-coroutine, who's elaboration is a define form. So rather than using begin, you can use a let to turn it into an expression, giving you something like this:

(register-message-handler                                  
  msg-type                                                 
  (let ()                                                   
    (define-coroutine (handler-accessor) ....)
    handler-accessor))                                

1Bad design decision...I know. Water under the bridge from many decades back. :(

Upvotes: 4

Related Questions