Clojure:从映射创建记录时确保数据完整性?

Clojure:从映射创建记录时确保数据完整性?,clojure,Clojure,我正在学习Clojure并享受它,但发现记录中的一个不一致之处让我困惑:为什么在创建新记录时默认的映射构造函数(map->Whatever)不检查数据完整性?例如: user=> (defrecord Person [first-name last-name]) #<Class@46ffda99 user.Person> user=> (map->Person {:first-name "Rich" :last-name "Hickey"}) #user.Perso

我正在学习Clojure并享受它,但发现记录中的一个不一致之处让我困惑:为什么在创建新记录时默认的映射构造函数(map->Whatever)不检查数据完整性?例如:

user=> (defrecord Person [first-name last-name])
#<Class@46ffda99 user.Person>
user=> (map->Person {:first-name "Rich" :last-name "Hickey"})
#user.Person {:first-name "Rich" :last-name "Hickey"}
user=> (map->Person {:first-game "Rich" :last-name "Hickey"})
#user.Person {:first-game "Rich" :first-name nil :last-name "Hickey"}
user=>(defrecord Person[名字/姓氏])
#
user=>(map->Person{:名字“Rich”:姓“Hickey”})
#user.Person{:名字“Rich”:姓“Hickey”}
user=>(地图->人物{:第一个游戏“Rich”:姓“Hickey”})
#user.Person{:first game“Rich”:first name nil:last name“Hickey”}
我相信映射不需要定义记录定义中的所有字段,它还可以包含不属于记录定义的额外字段。此外,我知道我可以定义自己的构造函数来包装默认构造函数,我认为可以使用
:post
条件来检查正确(全面)的记录创建(没有成功地使其工作)

我的问题是:是否有一种惯用的Clojure方法在从地图构建记录的过程中验证数据?还有,关于唱片我有什么遗漏吗


谢谢。

我认为您的全面性要求已经相当具体,因此我所知的任何内置内容都无法涵盖这一点

现在可以做的一件事是使用clojure.spec为构造函数提供一个
s/fdef
(然后插入它)


(如果为
::first name
::last name
定义了规范,那么这些规范也将被检查。)

我认为您的全面性要求已经非常具体,因此我所知的任何内置内容都没有涵盖这一点

现在可以做的一件事是使用clojure.spec为构造函数提供一个
s/fdef
(然后插入它)

(如果为
::first name
::last name
定义了规范,则也将检查这些规范。)

另一个选项是创建一个包装“构造函数”函数,指定允许的密钥。例如:

(def FooBar {(s/required-key :foo) s/Str (s/required-key :bar) s/Keyword})

(s/validate FooBar {:foo "f" :bar :b})
;; {:foo "f" :bar :b}

(s/validate FooBar {:foo :f})
;; RuntimeException: Value does not match schema:
;;  {:foo (not (instance? java.lang.String :f)),
;;   :bar missing-required-key}
第一行定义了一个仅接受以下映射的架构:

{ :foo "hello"  :bar :some-kw }
您的包装构造函数将类似于:

(def NameMap {(s/required-key :first-name) s/Str (s/required-key :last-name) s/Str})

(s/defn safe->person 
  [name-map :- NameMap]
  (map->Person name-map))

另一个选项是创建一个包装器“构造函数”函数,指定允许的键。例如:

(def FooBar {(s/required-key :foo) s/Str (s/required-key :bar) s/Keyword})

(s/validate FooBar {:foo "f" :bar :b})
;; {:foo "f" :bar :b}

(s/validate FooBar {:foo :f})
;; RuntimeException: Value does not match schema:
;;  {:foo (not (instance? java.lang.String :f)),
;;   :bar missing-required-key}
第一行定义了一个仅接受以下映射的架构:

{ :foo "hello"  :bar :some-kw }
您的包装构造函数将类似于:

(def NameMap {(s/required-key :first-name) s/Str (s/required-key :last-name) s/Str})

(s/defn safe->person 
  [name-map :- NameMap]
  (map->Person name-map))


老实说,我很少需要将地图转换为记录。我只是使用原始构造函数。用例是什么?如果你正在序列化,你可以使用EDN来保证它的安全。我刚刚重构了一个旧的代码问题,它使用映射来使用记录来理解它们的属性。JSON->地图向量将成为JSON->记录向量。用于EDN参考的Thx。我会调查的。老实说,我很少需要把地图转换成记录。我只是使用原始构造函数。用例是什么?如果你正在序列化,你可以使用EDN来保证它的安全。我刚刚重构了一个旧的代码问题,它使用映射来使用记录来理解它们的属性。JSON->地图向量将成为JSON->记录向量。用于EDN参考的Thx。我会调查的。谢谢你-我想这个答案可能涉及到规范,我会了解更多。我对如何防止这种情况发生感兴趣:
(地图->人{:名字“Rich”,:姓氏“Hickey”,:宠物“Spot”};=>#user.Person{:first name“Rich”,:last name“Hickey”,:pet“Spot”}
@ericky以下规范确保只有键
:a
:b
在地图中,根据需要调整:
(s/和(s/keys:req-un[:a::b])#(=2(计数%)
@ericky没有简单的方法将密钥限制为一组封闭的允许密钥,并禁止所有其他密钥,这是故意的。据我所知,Clojure设计人员提倡“开放”规范(可能更普遍的是系统对扩展“开放”)。根据这一观点,应该始终可以使用额外的键来丰富您的数据,并且您的系统应该能够容忍这一点(允许丰富的数据在其中流动)…谢谢您-我认为spec可能与此答案有关,我将了解更多信息。我对如何防止这种情况发生感兴趣:
(地图->人{:名字“Rich”,:姓氏“Hickey”,:宠物“Spot”};=>#user.Person{:first name“Rich”,:last name“Hickey”,:pet“Spot”}
@ericky以下规范确保只有键
:a
:b
在地图中,根据需要调整:
(s/和(s/keys:req-un[:a::b])#(=2(计数%)
@ericky没有简单的方法将密钥限制为一组封闭的允许密钥,并禁止所有其他密钥,这是故意的。据我所知,Clojure设计人员提倡“开放”规范(可能更普遍的是系统对扩展“开放”)。根据这一观点,应该始终可以使用额外的键来丰富您的数据,并且您的系统应该能够容忍这一点(允许丰富的数据在其中流动)…