I have the following pattern that I am trying to implement: I have an AST and 2 Generators, one for C# and one for VB. A generator takes the provided AST as input and generates source code in the selected language. First I define an abstraction for a generator:
(defprotocol Generator
(generate-code [this ast]))
Then I give an implementation, one for C# and one for VB (I will just give the C# implementation because the VB one is almost exactly the same):
(defn ^:private gen-csharp-class [ast]
(str "class " (:id ast) " { }"))
(defn ^:private gen-csharp [ast]
(cond
(instance? AstClass ast) (gen-csharp-class ast)))
(defrecord AGenerator []
Generator
(generate-code [this ast] (gen-csharp ast)))
I don't like the use of instance?
in the gen-csharp
function. Is there a more idiomatic way of writing this type of dispatch in Clojure?
You could reverse the abstraction:
(defprotocol CodeGenerator
(generate-class [this ast])
;; + other generator methods
)
(defrecord VBGenerator []
CodeGenerator
(generate-class [this ast] (gn-vb-class ast)))
(defrecord CSharpGenerator []
CodeGenerator
(generate-class [this ast] (gn-chsharp-class ast)))
and then write the instance?
calls only once:
(defn gen-code [generator ast]
(cond (instance? AstClass ast) (generate-class generator ast)
;; + other generator cases...
))
or you could use multimethods on the second argument for gen-class. Each multimethod definition would call the correct method from CodeGenerator
:
(defmulti gen-class (fn [_ ast] (type ast)))
(defmethod gen-class AstClass [generator ast]
(generate-class generator ast))