Warning: file_get_contents(/data/phpspider/zhask/data//catemap/3/clojure/3.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Functional programming 您如何具体地使用Clojure或一般的函数式语言实现契约式设计?_Functional Programming_Clojure_Scheme_Design By Contract - Fatal编程技术网

Functional programming 您如何具体地使用Clojure或一般的函数式语言实现契约式设计?

Functional programming 您如何具体地使用Clojure或一般的函数式语言实现契约式设计?,functional-programming,clojure,scheme,design-by-contract,Functional Programming,Clojure,Scheme,Design By Contract,我更喜欢Lisp变体(Clojure或Scheme的加分)中的示例,因为这是我最熟悉的,但是关于功能性语言中DBC的任何反馈当然对更大的社区都是有价值的 这里有一个显而易见的方法: (defn foo [action options] (when-not (#{"go-forward" "go-backward" "turn-right" "turn-left"} action) (throw (IllegalArgumentException.

我更喜欢Lisp变体(Clojure或Scheme的加分)中的示例,因为这是我最熟悉的,但是关于功能性语言中DBC的任何反馈当然对更大的社区都是有价值的

这里有一个显而易见的方法:

(defn foo [action options]
    (when-not (#{"go-forward" "go-backward" "turn-right" "turn-left"} action)
              (throw (IllegalArgumentException.
                     "unknown action")))
    (when-not (and (:speed options) (> (:speed options) 0))
              (throw (IllegalArgumentException.
                     "invalid speed")))
    ; finally we get to the meat of the logic)
我不喜欢这个实现,因为契约逻辑掩盖了核心功能;函数的真正用途在条件检查中丢失。这与我在报告中提出的问题相同。在Java这样的命令式语言中,我可以使用文档中嵌入的注释或元数据/属性将契约移出方法实现


有人考虑过在Clojure中将契约添加到元数据中吗?如何使用高阶函数?还有其他选择吗?

我可以想象Clojure中会出现这样的情况:

(defmacro defnc
  [& fntail]
  `(let [logic# (fn ~@(next fntail))]
     (defn ~(first fntail)
       [& args#]
       (let [metadata# (meta (var ~(first fntail)))]
         (doseq [condition# (:preconditions metadata#)]
           (apply condition# args#))
         (let [result# (apply logic# args#)]
           (doseq [condition# (:postconditions metadata#)]
             (apply condition# result# args#))
           result#)))))

(defmacro add-pre-condition!
  [f condition]
  `(do
     (alter-meta! (var ~f) update-in [:preconditions] conj ~condition)
     nil))

(defmacro add-post-condition!
  [f condition]
  `(do
     (alter-meta! (var ~f) update-in [:postconditions] conj ~condition)
     nil))
示例会话:

user=> (defnc t [a test] (a test))
\#'user/t
user=> (t println "A Test")
A Test
nil
user=> (t 5 "A Test")
java.lang.ClassCastException: java.lang.Integer (NO_SOURCE_FILE:0)
user=> (add-pre-condition! t (fn [a _] (when-not (ifn? a) (throw (Exception. "Aaargh. Not IFn!")))))
nil
user=> (t 5 "A Test")
java.lang.Exception: Aaargh. Not IFn! (NO_SOURCE_FILE:0)
user=> (t println "A Test")
A Test
nil
因此,您可以定义函数,然后根据自己的喜好定义前置和后置条件,而不会弄乱函数逻辑本身


如果出现问题,条件函数应该抛出异常。

Clojure已经支持前置和后置条件,但遗憾的是没有很好的文档记录:


您是否了解了plt计划中合同的实施方式?看一看@亚历克赛-这是一个惊人的资源!我对Scheme非常陌生(通过小/老练的书籍工作),我不知道它的存在,所以谢谢你。不是直接回答你的问题,而是看看QuickCheck及其衍生产品(ClojureCheck)。它基本上是基于属性的测试,在合同中定义属性,因此您也可以轻松地获得生成的测试。前/后条件将是Clojure 1.1的正式部分,将在任何一天发布。