首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >如何在Clojure中具体实现契约式设计,或者在一般的函数式语言中实现?

如何在Clojure中具体实现契约式设计,或者在一般的函数式语言中实现?
EN

Stack Overflow用户
提问于 2009-12-08 15:39:15
回答 2查看 1.2K关注 0票数 7

我更希望示例是Lisp变体( Clojure或Scheme的加分),因为这是我最熟悉的,但任何关于函数式语言中DBC的反馈对更大的社区来说都是有价值的。

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

代码语言:javascript
复制
(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)

我不喜欢这个实现的原因是,契约逻辑掩盖了核心功能;该函数的真正目的在条件检查中丢失了。这也是我在this question中提出的问题。在Java这样的命令式语言中,我可以使用文档中嵌入的注释或元数据/属性将契约移出方法实现。

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

EN

回答 2

Stack Overflow用户

回答已采纳

发布于 2009-12-10 00:00:57

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

Should I use a function or a macro to validate arguments in Clojure?

票数 4
EN

Stack Overflow用户

发布于 2009-12-08 20:54:13

我可以在Clojure中想象这样的场景:

代码语言:javascript
复制
(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))

示例会话:

代码语言:javascript
复制
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

因此,您可以定义函数,然后随心所欲地定义前置条件和后置条件,而不会弄乱函数逻辑本身。

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

票数 3
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/1865298

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档