【问题标题】:Forbidden keys in clojure.specclojure.spec 中的禁止键
【发布时间】:2016-11-03 00:43:13
【问题描述】:

我正在关注clojure.spec guide。我知道在使用 clojure.spec/keys 时可以声明必需和可选属性。

我不明白可选是什么意思。对我来说 :opt 什么也没做。

(s/valid? (s/keys :req [:my/a]) {:my/a 1 :my/b 2}) ;=> true

(s/valid? (s/keys :req [:my/a] :opt []) {:my/a 1 :my/b 2}) ;=> true

该指南承诺会向我解释这一点,“我们稍后会看到可选属性在哪里有用”,但我找不到解释。我可以声明禁止键吗?或者以某种方式声明一组有效键等于 :req 和 :opt 中的键?

【问题讨论】:

    标签: clojure clojure.spec


    【解决方案1】:

    这是一个非常好的问题,clojure.spec API 给出了(公认的、简短且不令人满意的)答案:

    :opt 键用作文档和 可以被生成器使用。

    如果地图包含额外的(我认为这是“禁止”的意思)密钥,我认为您不能使用此方法使地图无效。但是,您可以使用此规范来确保 ::bad-key 不存在:

    (s/def ::m (s/and (s/keys :req [::a]) #(not (contains? % ::bad-key))))
    (s/valid? ::m {::a "required!"})                        ; => true
    (s/valid? ::m {::a "required!" ::b "optional!"})        ; => true
    (s/valid? ::m {::a "required!" ::bad-key "no good!"})   ; => false
    

    您可以使用此规范将键的数量限制为您想要的集合:

    (s/def ::r (s/and (s/keys :req [::reqd1 ::reqd2]) #(= (count %) 2)))
    (s/valid? ::r {::reqd1 "abc" ::reqd2 "xyz"})              ; => true
    (s/valid? ::r {::reqd1 "abc" ::reqd2 "xyz" ::extra 123})  ; => false
    

    不过,处理此 IMO 的最佳方法是简单地忽略存在您不关心的关键礼物。

    希望随着规范的成熟,这些好东西会被添加。或者,也许他们已经在那里(它正在迅速变化)而我根本不知道。这是 clojure 中的一个非常新的概念,因此我们大多数人都有很多东西要学习。

    更新 - 2016 年 12 月 我只是想在写完这 6 个月后重温一下。看起来我最初关于忽略您不关心的键的评论是首选方法。事实上,在我两周前参加的 clojure/conj 会议上,Rich 的主题演讲专门讨论了从功能级别到应用程序级别的所有软件级别的版本控制概念。他甚至在演讲中特别提到了不允许使用密钥的概念,可以在on youtube 找到。他说它是有意设计的,因此只能指定所需的密钥。禁用键确实没有什么好的目的,应该谨慎行事。

    关于:opt 键,我认为最初的答案仍然很好——它是文档,实际上,它允许生成这些可选指定的键:

    (s/def ::name #{"Bob" "Josh" "Mary" "Susan"})
    (s/def ::height-inches (s/int-in 48 90))
    (s/def ::person (s/keys :req-un [::name] :opt-un [::height-inches]))
    
    (map first (s/exercise ::person))
    
    ; some generated data have :height-inches, some do not
    ({:name "Susan"}
     {:name "Mary", :height-inches 48}
     {:name "Bob", :height-inches 49}
     {:name "Josh"}
    

    【讨论】:

    • "禁用键确实没有什么好的目的,应该谨慎行事。"唔。我必须观看演讲才能在上下文中听到它,但是,比如说,检查密码字段是否不会从函数中泄漏出来呢?我想重点是,除了显式密钥之外,您无法真正确保不会以其他方式泄漏它,但是说检查显而易见的东西没有好的目的似乎有点夸大其词,不是吗?跨度>
    • 所以我听了演讲。我看到规范是关于你能做什么,而不是你不能做什么。但他并没有完全说忽略它是首选方式。他说这是两种首选方式之一,另一种是“制定政策”。如果我理解正确,我可以做几件事来不阻止增长:1)定义一个单独命名的规范与“只是忽略”一个使用您显示的方法不允许密钥的规范,我现在可以使用,也许保留永远使用,或稍后放弃,2)编写独立的检查器。他实际上也解决了我的问题:也许你应该使用选择键。
    • 这是另一个需要考虑的用例:我正在解析来自二进制协议的数据。如果数据标头中的一个键与某个值匹配,则应该从标头后面的位中解析出一个附加字段。但是,如果标头与该值不匹配,那么后续位已被消耗到此可选额外字段中将是非常糟糕的。在这种情况下,在我看来,我确实想在这种情况下禁止这个键,因为我想知道我是否解析数据包错误。
    • (我要补充一点,可选键的值也可能是 nil,但我仍然需要强制它要么是 nil,要么不存在。)
    【解决方案2】:

    关于可选键的要点是 如果它们出现在地图中就会被验证

    【讨论】:

    • 那是不真实的。无论如何都会验证该值。
    • 如果键既不是必需的也不是可选的,则根本不会验证该值。我并不是说对于必需的键值没有经过验证
    • “此外,all 命名空间限定键的值将由任何注册的规范验证(并可能解构)。” (s/keys 的文档)
    • 哎呀,我脑子里这么清楚。将答案作为参考。谢谢!
    猜你喜欢
    • 2022-01-02
    • 2017-01-15
    • 1970-01-01
    • 2012-10-30
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-05-28
    • 2023-03-05
    相关资源
    最近更新 更多