如何修复此Scheme协程代码以在Racket中运行

时间:2018-08-05 01:43:16

标签: scheme lisp racket

我正在尝试使用this accepted answer中的方案实现在Racket LISP中为个人学习项目实现协程。但是,将我的.rkt文件加载到球拍repl时,出现以下错误:

; 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))))

似乎在代码的这一部分中抱怨定义:

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))))))                                

根据accepted answer here,Scheme不会共享此确切错误,并且在尝试在表达式中定义时对于Racket是唯一的。

调用代码(定义协程)似乎是:

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))]))                                  

这是我的第一个Racket项目,所以我正在学习很多。上面的(开始)试图定义并返回一个调用对象方法的协程。我确定此代码段存在很多问题,但调试器将上述问题拒之门外,使我免于以后出现错误:)

我对Racket,Scheme或LISP的理解不够熟练,目前我几乎无法理解该错误。有人可以帮我解决问题,并希望更正此问题,以便我可以在Racket中使用此协程代码吗?

1 个答案:

答案 0 :(得分:4)

在球拍中,begin不会创建新的范围。 1 这意味着您在begin表单内定义的任何内容仍在该表单之外的范围内。这也意味着begin不会更改上下文,因此,如果您处于表达式上下文中,则begin会保留该上下文。

define格式(大致)具有以下语法:

(define <id> <expr>)

<id>是变量名,<expr>是表达式。但是,define表单本身是不是表达式,因此类似这样的内容是无效的:

(define x (define y 5))

并且因为begin不会更改上下文,所以这也是无效的:

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

相反,您可以使用let创建一个新的范围。并且由于let本身是一个表达式,因此可以将其放在define中。因此,您可以编写如下内容:

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

现在,x已按预期绑定到5

回到原来的问题,您得到了以下代码:

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))]))                                  

我假设register-message-handler是一个函数,因此需要一个表达式。但是您有define-coroutine,详细说明的是define形式。因此,除了使用begin之外,您还可以使用let将其转换为表达式,从而获得如下效果:

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

1 错误的设计决策...我知道。几十年前的桥下水。 :(