在Clojure中,我如何在自己的记录和类型上实现标准的Clojure集合接口?

18

我希望创建一个抽象,代表一个数据库表,但可以使用所有常规的Clojure seq、conj等操作。我需要添加协议吗?


1
一个记录向量作为表示不足以满足需求,这是有原因的吗?(我的意思是认真提问,而非挖苦)。所有闭包API都可以使用这样的抽象。从Clojure的基本原理http://clojure.org/rationale中可以看到:“让100个函数操作一个数据结构比让10个函数操作10个数据结构更好。”- Alan J. Perlis - Alex Stoddard
原因是当我将一个项目添加到向量中时,我希望该项目能够自动持久化到数据存储中,而没有任何方法可以隐藏它,除非重新实现conj或cons或向量的添加函数。 - yazzapps.com
3
处理这种事情时一定要非常小心。如果你的数据库不是不可变的,那么你基本上就是在向Clojure撒谎,这可能会非常快地出错。大部分Clojure的设计都建立在集合不可变的概念上,因此对于某些函数的执行方式做了很多假设。例如,在集合上使用conj操作不应该改变原始集合,而应该返回一个新的集合。违反这个规则可能会导致Clojure的函数以非常奇怪的方式失败。 - Timothy Baldridge
1个回答

15

是的。这个协议是由Java接口 clojure.lang.ISeq 定义的。你可能想要扩展 clojure.lang.ASeq,它提供了一个抽象实现。

这里有一个例子:一个可关闭的资源的 seq 抽象,在 seq 结束时自动关闭。(未经严格测试)

(deftype CloseableSeq [delegate-seq close-fn]
  clojure.lang.ISeq
    (next [this]
      (if-let [n (next delegate-seq)]
        (CloseableSeq. n close-fn)
        (.close this)))
    (first [this] (if-let [f (first delegate-seq)] f (.close this)))
    (more [this] (if-let [n (next this)] n '()))
    (cons [this obj] (CloseableSeq. (cons obj delegate-seq) close-fn))
    (count [this] (count delegate-seq))
    (empty [this] (CloseableSeq. '() close-fn))
    (equiv [this obj] (= delegate-seq obj))
  clojure.lang.Seqable 
    (seq [this] this)
  java.io.Closeable
    (close [this] (close-fn)))

那么我使用extendprotocol语法吗? - yazzapps.com
更新了答案并附上了一个示例。 - Abhinav Sarkar
4
seq 的实现似乎不正确,因为当在空序列上调用 seq 时,seq 应该返回 nil,但这里你只是返回原始序列。 - Shantanu Kumar

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接