如何制作自定义 clojure.spec 生成器?
How do I make custom clojure.spec generator?
我正在尝试指定以下名为 Connection 的数据结构:
{:id "some string" :channel "instance of org.httpkit.server.AsyncChannel" }
这是我的规格:
(defn make-channel []
(proxy [AsyncChannel] [nil nil]
(toString [] "mock AsyncChannel")))
(defn channel-gen
[]
(->> (s/gen (s/int-in 0 1))
(gen/fmap (fn [_] (make-channel)))))
(s/def ::channel (s/spec (::channel-type)
:gen channel-gen))
(s/def ::id string?)
(s/def ::connection (s/keys :req-un [::channel ::id]))
(s/fdef make-connection
:args ::channel
:ret ::connection)
我收到以下错误,我不知道这里出了什么问题:
clojure.lang.ExceptionInfo: Unable to construct gen at: [] for: gameserve.ws$make_connection@788ffa19
clojure.lang.Compiler$CompilerException: clojure.lang.ExceptionInfo: Unable to construct gen at: [] for: gameserve.ws$make_connection@788ffa19 #:clojure.spec.alpha{:path [], :form #object[gameserve.ws$make_connection 0x788ffa19 "gameserve.ws$make_connection@788ffa19"], :failure :no-gen}
我无法重现您的错误,但我想指出一些可能有助于您完成这项工作的事情。
您的 gen/fmap
忽略其参数已经是一回事了:gen/return
.
此处调用不带参数的关键字,这将引发 IllegalArgumentException
。只需删除 ::channel-type
.
周围的括号
(s/def ::channel (s/spec (::channel-type)
:gen channel-gen))
在这里,您正在制作一个讨论单一事物的 args 规范。 :args
始终是一个参数序列,如果函数只接受一个参数,它是一个长度为 1 的序列。您通常会使用 s/cat
.
(s/fdef make-connection
:args ::channel
:ret ::connection)
以下适合我。 (它假设您的频道资料是正确的。)
(ns foo.core
(:require
[clojure.spec.gen.alpha :as gen]
[clojure.spec.alpha :as s]))
(defn make-channel []
:mock-channel)
(defn channel-gen
[]
(gen/return (make-channel)))
(s/def ::channel-type any?)
(s/def ::channel (s/spec ::channel-type
:gen channel-gen))
(s/def ::id string?)
(s/def ::connection (s/keys :req-un [::channel ::id]))
(defn make-connection [c])
(s/fdef make-connection
:args (s/cat :c ::channel)
:ret ::connection)
(comment
(s/exercise ::connection)
;;=> ([{:channel :mock-channel, :id ""} {:channel :mock-channel, :id ""}]
;; [{:channel :mock-channel, :id "k"} {:channel :mock-channel, :id "k"}] ,,,)
(s/exercise-fn `make-connection)
;;=> ([(:mock-channel) nil] [(:mock-channel) nil] ,,,)
)
我正在尝试指定以下名为 Connection 的数据结构:
{:id "some string" :channel "instance of org.httpkit.server.AsyncChannel" }
这是我的规格:
(defn make-channel []
(proxy [AsyncChannel] [nil nil]
(toString [] "mock AsyncChannel")))
(defn channel-gen
[]
(->> (s/gen (s/int-in 0 1))
(gen/fmap (fn [_] (make-channel)))))
(s/def ::channel (s/spec (::channel-type)
:gen channel-gen))
(s/def ::id string?)
(s/def ::connection (s/keys :req-un [::channel ::id]))
(s/fdef make-connection
:args ::channel
:ret ::connection)
我收到以下错误,我不知道这里出了什么问题:
clojure.lang.ExceptionInfo: Unable to construct gen at: [] for: gameserve.ws$make_connection@788ffa19
clojure.lang.Compiler$CompilerException: clojure.lang.ExceptionInfo: Unable to construct gen at: [] for: gameserve.ws$make_connection@788ffa19 #:clojure.spec.alpha{:path [], :form #object[gameserve.ws$make_connection 0x788ffa19 "gameserve.ws$make_connection@788ffa19"], :failure :no-gen}
我无法重现您的错误,但我想指出一些可能有助于您完成这项工作的事情。
您的 gen/fmap
忽略其参数已经是一回事了:gen/return
.
此处调用不带参数的关键字,这将引发 IllegalArgumentException
。只需删除 ::channel-type
.
(s/def ::channel (s/spec (::channel-type)
:gen channel-gen))
在这里,您正在制作一个讨论单一事物的 args 规范。 :args
始终是一个参数序列,如果函数只接受一个参数,它是一个长度为 1 的序列。您通常会使用 s/cat
.
(s/fdef make-connection
:args ::channel
:ret ::connection)
以下适合我。 (它假设您的频道资料是正确的。)
(ns foo.core
(:require
[clojure.spec.gen.alpha :as gen]
[clojure.spec.alpha :as s]))
(defn make-channel []
:mock-channel)
(defn channel-gen
[]
(gen/return (make-channel)))
(s/def ::channel-type any?)
(s/def ::channel (s/spec ::channel-type
:gen channel-gen))
(s/def ::id string?)
(s/def ::connection (s/keys :req-un [::channel ::id]))
(defn make-connection [c])
(s/fdef make-connection
:args (s/cat :c ::channel)
:ret ::connection)
(comment
(s/exercise ::connection)
;;=> ([{:channel :mock-channel, :id ""} {:channel :mock-channel, :id ""}]
;; [{:channel :mock-channel, :id "k"} {:channel :mock-channel, :id "k"}] ,,,)
(s/exercise-fn `make-connection)
;;=> ([(:mock-channel) nil] [(:mock-channel) nil] ,,,)
)