请在 2024 Clojure状态调查! 中分享您的想法。

欢迎!请查阅关于页面获取更多关于这个平台如何运作的信息。

0
协议

嗨!

以下是从 honeysql 的一段代码。

(extend-protocol ToSql
  #?(:clj clojure.lang.Keyword
     :cljs cljs.core/Keyword)
  (to-sql [x]
    (let [s (name x)]
      (case (.charAt s 0)
        \% (let [call-args (string/split (subs s 1) #"\." 2)]
             (to-sql (apply call (map keyword call-args))))
        \? (to-sql (param (keyword (subs s 1))))
        (quote-identifier x))))

  .... ;SKIPPED

  #?(:clj Object :cljs default)
  (to-sql [x]
    #?(:clj (add-anon-param x)
       :cljs (if (sequential? x)
               (seq->sql x)
               (add-anon-param x)))))

假设我在调用

(to-sql :column-name)

被调用的方法取决于查找的 顺序 ,因为 Keyword 是 Object 的子类,而 :column-name 是 Keyword 和 Object。

我在 clojure 协议文档中没有找到有关方法查找定义顺序的信息。

另外,我对 extend-protocol 的实现进行了调查,据我理解,在 :impls 中有一个通用的 HashMap 存储所有方法,因此也没有查找顺序的任何保证。

换句话说,扩展协议的规则是什么,对于具有子类关系的类型,或者可能不是这样,我应该是只为没有类型关系的类型扩展协议。

谢谢!

1 答案

+2

被选为最佳答案
 
最佳答案

将选择最具体的匹配项 —— 有时根据关键字和对象实现,关键字实现将被选中。

如果最具体的匹配项是模糊的(同一具体类型扩展了两个接口,这两个接口都是扩展的),所选实现是任意的且未定义的(因此你可能应该避免这样做)。

来源:https://clojure.org/reference/protocols

“如果一个接口是从另一个接口派生的,则使用更派生的接口,否则使用哪个界面是未指定的”


编辑
只是为了澄清。

我想要扩展同一个协议 *重写* 已经在之前的扩展中被扩展的类型,因为 ToSql 协议有一个扩展。

例如,在我使用的 honey sql 库中,我为 Keyword 类型扩展了 ToSql,并且我在自己的代码中也扩展了 Keyword 类型的 ToSql。

这些扩展是如何组合的?我的扩展会覆盖库中定义的扩展吗?

或者这是“同一具体类型扩展了两个接口”的情况,其中 Keyword 是一个扩展类型,ToSql 是一个接口,这个接口两次被同一个类型扩展。

谢谢!
如我所见,extend-protocol 转换为 Keyword 的 extend-type ToSql,并且最新的 extend-type 获胜。

(-reset-methods (alter-var-root (:var proto) assoc-in [:impls atype] mmap))

请确认。

谢谢!
是的,在协议中,某一特定类型的“槽”只有一个,因此你可以通过扩展来替换它。但是,当你这样做时请小心(因为你在替换别人安装的功能)。有关准则,请参阅https://clojure.org/reference/protocols#_guidelines_for_extension
by
作为HoneySQL的维护者,我会担心你在这里尝试覆盖内置的低级类型转换之一——你对HoneySQL处理关键字的方式有何特别需求?
by
嗨,Sean!
HoneySQL非常棒,紧凑且最小化。读它并进行研究对我来说是一种极大的乐趣:)
此外,我还没有玩过你的next.jdbc,但它绝对在我的计划中。

我的任务是以下:
我需要将几个具有相同元数据形状(表和列)的批量插入Honey SQL命令合并为单个jdbc/execute! 调用。
例如:
1. {:insert-into [:my-table]
    :values [{:col1 "str1"
               :col2 "str2"}]}
2. {:insert-into [:my-table]
    :values [{:col1 "str1"
              :col2 nil}]} <--------- 这里我们有nil值

HoneySQL给我的是:
1. "insert into "my-table" ("col1","col2") VALUES (?, ?)"  ["str1" "str2"]
2. "insert into "my-table" ("col1","col2") VALUES (?, NULL)"  ["str1"]   <----- NULL被内联

jdbc/execute! 等待SQL和SQL参数向量,因此我想提交一个SQL语句和SQL参数向量
"insert into "my-table" ("col1","col2") VALUES (?, ?)" 和 [["str1" "str2"] ["str1"]],但要做到这一点,我需要有相同的参数"布局"。

因此我需要覆盖HoneySQL默认的内联'NULL'(和布尔值)行为,将它们提升到SQL参数中。

结果:

1. "insert into "my-table" ("col1","col2") VALUES (?, ?)"  ["str1" "str2"]
2. "insert into "my-table" ("col1","col2") VALUES (?, ?)"  ["str1" nil]

也许你可以提供更准确的方法。
...