You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用clojure.spec指定类apply函数的规格

在Clojure.spec中实现泛型风格的函数约束

问题背景

现有这个apply函数:

(defn apply [f v]
  (f v))

我们需要给它定义规格,要求:

  • 入参v必须符合函数f的入参规格
  • apply的返回值必须和f的返回值规格一致

之前写的规格过于宽松,完全没体现这种关联约束:

(s/fdef apply
  :args (s/cat :f (s/fspec :args (s/cat :v any?) :ret any?))
  :ret any?)

本来想用量化谓词argument?和returned?来实现关联约束,类似这样的伪代码:

(s/fdef apply
  :args (s/cat :f (s/fspec :args (s/cat :v argument?) :ret returned?))
  :ret returned?)

但这种参数化写法在Clojure.spec里无法直接使用,想知道有没有可行的实现方式。

实现方案

可以实现,根据你使用的Clojure.spec版本,有两种对应方式:

方式一:适用于Clojure.spec 1(clojure.spec.alpha)

通过自定义验证逻辑,从f的规格中提取对应规则来验证v和返回值:

(require '[clojure.spec.alpha :as s])

(s/fdef apply
  :args (s/cat :f (s/fspec :args (s/cat :v any?) :ret any?)
               :v (s/arg (fn [args v]
                           (let [f-spec (s/get-spec (:f args))
                                 v-spec (-> f-spec :args :v)]
                             (s/valid? v-spec v)))
                         "v必须匹配f的入参规格"))
  :ret (s/ret (fn [args ret-val]
                (let [f-spec (s/get-spec (:f args))
                      ret-spec (:ret f-spec)]
                  (s/valid? ret-spec ret-val)))
              "返回值必须匹配f的返回值规格"))

方式二:适用于Clojure.spec 2(clojure.spec.alpha2)

spec 2支持直接关联同一fdef内的规格,写法更简洁,类似泛型的参数化效果:

(require '[clojure.spec.alpha2 :as s])

(s/fdef apply
  :args (s/cat :f (s/fspec :args (s/cat :v ::arg) :ret ::ret)
               :v ::arg)
  :ret ::ret)

说明

  • 第一种方式里,s/arg和s/ret允许我们基于整个入参集合(args)做自定义验证,动态提取f的规格规则来检查v和返回值。
  • 第二种方式利用spec 2的新特性,用命名关键字::arg和::ret自动绑定f的fspec里对应的规格,实现了和Java泛型类似的参数化约束。

内容的提问来源于stack exchange,提问作者MPaga

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.05 04:21:19