【发布时间】:2014-12-09 23:40:52
【问题描述】:
我是 Clojure 的新手。
在 Java 中,我可以做类似这个非常人为的例子:
public abstract class Foo {
public void sayHello(String name) {
System.out.println("Hello, " + name + "!");
}
}
public interface Fooable {
public void sayHello(String name);
}
public class Bar extends Foo implements Fooable, Barable {
...
}
public class Baz extends Foo implements Fooable, Barable {
...
}
所以,这里我们有两个类,以相同的方式实现 Fooable 接口(通过它们的抽象基类父类)和(可能)以两种不同的方式实现 Barable 接口。
在 Clojure 中,我可以使用 defrecord 来定义 Bar 和 Baz 类型,并让它们实现协议而不是接口(据我了解,从本质上讲,接口就是协议)。我知道如何在最基本的意义上做到这一点,但任何更复杂的事情都难倒我。
鉴于此:
(defrecord Bar [x])
(defrecord Baz [x y])
(defprotocol Foo (say-hello [this name]))
我将如何重新创建上面的抽象基类功能,即在多个 defrecord 类型中以相同的方式实现一个协议,而无需重复代码?我当然可以这样做,但代码重复让我畏缩:
(extend-type Bar
Foo
(say-hello [this name] (str "Hello, " name "!")))
(extend-type Baz
Foo
(say-hello [this name] (str "Hello, " name "!")))
必须有一种更清洁的方式来做到这一点。再说一次,我是 Clojure 的新手(和一般的 Lisp;我正在尝试同时学习 Common Lisp),所以宏对我来说是一个全新的范例,但我想我会尝试一下。毫不奇怪,它失败了,我不知道为什么:
(defmacro extend-type-list [tlist proto fmap]
(doseq
[t tlist] (list 'extend t proto fmap)))
fmap 当然是函数映射,即{:say-hello (fn [item x] (str "Hello, " x "!"))} doseq,应用于记录类型的具体列表和具体协议,确实有效。当然,在宏中,macroexpand 调用不会返回 nil。
所以,我猜问题 1 是“我的宏有什么问题?”。问题 2 是,在没有大量重复样板代码的情况下,我还能如何以编程方式扩展类型的协议?
【问题讨论】: