【问题标题】:clojure.spec/unform returning non-conforming valuesclojure.spec/unform 返回不一致的值
【发布时间】:2016-08-25 13:54:28
【问题描述】:

在很大程度上,我与 clojure.spec 相处得很好。但是,在处理unform 时,我遇到了一个我无法弄清楚的问题。这是 Hiccup 的一个松散规范,可以让我们动起来:

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

(s/def ::hiccup
  (s/and
      vector?
      (s/cat
        :name       keyword?
        :attributes (s/? map?)
        :contents   (s/* ::contents))))

(s/def ::contents
  (s/or
    :element-seq (s/* ::hiccup)
    :element     ::hiccup
    :text        string?))

现在,在我们得意忘形之前,让我们看看它是否适用于一个小的传递案例。

(def example [:div])

(->> example
     (s/conform ::hiccup))

;;=> {:name :h1}

像魅力一样工作。但是我们可以撤消我们的一致性吗?

(->> example
     (s/conform ::hiccup)
     (s/unform ::hiccup))

;;=> (:div)

嗯,那应该是一个向量。我错过了什么吗?让我们看看规范对此有何评论。

(->> example
     (s/conform ::hiccup)
     (s/unform ::hiccup)
     (s/explain ::hiccup))

;; val: (:div) fails spec: :user/hiccup predicate: vector?
;;=> nil

确实,它失败了。所以问题是:我怎样才能让它正常工作?

【问题讨论】:

  • 我认为不可能。 unform 可以恢复数据的形状,但不能恢复类型……因为没有指定类型,只是一个谓词。
  • @Hoagy 你是否已经尝试过 s/merge 而不是 s/and ?(clojure.github.io/clojure/branch-master/…)
  • @TimothyPratley 但这是正确的行为吗?我想不是。这不是一个直接的模拟,但我认为当前的行为就像unformconformed 上设置(s/and int? pos?) 的值并获得一个浮点数。好像没有错吧?
  • @JeroenvanDijk 我没有,但那是因为 s/merge 用于地图验证规范,而这不是。
  • @HoagyCarmichael 你说得对,现在不可能。和这个问题一样:dev.clojure.org/jira/browse/…。也许你可以投票?

标签: clojure clojurescript


【解决方案1】:

这里回复晚了。我不知道这个问题有多老,但既然我已经写了回复,我不妨提交它。

采用您为 ::hiccup 提供的规范:

(s/def ::hiccup
  (s/and
      vector?
      (s/cat
        :name       keyword?
        :attributes (s/? map?)
        :contents   (s/* ::contents))))

and 中的 vector? 规范将针对该谓词测试输入数据。不幸的是,正如您所经历的那样,它不会变形为矢量。

您可以采取一些措施来解决此问题,即添加一个中间规范,该规范在符合时充当身份,在取消成型时将充当所需的角色。例如

(s/def ::hiccup                                                                                                                                                                                           
  (s/and vector?                                                                                                                                                                                          
         (s/conformer vec vec)                                                                                                                                                                            
         (s/cat                                                                                                                                                                                           
          :name       keyword?                                                                                                                                                                            
          :attributes (s/? map?)                                                                                                                                                                          
          :contents   (s/* ::contents)))) 

(s/unform ::hiccup (s/conform ::hiccup [:div]))

;;=> [:div]

(s/conformer vec vec) 将成为满足vector? 的所有内容的空操作,并使用vec 作为conformer 中的解构函数将确保解构整个and 规范的结果保持为向量。

我是规范自己的新手,这就是我如何让它按您的意图工作的方式。请记住,它可能不是规范设计人员打算使用的方式。

【讨论】:

    【解决方案2】:

    值得一提的是,我制定了一个规范来检查向量并取消格式化为向量:

    (defn vector-spec
      "Create a spec that it is a vector and other conditions and unforms to a vector.
    
      Ex (vector-spec (s/spec ::binding-form))
         (vector-spec (s/* integer?))"
      [form]
      (let [s (s/spec (s/and vector? form))]
        (reify
          s/Specize
          (specize* [_] s)
          (specize* [_ _] s)
    
          s/Spec
          (conform* [_ x] (s/conform* s x))
          (unform* [_ x] (vec (s/unform* s x))) ;; <-- important
          (explain* [_ path via in x] (s/explain s path via in x))
          (gen*  [_ overrides path rmap] (s/gen* s overrides path rmap))
          (with-gen* [_ gfn] (s/with-gen s gfn))
          (describe* [_] (s/describe* s)))))
    

    在你的例子中,你会这样使用它:

    (s/def ::hiccup
      (vector-spec
        (s/cat
          :name       keyword?
          :attributes (s/? map?)
          :contents   (s/* ::contents))))
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2015-08-29
      • 2019-01-28
      • 1970-01-01
      • 2023-02-08
      • 1970-01-01
      • 2016-01-27
      • 2016-02-27
      相关资源
      最近更新 更多