Clojure spec中的"conformer"函数是什么?

3

clojure.spec.alpha API 中有一个名为 conformer 的宏,其描述如下:

Usage: (conformer f)
       (conformer f unf)

takes a predicate function with the semantics of conform i.e. it should
return either a (possibly converted) value or :clojure.spec.alpha/invalid,
and returns a spec that uses it as a predicate/conformer. Optionally takes
a second fn that does unform of result of first
这对我来说不太清楚,如果不是深奥的话。
它有什么用途?“unformer”(难道不应该是“unconformer”)有什么用途?我想从返回的“conformed value”重新创建原始数据?
更新
经过15分钟的实验,似乎是从“谓词”中创建一个新的“规范”(这个“规范”有什么特别之处吗?)
我试过了。
(require '[clojure.spec.alpha :as s :refer [valid? explain conform conformer]])

; ---
; Using purely clojure.spec.alpha:
; ---

(s/def ::vtx-x float?)  
(s/def ::vtx-y float?)
(s/def ::vertex (s/keys :req [::vtx-x ::vtx-y]))

(type (s/get-spec ::vertex))
;=> clojure.spec.alpha$map_spec_impl$reify__1997

(conform ::vertex { ::vtx-x 1.0 ::vtx-y 2.0 })
;=> #:user{:vtx-x 1.0, :vtx-y 2.0}

(valid? ::vertex { ::vtx-x 1.0 ::vtx-y 2.0 })
;=> true

(conform ::vertex { ::vtx-x 1.0 })
;=> :clojure.spec.alpha/invalid

; ---
; Using my own special sauce predicate function, where the conformed
; value carries additional information ... maybe for a debugging system?
; ---

(defn my-vertex-conformer [v]
   (when-let [ { x ::vtx-x y ::vtx-y } v ]
      (if (and (float? x) (float? y)) 
         [:comment "Vertex conforms!" :something (+ x y) :orig v]
         ; else
         :clojure.spec.alpha/invalid)))

(defn my-vertex-unformer [conf-v] (get conf-v :orig))

(s/def ::my-vertex (conformer my-vertex-conformer my-vertex-unformer))

(type (s/get-spec ::my-vertex))
;=> clojure.spec.alpha$spec_impl$reify__2059

(conform ::my-vertex { ::vtx-x 1.0 ::vtx-y 2.0 })
;=> [:comment "Vertex conforms!" :something 3.0 
;=>         :orig #:user{:vtx-x 1.0, :vtx-y 2.0}]

(valid? ::my-vertex { ::vtx-x 1.0 ::vtx-y 2.0 })
;=> true

(conform ::my-vertex { ::vtx-x 1.0 })
;=> :clojure.spec.alpha/invalid

奖金,令人惊讶的是没有例外,这是一个疏忽吗?
(conformer map?)
;=> #object[clojure.spec.alpha$spec_impl$reify__2059 0x770b843 "clojure.spec.alpha$spec_impl$reify__2059@770b843"]

(type (conformer map?))
;=> clojure.spec.alpha$spec_impl$reify__2059

我真的很喜欢这个规范,显然它也转移到了Elixir:https://github.com/vic/spec - 它最初来自哪里? - David Tonhofer
玩具示例在此处:https://clojuredocs.org/clojure.spec.alpha/conformer - glts
1个回答

3
它的用途是什么?
用于创建一种规范,当使用该规范来符合一个值时,可以返回与其给定值不同的值。
(s/conform str 1)
=> 1   ;; (str 1) returned truthy value; input value unchanged
(s/conform (s/conformer str) 1)
=> "1" ;; returns (str 1)

"unformer"(不应该是 "unconformer")的作用是什么?我猜是用来从返回的 "conformed value" 中重新创建原始数据吗?
是的,unformer 函数可以用来反转由 s/unform 进行的任何更改。
(s/def ::str (s/conformer str #(Integer/parseInt %)))
(s/conform ::str 1)
=> "1"
(s/unform ::str "1")
=> 1

有机会简化您的样例规范:

(defn my-vertex-conformer [v]
  (let [{x ::vtx-x y ::vtx-y} v] ;; don't need validation here
    {:comment "Vertex conforms!" :something (+ x y) :orig v}))

(s/def ::my-vertex
  (s/and ::vertex ;; because the validation is done by (s/and ::vertex ...)
         (s/conformer my-vertex-conformer
                      :orig))) ;; keyword can be used as unform function

(->> {::vtx-x 1.0 ::vtx-y 2.0}
     (s/conform ::my-vertex)
     (s/unform ::my-vertex))
=> {::vtx-x 1.0 ::vtx-y 2.0}

奖励,惊人的没有例外,这是一个疏忽吗?(conformer map?)
不,这里没有问题,虽然使用像map?这样的布尔谓词函数与conformer可能不寻常:
(s/conform (s/conformer map?) {})
=> true
(s/conform (s/conformer map?) [])
=> false

(s/conformer map?)是一个规范,它可以接受任何值,并且如果它是一个映射就符合true,否则为false。

最初,[spec]来自哪里?

“合同”的概念在各种形式上已经存在一段时间了,例如 https://docs.racket-lang.org/guide/contracts.html。还可参见https://en.wikipedia.org/wiki/Design_by_contract


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接