我知道在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"
但这真的不是我想要的 - 而是我想以某种方式保留模块/需要的方式而不必重新定义 (几乎相同)功能一遍又一遍。
任何想法如何做到这一点?
答案 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
定义的单元和一个使用x
和foo
功能的单元。我已经简化了后一部分,只是打印出结果,所以我不必创建另一个签名。
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