如何制作参数化模块

时间:2017-03-31 07:17:26

标签: module racket

我知道在Racket上用“是否有可能”开始提问是一种愚蠢的行为(但我会去)

是否可以创建类似于OCaml仿函数的参数化模块? 假设我有以下结构

;;module A
(define x ...)

(provide foo)

(define (foo a) (format "x for foo: ~a ~a" x a))
(define (bar a) (format "x for bar: ~a ~a" x a))

然后我定义了2个使用模块As定义的其他模块

;;module B

(require "A.rkt" #| '(value for x is "lala") |# )

(define result1 (foo "lulu"))
(define result1 (bar "lili"))

;;result1 => "x for foo: lala lulu"
;;result2 => "x for bar: lala lili"

;;module C

(require "A.rkt" #| '(value for x is "empty") |# )

(define result1 (foo "really"))
(define result1 (bar "nothing"))

;;result1 => "x for foo: empty really"
;;result2 => "x for bar: empty nothing"

显然我可以定义foo和bar,每个都有一个额外的参数, 部分应用该额外的参数并定义新的函数

;;module A2

(provide foo)

(define (foo x a) (format "x for foo: ~a ~a" x a))
(define (bar x a) (format "x for bar: ~a ~a" x a))

然后

;;module B2

(require "A2.rkt")

(define foo1 (partial foo "homemade"))
(define bar1 (partial bar "homemade"))

(define result1 (foo1 "really"))
(define result1 (bar1 "nothing"))

;;result1 => "x for foo: homemade really"
;;result2 => "x for bar: homemade nothing"

但这真的不是我想要的 - 而是我想以某种方式保留模块/需要的方式而不必重新定义 (几乎相同)功能一遍又一遍。

任何想法如何做到这一点?

1 个答案:

答案 0 :(得分:3)

正如Alexis所说,Racket中与ML仿函数最接近的是unit系统。这是您的示例(略微简化)到单位的一种翻译。

首先,定义组件之间接口的签名:

(define-signature in^ (x))
(define-signature out^ (foo bar))

A组件接受in^绑定的值(这里只是x)并生成out^绑定的定义(foo和{{ 1}}):

bar

(define-unit A@ (import in^) (export out^) (define (foo a) (format "x for foo: ~a ~a" x a)) (define (bar a) (format "x for bar: ~a ~a" x a))) 组件实际上需要分为两部分:一个提供B定义的单元和一个使用xfoo功能的单元。我已经简化了后一部分,只是打印出结果,所以我不必创建另一个签名。

bar

在此示例中,可以从签名和单元定义自动推断链接。要将这些组件链接在一起并运行结果,您可以执行以下操作:

(define-unit B-in@
  (import)
  (export in^)
  (define x "lala"))

(define-unit B@
  (import out^)
  (export)
  (printf "~a\n" (foo "lulu"))
  (printf "~a\n" (bar "lili")))

这等同于调用以下复合单元(类似于仿函数应用程序),可以更明确地编写如下:

(invoke-unit/infer (link A@ B-in@ B@))

除了要求Racket推断链接外,您还可以按如下方式明确写出:

(invoke-unit (compound-unit/infer (import) (export) (link A@ B-in@ B@)))

您可以在调用复合单元时从上下文中获取绑定,而不是创建一个显式单元来满足(invoke-unit (compound-unit (import) (export) (link [((B-in : in^)) B-in@] [((A : out^)) A@ B-in] [() B@ A]))) 导入。例如:

in^

另一个重要的特殊形式是(define-unit C@ (import out^) (export) (printf "~a\n" (foo "really")) (printf "~a\n" (foo "nothing"))) (let ([x "empty"]) ;; the in^ bindings come from here! (invoke-unit (compound-unit/infer (import in^) (export) (link A@ C@)) (import in^))) ,它采用单位的出口并将其转换为普通的球拍定义。

define-values/invoke-unit