是否存在类似于Clojure的CLOS(Common Lisp Object System)?
Clojure本身没有对象系统,原因有两个:
但显然,你可以在Clojure中实现对象系统。毕竟,Clojure是图灵完备的。
Mikel Evins正在开发一种名为Categories的新型OO方法。他已经为多种Lisp编写了实现,包括Clojure(尽管并不保证所有移植版本都始终是最新的)。
Categories正在逐渐被Bard替代,这是Mikel正在设计的一种新Lisp方言,其内置了Categories。(然后,Bard可能成为Closos的实现语言,这是Mikel提出的一种设计操作系统的想法。)
Clojure没有CLOS也不想要它,但你可以实现它。
Clojure希望是不可变的,所以拥有可变的面向对象可能有点愚蠢,但你可以拥有一种面向对象的方式。
有了这三个东西,你应该能够满足所有需要,但大多数情况下,最好只使用普通函数和标准数据结构。
https://groups.google.com/forum/?fromgroups=#!topic/clojure/q3PazKoRlKU
一旦您开始构建越来越大的应用程序,命名空间之间开始相互依赖,如果没有大量的依赖关系,单独测试高级组件会变得非常棘手。大多数解决方案涉及函数重新绑定和其他黑魔法,但问题是,在测试时,原始依赖仍在加载->如果您有一个庞大的应用程序,则会成为一个大问题。
(ns redis-ex.history
(:require [taoensso.carmine :as car]
[clojure.string :as st]))
(defmacro wcr [store kdir f & args]
`(car/with-conn (:pool ~store) (:conn ~store)
(~f (st/join "/" (concat [(:ns ~store)] ~kdir)) ~@args)))
(defn empty [store kdir]
(wcr store kdir car/del))
(defn add-instance [store kdir dt data]
(wcr store kdir car/zadd dt data))
(defn get-interval [store kdir dt0 dt1]
(wcr store kdir car/zrangebyscore dt0 dt1))
(defn get-last [store kdir number]
(wcr store kdir car/zrange (- number) -1))
(defn make-store [pool conn ns]
{:pool pool
:conn conn
:ns ns})
所有函数都应该进行测试...这并不新鲜,是标准的Clojure代码。
(ns redis-ex.test-history0
(:require [taoensso.carmine :as car]
[redis-ex.history :as hist]))
(def store
(hist/make-store
(car/make-conn-pool)
(car/make-conn-spec)
"test"))
(hist/add-instance store ["hello"] 100 100) ;;=> 1
(hist/get-interval store ["hello"] 0 200) ;;=> [100]
在观看Misko Hevery的演讲后,我意识到“面向对象”并不是邪恶的,而实际上非常有用。
http://www.youtube.com/watch?v=XcT4yYu_TTs
基本思想是,如果您想构建一个大型应用程序,必须将“功能”(程序的实质部分)与“连线”(接口和依赖项)分开。依赖关系越少越好。send
,它只会在映射中关联了现有键的函数时分派该函数。(defn call-if-not-nil [f & vs]
(if-not (nil? f) (apply f vs))
(defn send [obj kw & args]
(call-if-not-nil (obj kw) obj))
hara.fn
名称空间中),其中包含实现。如果您想要自己实现它,则只需4行代码。
make-store
函数以在地图中添加函数。现在你有了一层间接性。;;; in the redis-ex.history namespace, make change `make-store`
;;; to add our tested function definitions as map values.
(defn make-store [pool conn ns]
{:pool pool
:conn conn
:ns ns
:empty empty
:add-instance add-instance
:get-interval get-interval
:get-last get-last})
;;; in a seperate test file, you can now test the 'OO' implementation
(ns redis-ex.test-history1
(:require [taoensso.carmine :as car]
[redis-ex.history :as hist]))
(def store
(hist/make-store
(car/make-conn-pool)
(car/make-conn-spec)
"test"))
(require '[hara.fn :as f])
(f/send store :empty ["test"])
;; => 1
(f/send store :get-instance ["test"] 100000)
;; => nil
(f/send store :add-instance ["test"]
{100000 {:timestamp 1000000 :data 23.4}
200000 {:timestamp 2000000 :data 33.4}
300000 {:timestamp 3000000 :data 43.4}
400000 {:timestamp 4000000 :data 53.4}
500000 {:timestamp 5000000 :data 63.4}})
;; => [1 1 1 1 1]
因为 make-store
函数构造了一个完全自包含的 store
对象,所以可以定义函数来利用它。
(ns redis-ex.app
(:require [hara.fn :as f]))
(defn get-last-3-elements [st kdir]
(f/send st :get-last kdir 3))
如果你想使用它...你可以这样做:
(ns redis-ex.test-app0
(:use redis-ex.app
redis-ex.history)
(:require [taoensso.carmine :as car]))
(def store
(hist/make-store
(car/make-conn-pool)
(car/make-conn-spec)
"test"))
(get-last-3-elements ["test"] store)
;;=> [{:timestamp 3000000 :data 43.4} {:timestamp 4000000 :data 53.4} {:timestamp 5000000 :data 63.4}]
因此,这样做的真正优点是get-last-3-elements
方法可以在完全不同的命名空间中。它根本不依赖于数据库实现,因此现在仅需要轻量级测试工具即可测试此函数。
然后定义模拟变得微不足道。可以在不加载任何数据库库的情况下测试redis-ex.usecase命名空间。
(ns redis-ex.test-app1
(:use redis-ex.app))
(defn make-mock-store []
{:database [{:timestamp 5000000 :data 63.4}
{:timestamp 4000000 :data 53.4}
{:timestamp 3000000 :data 43.4}
{:timestamp 2000000 :data 33.4}
{:timestamp 1000000 :data 23.4}]
:get-last (fn [store kdir number]
(->> (:database store)
(take number)
reverse))})
(def mock-store (make-mock-store))
(get-last-3-elements ["test"] mock-store)
;; => [{:timestamp 3000000 :data 43.4} {:timestamp 4000000 :data 53.4} {:timestamp 5000000 :data 63.4}]
这是一篇旧帖,但我想回复一下。
Clojure没有面向对象(OO)支持,也没有CLOS支持。环境的底层对象系统仅在互操作性方面提供了基本支持,而不能用于在Clojure中创建自己的类/对象层次结构。Clojure旨在轻松访问CLR或JVM库,但OOP支持到此为止。
Clojure是一种Lisp语言,支持闭包和宏。有了这两个特性,您可以在几行代码中开发一个基本的对象系统。
现在的问题是,在Lisp方言中,您真的需要OOP吗?我会说不需要和需要。不需要,因为大多数问题都可以在任何Lisp中更优雅地解决,而无需对象系统。我会说需要,因为您仍然需要时不时地使用OOP,那么最好提供一个标准的参考实现,而不是让每个极客都去实现它。
我建议您看一下Paul Graham的《On Lisp》书。您可以免费在线查阅。
这是一本非常好的书,真正把lisp的精髓掌握了。你需要稍微调整一下语法到clojure,但概念保持不变。对于你的问题很重要的是,最后一章展示了如何在lisp中定义自己的对象系统。
顺便提一下,clojure支持不可变性。你可以在clojure中创建一个可变的对象系统,但如果你坚持使用不可变性,即使使用OOP,你的设计也会有所不同。大多数标准的设计模式和构造都是以可变性为前提的。
eval
,而我们也可能在计算机上永远没有 Lisp。因此,开始了一段长期的 Lisp 黑客历史,他们不尊重别人所说的正确做法!所以我说,去吧,将 CLOS 移植到 Clojure 上,看看会发生什么。真的,最坏的情况是什么呢? :-) - Ken