【问题标题】:How to make a parametrized module如何制作参数化模块
【发布时间】:2017-03-31 07:17:26
【问题描述】:

我知道在 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 个使用模块作为定义的其他模块

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

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

有什么办法吗?

【问题讨论】:

  • 如被问及,您需要units,它与 ML 仿函数极为相似。但在实践中,您通常可以使用 parameters 或其他更简单的结构,因此单位在实践中并不常用。
  • 我阅读了参数,但我还没有理解它们。也许您可以在上面写一篇超级棒的博客文章;-) 不,非常感谢您的指点
  • 我会写一个真实的答案,但现在,我需要睡觉了。 ;) 如果其他人在我有机会的时候还没有,我会写一些东西。

标签: module racket


【解决方案1】:

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

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

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

A 组件接受 in^ 绑定的值(此处为 x)并为 out^ 绑定(foobar)生成定义:

(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 定义的单元和一个使用foobar 函数的单元。我已经简化了后面的部分,只打印出结果,所以我不必创建另一个签名。

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

这相当于调用下面的复合单元(类似于函子应用程序),可以更明确地写成这样:

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

除了让 Racket 推断链接之外,您还可以如下显式编写:

(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,它接受一个单位的导出并将它们转换成正常的球拍定义。

(let ([x "apple"])  ;; in in^ bindings come from here!
  (define-values/invoke-unit A@ (import in^) (export out^))
  ;; the out^ exports are available for the rest of the let body
  (foo "orange"))

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-06-21
    • 1970-01-01
    • 2016-09-10
    • 2013-08-23
    • 2014-06-21
    • 2018-07-03
    相关资源
    最近更新 更多