clojure 中比使用实例更惯用的模式?按类型分派
More idiomatic pattern in clojure than using instance? to dispatch on type
我尝试实现以下模式:我有一个 AST 和 2 个生成器,一个用于 C#,一个用于 VB。生成器将提供的 AST 作为输入并生成所选语言的源代码。首先我为生成器定义一个抽象:
(defprotocol Generator
(generate-code [this ast]))
然后我给出一个实现,一个是C#的,一个是VB的(我只给出C#的实现,因为VB的几乎一模一样):
(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)))
我不喜欢在 gen-csharp
函数中使用 instance?
。在 Clojure 中是否有更惯用的方式来编写这种类型的调度?
你可以反转抽象:
(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)))
然后只写一次 instance?
调用:
(defn gen-code [generator ast]
(cond (instance? AstClass ast) (generate-class generator ast)
;; + other generator cases...
))
或者你可以在 gen-class 的第二个参数上使用 multimethods。每个多方法定义都会从 CodeGenerator
:
调用正确的方法
(defmulti gen-class (fn [_ ast] (type ast)))
(defmethod gen-class AstClass [generator ast]
(generate-class generator ast))
或者,您也可以只使用无协议的多方法。
(defmulti gen-code (fn [language ast] [language (type ast)]))
此处第一个参数将是表示目标语言的关键字,但是,您也可以使用类型。
;; csharp definitions
(defmethod gen-code [:csharp AstClass] [_ ast]
(gen-chsharp-class ast))
;; vb definitions
(defmethod gen-code [:vb AstClass] [_ ast]
(gen-vb-class vb))
(defmethod gen-code [:vb AstNumber] [_ ast]
...)
...
然后用节点调用gen-code:
(gen-code :vb (new AstClass ...))
我尝试实现以下模式:我有一个 AST 和 2 个生成器,一个用于 C#,一个用于 VB。生成器将提供的 AST 作为输入并生成所选语言的源代码。首先我为生成器定义一个抽象:
(defprotocol Generator
(generate-code [this ast]))
然后我给出一个实现,一个是C#的,一个是VB的(我只给出C#的实现,因为VB的几乎一模一样):
(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)))
我不喜欢在 gen-csharp
函数中使用 instance?
。在 Clojure 中是否有更惯用的方式来编写这种类型的调度?
你可以反转抽象:
(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)))
然后只写一次 instance?
调用:
(defn gen-code [generator ast]
(cond (instance? AstClass ast) (generate-class generator ast)
;; + other generator cases...
))
或者你可以在 gen-class 的第二个参数上使用 multimethods。每个多方法定义都会从 CodeGenerator
:
(defmulti gen-class (fn [_ ast] (type ast)))
(defmethod gen-class AstClass [generator ast]
(generate-class generator ast))
或者,您也可以只使用无协议的多方法。
(defmulti gen-code (fn [language ast] [language (type ast)]))
此处第一个参数将是表示目标语言的关键字,但是,您也可以使用类型。
;; csharp definitions
(defmethod gen-code [:csharp AstClass] [_ ast]
(gen-chsharp-class ast))
;; vb definitions
(defmethod gen-code [:vb AstClass] [_ ast]
(gen-vb-class vb))
(defmethod gen-code [:vb AstNumber] [_ ast]
...)
...
然后用节点调用gen-code:
(gen-code :vb (new AstClass ...))