clojure:我可以定义隐式转换的可能性吗?

时间:2013-10-28 10:02:52

标签: clojure protocols implicit-conversion coalescing

我有一个名为IExample的协议,我定义了一个实现它的记录类型A

(defprotocol IExample
  (foo [this] "do something")
  (bar [this] "do something else"))

(defrecord A [field1 field2]
  IExample
  (foo [this]
    (+ field1 field2))
  (bar [this]
    (- field1 field2)))

假设我想扩展另一个(基本)类型B来实现此协议,但我知道如何从B转换为A

 (defn B-to-A
   "converts a B object to an A object"
   [Bobj] ...)

因为我有这个转换,我可以委托IExample协议的所有调用 B上的IExample A协议授权{/ p>}:

(extend B
  IExample {
    :foo (fn [this] (foo (B-to-A this)))
    :bar (fn [this] (bar (B-to-A this)))})

然而,这似乎是一个非常多的样板(特别是对于更大的协议) 这不是clojure-idiomatic。

我怎样才能告诉clojure每次都隐含地将B转换为A 使用IExample函数在B对象上调用B-to-A函数?

2 个答案:

答案 0 :(得分:2)

就样板文件而言,您可以编写一些宏来为您编写所有样板文件。另一方面,您可以在这里再次查看您的设计。

我们这里有3件事(类型):ABIExample。然后我们在这些事物之间有两种关系:1)a-to-example : A -> IExample 2)b-to-a : B -> A从中我们可以通过使用组合来获得第三关系,即compose b-to-a with a-to-example : B -> IExample。现在,如果我们尝试将此设计转移到协议,我们会发现它不是一个简单的翻译,因为协议不会像上面设计中讨论的那样直接组成,而是我们可以使用如下所示的中间协议IToExample

(defprotocol IExample
  (foo [this] "do something")
  (bar [this] "do something else"))

(defprotocol IToExample
  (to-example [this] "convert to IExample"))

(defrecord A [field1 field2]
  IExample
  (foo [this]
    (+ field1 field2))
  (bar [this]
    (- field1 field2))
  IToExample
  (to-example [this] this))

(deftype B [])
(defn b-to-a [b] (A. ....))
(extend B
  IToExample {:to-example b-to-a})

我们做了什么,我们在设计中将-> IExample表示为具有一个函数的IToExample协议。所以我们得到了:

  • a-to-example : A -> IExample通过实施IToExample for A
  • 通过正常功能
  • b-to-a : B -> A
  • compose b-to-a with a-to-example : B -> IExample通过为B实施IToExample并使用b-to-a。

答案 1 :(得分:1)

这取决于。如果查看clojure核心seq函数,您可能会注意到ISec接口只有4个方法,并且整个“公共”序列库由(更多)调用(some-internal-function (seq argument))的函数定义 - 并且他们往往被明确记录为也这样做。从概念上讲,有一个像IExample接口这样的协议,以及一个描述seq函数的附加协议,可以从某种类型转换为实现ISeq的东西。

如果数据类型只需要实现几个方法(因此IExample可能很小),并且作用于协议的算法数量很大(因为您可以按常规编写所有这些方法),这是一个特别有用的策略函数)。