【问题标题】:Dynamically loading modules in Racket在 Racket 中动态加载模块
【发布时间】:2020-10-20 11:20:08
【问题描述】:

对于一个项目,我正在创建一个类driver%,它应该是提供相同过程的不同模块的抽象层。该类将使用指定要使用的模块的参数进行初始化。

此外,我希望driver% 公开相同的过程但没有副作用,以方便使用驱动程序的单元测试类。

考虑以下几点:

module_one.rkt
#lang racket
(provide foo)
(define (foo)
  (display "called from "module one"))
module_two.rkt
#lang racket
(provide foo)
(define (foo)
  (display "called from "module two"))
driver.rkt
#lang racket
(require (prefix-in one: "module_one.rkt")
         (prefix-in two: "module_two.rkt"))

(provide driver%)

(define driver%
  (class object%
         (super-new)
         (init driver-choice)
         (define choice driver-choice)
         (define/public (foo)
           (case choice
             [(1) (one:foo)]
             [(2) (two:foo)]
             [else void]))))       

这满足了上述要求,但不是很优雅:对于每个公开的过程,都必须添加另一个 case 表达式。这似乎没有必要,因为 API 的选择是在类被实例化时做出的,所以任何地方的选择都是一样的。

对于这个问题,更可接受的解决方案是什么?我已经研究过使用local-require,但这似乎不适用于define/public

非常感谢!

【问题讨论】:

    标签: racket


    【解决方案1】:

    您可以使用dynamic-require 动态地要求模块。您还可以定义一个宏来减少重复代码。

    但首先,请注意完全可以避免使用class

    ;; module-one.rkt
    #lang racket
    (provide foo bar)
    (define (foo) (displayln "called foo from module-one"))
    (define (bar) (displayln "called bar from module-one"))
    
    ;; module-two.rkt
    #lang racket
    (provide foo bar)
    (define (foo) (displayln "called foo from module-two"))
    (define (bar) (displayln "called bar from module-two"))
    
    ;; driver.rkt
    #lang racket
    
    (define ((make-driver choice) method-name)
      (case choice
        [(1) ((dynamic-require "module-one.rkt" method-name))]
        [(2) ((dynamic-require "module-two.rkt" method-name))]
        [else (void)]))
    
    (define a-driver (make-driver 1))
    (a-driver 'foo)
    (a-driver 'bar)
    
    (define b-driver (make-driver 2))
    (b-driver 'foo)
    (b-driver 'bar)
    

    输出:

    called foo from module-one
    called bar from module-one
    called foo from module-two
    called bar from module-two
    

    如果你真的想使用class,这里有一种可能性:

    ;; driver.rkt
    #lang racket
    
    (require syntax/parse/define)
    
    (define-simple-macro (driver 
                           #:modules ([mod-id mod-path] ...)
                           #:methods (methods ...))
      (class object% (super-new)
        (init-field driver-choice)
        (begin
          (define/public (methods)
            (define method-name 'methods)
            (case driver-choice
              [(mod-id) ((dynamic-require mod-path method-name))]
              ...
              [else (void)])) ...)))
    
    (define driver%
      (driver
       #:modules ([1 "module-one.rkt"] [2 "module-two.rkt"])
       #:methods (foo bar)))
    
    (define a-driver (new driver% [driver-choice 1]))
    (send a-driver foo)
    (send a-driver bar)
    
    (define b-driver (new driver% [driver-choice 2]))
    (send b-driver foo)
    (send b-driver bar)
    

    输出:

    called foo from module-one
    called bar from module-one
    called foo from module-two
    called bar from module-two
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-11-10
      相关资源
      最近更新 更多