你能提供一些示例吗?
reify
是对于 defrecord
,就像 fn
对于 defn
一样。
简单来说,协议是数据类型应该支持的函数列表,记录是数据类型,而 reification 则是匿名数据类型。
也许这有点啰嗦,但是如果不了解协议和类型/记录,就无法具体理解 reify
:协议是一种使用相同名称的函数(例如 conj
),但在给定不同参数时实际上会以不同方式工作的方法 ((conj [:b :c] :a) => [:b :c :a]
,但是 (conj '(:b :c) :a) => (:a :b :c)
)。 记录类似于对象或类型(但它们的行为类似于映射,使它们更棒)。
更基本地,目标是解决“表达式问题”,即具有能够 轻松添加新类型数据以与现有函数一起使用,并能够轻松地添加新函数以与现有数据无缝配合工作的能力。
因此,有一天你对自己说:“自己,你应该学习成为一只鸭子意味着什么!” 于是你写了一个协议:
(defprotocol Quacks
(quack [_] "should say something in ducky fashion"))
但这一切都太抽象了,所以你要“实际化”它:
(def donald (reify Quacks
(quack [_] "Quacks and says I'm Donald")))
现在终于可以体验你的创作了:
(quack donald) => "Quacks and says I'm Donald"
然后你想起了达菲鸭:
(def daffy (reify Quacks
(quack [_] (str "Quacks and says I'm Daffy"))))
(quack daffy) => "Quacks and says I'm Daffy"
但是当你想起Huey时,你意识到了自己的错误,并以可重复使用的方式定义了鸭子:
(defrecord Duck [name]
Quacks
(quack [_] (str "Quacks and says I'm " name)))
创建new
鸭子(有几种方法可以做到):
(def huey (->Duck "Huey"))
(def duey (Duck. "Duey"))
(def louie (new Duck "Louie"))
(quack huey) => "Quacks and says I'm Huey"
记住,记录就像地图一样(感谢协议!):
(:name huey) => "Huey"
但是你记得鸭子必须要 又 嘎嘎叫 又 走路,所以你写了另一个协议:
(defprotocol Walks
(walk [_] "should walk like a duck"))
并扩展“duck”的定义
(extend-type Duck
Walks
(walk [_] "waddle waddle"))
(walk louie) => "waddle waddle"
现在我们可以扩展其他类型以实现相同的协议(教相同的函数如何与其他事物一起工作):
所以让我们假设我们也想让程序员嘎嘎叫 :-)
(defrecord Programmer [] Quacks
(quack [_] "Monads are simply monoids in a category of endofunctors..."))
(quack (Programmer.)) => "Monads are simply monoids in a category of endofunctors..."
我推荐这篇解释协议的文章,以及这篇关于reify的解释和《Clojure for the Brave and True》中关于协议的章节。
免责声明:这只是为了初步理解协议,而不是如何使用它们的最佳实践。 “嘘!我回答这个问题主要是为了教自己,因为直到昨天我从未真正编写过自己的协议/接口!”
因此,虽然我希望它可以增强其他人的学习,但我非常欢迎批评或编辑建议!
reify
宏允许创建一个匿名类,该类扩展 java.lang.Object
类和/或实现指定的接口/协议。 API 文档 没有清楚地描述其目的,而是提供了该宏执行的技术细节。Java 互操作文档 提供了对此目的的简要描述:
从 Clojure 1.2 开始,reify 还可用于实现接口。
在 数据类型文档中可以找到更多信息,其中提供了非常详细的说明,说明了它是如何工作并与 proxy
进行比较:
虽然 deftype 和 defrecord 定义了命名类型,但 reify 定义了匿名类型并创建了该类型的实例。使用情况是当您需要一次性实现一个或多个协议或接口,并希望利用本地上下文时。在这方面,它类似于 Java 中的 proxy 或匿名内部类。
reify 的方法体是词法闭包,并且可以引用周围的局部范围。reify 与 proxy 的不同之处在于:
仅支持协议或接口,不支持具体的超类。方法体是结果类的真实方法,而不是外部函数。对实例上的方法的调用是直接的,而不是使用 map 查找。不支持动态交换方法映射中的方法。结果比 proxy 更好的性能,在构建和调用方面都是如此。只要其约束条件不是禁止性的,reify 在所有情况下都优于 proxy。
reify
扩展除 Object
以外的类,因此您的第一句话略有不正确。 - Nathan Davis