【问题标题】:clojure.spec human readable shape?clojure.spec 人类可读的形状?
【发布时间】:2017-07-09 20:11:08
【问题描述】:

使用 clojure.spec,有没有办法为嵌套地图定义更“人类可读”的规范?以下内容读起来不太好:

(s/def ::my-domain-entity (s/keys :req-un [:a :b])
(s/def :a (s/keys :req-un [:c :d]))
(s/def :b boolean?)
(s/def :c number?)
(s/def :d string?)

鉴于符合实体的形状类似于

{:a {:c 1 :d "hello"} :b false}

我的抱怨是,如果规范有任何类型的嵌套映射或任何深层结构,阅读规范就会变得困难(呃)……因为您在文件中上下移动键,而它们不是“就地”声明.

为了比较,类似 schema 的东西允许更易读的嵌套语法,紧密反映实际数据形状:

(m/defschema my-domain-entity {:a {:c sc/number :d sc/string} :b sc/bool})

这可以在 clojure.spec 中完成吗?

【问题讨论】:

标签: clojure clojure.spec


【解决方案1】:

spec 的价值主张之一是它不尝试定义实际的模式。它不会将实体的定义绑定到其组件的定义。引用spec rationale:

大多数用于指定结构的系统将键集的规范(例如映射中的键、对象中的字段)与这些键指定的值的规范混为一谈。 IE。在这种方法中,map 的模式可能会说 :a-key 的类型是 x-type 而 :b-key 的类型是 y-type。这是僵化和冗余的主要来源。

在 Clojure 中,我们通过动态组合、合并和构建地图来获得力量。我们经常处理可选和部分数据、由不可靠的外部源产生的数据、动态查询等。这些映射表示相同键的各种集合、子集、交集和并集,并且通常对于相同的键应该具有相同的语义。它被使用。定义每个子集/联合/交集的规范,然后冗余地说明每个键的语义是一种反模式,在大多数动态情况下是不可行的。

所以要直接回答这个问题,不,spec 没有提供这种类型的规范,因为它是专门设计的。您需要在类似于模式的定义中牺牲一定程度的人类可读性,以换取更加动态、可组合、灵活的规范。

尽管这不是您的问题,但请考虑使用将实体定义与其组件定义分离的系统的好处。这是人为的,但考虑定义一辆车(保持简单以节省空间,只使用轮胎和底盘):

(s/def ::car (s/keys :req [::tires ::chassis]))

我们定义一次,我们可以在上面放任何我们想要的轮胎配置:

(s/def ::tires (s/coll-of ::tire :count 4))

(s/def ::tire (s/or :goodyear ::goodyear}
                    :michelin ::michelin))

(s/def ::goodyear #{"all-season" "sport" "value"})
(s/def ::michelin #{"smooth ride" "sport performance"})

(s/def ::chassis #{"family sedan" "sports"})

以下是不同的配置,但都是有效的汽车:

(s/valid? ::car {::tires ["sport" "sport" "sport" "sport"]
                 ::chassis "sports"})

(s/valid? ::car {::tires ["smooth ride" "smooth ride"
                          "smooth ride" "smooth ride"]
                 ::chassis "family sedan"})

这是人为的,但可以清楚地看到,将组件定义为与组件组合在一起形成的组件分开是很灵活的。轮胎有自己的规格,它们的规格并不是汽车的定义,即使它们是汽车的部件。它更冗长,但更灵活。

【讨论】:

  • 感谢您的详细解释。我已经阅读了 clojure.spec/guide,尤其是上面引用的段落,但直到现在还没有完全理解。+1
  • 我想我的误解与我的特定实体的形状有关......主要是:kwd -> predicate-fn如(def ::a string?)的平面图,感觉很冗长。在上面的示例中,集合、seqs 和 kwd 对更多样化,事情看起来更真实。
猜你喜欢
  • 2016-07-05
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-09-07
  • 1970-01-01
  • 2010-12-27
  • 2011-09-14
  • 2018-06-12
相关资源
最近更新 更多