s/multi-spec中的retag参数是什么意思?

Ole*_*Cat 8 clojure multimethod clojure.spec

您能用实例解释retag参数如何影响multi-spec创建?我觉得multi-spec文档难以消化.

Tay*_*ood 5

从文档字符串:

在生成期间使用retag来使用匹配标记重新生成生成的值.retag可以是关键字,dispatch-tag将关联到哪个键,或者生成值和dispatch-tag的fn,它应返回适当重新标记的值.

如果retag是关键字(如规范指南示例中所示),则在此处multi-spec内部创建一个在生成器实现函数中使用的函数.例如,这两个多规范声明在功能上是等效的:

(s/def :event/event (s/multi-spec event-type :event/type))
(s/def :event/event (s/multi-spec event-type
                                  (fn [genv tag]
                                    (assoc genv :event/type tag))))
Run Code Online (Sandbox Code Playgroud)

考虑到指南的示例,传递retag 函数似乎不是一个非常有用的选项,但在multi-spec用于非地图时更有用.例如,如果你想使用multi-specs/cat如符合规范函数参数:

(defmulti foo first)
(defmethod foo :so/one [_]
  (s/cat :typ #{:so/one} :num number?))
(defmethod foo :so/range [_]
  (s/cat :typ #{:so/range} :lo number? :hi number?))
Run Code Online (Sandbox Code Playgroud)

foo取决于第一个arg,取两个或三个args.如果我们multi-spec使用s/cat关键字/标签尝试这种天真,它将无法工作:

(s/def :so/foo (s/multi-spec foo :typ))
(sgen/sample (s/gen :so/foo))
;; ClassCastException clojure.lang.LazySeq cannot be cast to clojure.lang.Associative
Run Code Online (Sandbox Code Playgroud)

这是能够传递retag函数的地方很有用:

(s/def :so/foo (s/multi-spec foo (fn [genv _tag] genv)))
(sgen/sample (s/gen :so/foo))
;=>
;((:so/one -0.5)
; (:so/one -0.5)
; (:so/range -1 -2.0)
; (:so/one -1)
; (:so/one 2.0)
; (:so/range 1.875 -4)
; (:so/one -1)
; (:so/one 2.0)
; (:so/range 0 3)
; (:so/one 0.8125))
Run Code Online (Sandbox Code Playgroud)