在重构时有没有一种检查返回类型的好方法?

时间:2016-01-21 09:52:02

标签: clojure return-type

目前,当我在Clojure中进行重构时,我倾向于使用这种模式:

(defn my-func [arg1 arg2]
  (assert (= demo.core.Record1 (class arg1) "Incorrect class for arg1: " (class arg1))
  (assert (= demo.core.Record1 (class arg2) "Incorrect class for arg2: " (class arg2))
  ...

也就是说,我发现自己手动检查返回类型,以防系统的下游部分将它们修改为我不期望的东西。 (如果我重构并得到一个我不希望的堆栈跟踪,那么我将我的假设表达为不变量,并从那里向前迈进)。

从某种意义上说,这正是Bertrand Meyer预期的那种不变检查。 (Object Oriented Software Construction的作者,以及Design by Contract的想法的支持者。)

挑战在于,直到运行时我都找不到这些。我很乐意在编译时找到它们 - 只需说明函数所期望的内容。

现在我知道 Clojure本质上是一种动态语言。 (虽然Clojure有一个'编译器,但我们应该期望函数值的应用只能在运行时实现。)

我只是想要一个好的模式来使重构更容易。 (即查看将参数更改为函数的所有流动效果,而不会在第一次调用时看到它中断,然后移动到下一个,然后转到下一个断点。)

我的问题是:在重构时是否有一种检查返回类型的好方法?

2 个答案:

答案 0 :(得分:3)

如果我理解你的话,棱柱/架构应该是你的选择。 https://github.com/plumatic/schema

(s/defn ^:always-validate my-func :- SomeResultClass
  [arg1 :- demo.core.Record1
   arg2 :- demo.core.Record1]
  ...)

您应该在发布前关闭所有验证,这样不会影响性能。

core.typed很好,但据我记得,它强制您注释所有代码,而架构只允许您注释关键部分。

答案 1 :(得分:2)

您有几个选项,其中只有一个是"编译" 时间:

<强>测试

由于Clojure是一种动态语言,测试绝对必不可少。重构时,它们是您的安全网。即使在静态类型语言中,测试仍然有用。

前后条件

它们允许您通过向函数添加元数据来验证不变量,例如Michael Fogus' blog中的示例:

(defn constrained-fn [f x]
  {:pre  [(pos? x)]
   :post [(= % (* 2 x))]}
  (f x))

(constrained-fn #(* 2 %) 2)
;=> 4
(constrained-fn #(float (* 2 %)) 2)
;=> 4.0
(constrained-fn #(* 3 %) 2)
;=> java.lang.Exception: Assert failed: (= % (* 2 x)

<强> core.typed

core.typed是此列表中唯一可以进行编译时检查的选项。然后你的例子将表达如下:

(ann  my-func (Fn [Record1 Record1 -> ResultType]))
(defn my-func [arg1 arg2]
...)

这是以运行core.typed作为单独操作的代价为代价的,可能是测试套件的一部分。

仍然在运行时验证/检查领域,还有更多选项,例如bouncerschema