I'm new to Clojure.
In Java I can do something like this extremely contrived example:
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 {
...
}
So, here we have two classes, implementing the Fooable interface the same way (through their abstract base class parent) and (presumably) implementing the Barable interface two different ways.
In Clojure, I can use defrecord to define types Bar and Baz, and have them implement protocols rather than interfaces (which, essentially, are what protocols are, from what I understand). I know how to do this in the most basic sense, but anything more complex is stumping me.
Given this:
(defrecord Bar [x])
(defrecord Baz [x y])
(defprotocol Foo (say-hello [this name]))
how would I recreate the abstract base class functionality above, i.e. have one protocol implemented the same way across multiple defrecord types, without duplicating code? I could of course do this, but the code repetition makes me cringe:
(extend-type Bar
Foo
(say-hello [this name] (str "Hello, " name "!")))
(extend-type Baz
Foo
(say-hello [this name] (str "Hello, " name "!")))
There has to be a cleaner way of doing this. Again, I'm new to Clojure (and Lisp in general; I'm trying to learn Common Lisp concurrently), so macros are an entirely new paradigm for me, but I thought I'd try my hand at one. Not surprisingly, it fails, and I'm not sure why:
(defmacro extend-type-list [tlist proto fmap]
(doseq
[t tlist] (list 'extend t proto fmap)))
fmap of course is a map of functions, i.e. {:say-hello (fn [item x] (str "Hello, " x "!"))} The doseq, applied to a concrete list of record types and a concrete protocol, does work. Within the macro, of course, it doesn't, macroexpand calls return nil.
So, question 1, I guess, is "what's wrong with my macro?". Question 2 is, how else can I programmatically extend protocols for types without a lot of repetitive boilerplate code?