为什么#:fallbacks 中的代码不能引用泛型方法?

Why can't code in #:fallbacks refer to the generic methods?

此代码:

(require racket/generic)

;; A holder that assigns ids to the things it holds. Some callers want to know the
;; the id that was assigned when adding a thing to the holder, and others don't.
(define-generics holder
  (add-new-thing+id holder new-thing) ;returns: holder id  (two values)
  (add-new-thing holder new-thing)    ;returns: holder
  #:fallbacks
  [(define (add-new-thing holder new-thing)  ;probably same code for all holder structs
     (let-values ([(holder _) (add-new-thing+id holder new-thing)])
       holder))])

产生此错误消息:

add-new-thing+id: method not implemented in: (add-new-thing+id holder new-thing)

我可以通过在回退中添加 define/generic 来修复它,如下所示:

  #:fallbacks
  [(define/generic add add-new-thing+id)
   (define (add-new-thing holder new-thing)
     (let-values ([(holder _) (add holder new-thing)])
       holder))])

但这似乎增加了复杂性而没有增加价值,我不明白为什么一个有效而另一个无效。

据我了解#:fallbacks,其思想是泛型定义可以从最原始的方法中构建方法,因此实现泛型接口的结构并不总是需要重新实现相同的大集合通常只使用相同代码调用核心方法的方法——但如果需要,它们可以覆盖那些 "derived" 方法。说,为了优化。这是一个非常有用的东西*——但我是否误解了回退?

回退代码无法引用泛型方法似乎很奇怪。回退的主要价值不是调用它们吗? define/generic 的文档说在结构定义的 #:methods 子句之外调用它是一个语法错误,所以我可能误用了它。无论如何,有人可以解释 #:fallbacks 子句中代码的规则是什么吗?你应该怎么写?

* Clojure 世界有类似的东西,在 potemkin 库的 def-abstract-typedeftype+ 中,但没有很好地集成到语言中。 potemkin/def-map-type 很好地说明了为什么回退(无论如何,据我所知)是如此有价值的功能。

您的代码的第二个版本是正确的。

如果您有 add-new-thing+id 的回退定义,那么您的代码的第一个版本可以工作,但是因为您指的是回退范围之外该方法的任何可能定义,所以您需要导入它。


不得不在回退子句中再次定义泛型实际上感觉有点重复。这是因为 #:fallbacks#:methods 的工作方式相同,因此具有使用自己的定义覆盖泛型的相同行为。

为了明确表示你正在覆盖一个方法,你需要在你的子句中 "import" 它,使用 define/generic (这不是真的定义任何东西,它只是将泛型导入上下文。

正如 define/genericdocumentation 所说:

When used inside the method definitions associated with the #:methods keyword, binds local-id to the generic for method-id. This form is useful for method specializations to use generic methods (as opposed to the local specialization) on other values.

然后在define-generics:

The syntax of the fallback-impls is the same as the methods provided for the #:methods keyword for struct.

这意味着 #:fallbacks 与在结构中使用 #:methods 具有相同的行为。

为什么?

该行为背后的逻辑是方法定义块,如 #:methods#:fallbacks 可以访问它们自己的所有泛型定义,因此很容易引用您自己的上下文。要在此上下文之外显式使用泛型,您需要使用 define/generic.