Ключи */ ключи со встроенными значениями
Я хочу написать спецификацию с keys
/ keys*
но я был в состоянии указать спецификации значений, что не поддерживается проектом, и я понял причину этого. Однако иногда вы хотите (или просто хотите использовать обычную или стороннюю) связь между ключами и значениями, когда существует определенный контекст карты.
Я все еще новичок в спецификации, и это только первый раз, когда я интегрирую его в существующий проект, и он постоянно вызывает у меня проблемы, потому что он предполагает слишком много, особенно по причине, упомянутой выше. Например, представьте карту, которая описывает период времени и имеет until
ключ для даты, и в тех же ns есть карта для обработки списка и есть until
это берет предикатную функцию. Теперь мне нужно возиться с ручным написанием полностью пространственных ключей для пространств имен, которые даже не существуют (alias
ing - это мило, но его придется постоянно дублировать в нескольких пространствах имен / файлах). Помимо раздражения я чувствую, что это также подвержено ошибкам.
И еще одно место, где keys
/ keys*
предполагается, что слишком много, если я даже хочу ключевые слова в качестве моих ключей. Я сейчас пишу DSL для непрограммистов, но для технических пользователей, и суть в том, что я хочу указать карту с символами в качестве ключей. Это, кажется, не поддерживается вообще.
Есть что-то, чего я не получаю? или в спецификации действительно отсутствует необходимая функциональность?
2 ответа
Вы можете указать карту с символами в качестве ключей либо с помощью map-of:
(s/def ::sm (s/map-of symbol? any?))
или указав карту как набор записей:
(s/def ::sm (s/every (s/tuple symbol? any?) :kind map? :into {}))
Последнее особенно интересно, так как вместо одного кортежа вы можете использовать различные типы кортежей для описания более интересных карт. Вы даже можете подключить эти символы к другим существующим спецификациям следующим образом:
(s/def ::attr1 int?)
(s/def ::attr2 boolean?)
(s/def ::sm (s/every (s/or :foo (s/tuple #{'foo} ::attr1)
:bar (s/tuple #{'bar} ::attr2))
:kind map? :into {}))
(s/valid? ::sm {'foo 10 'bar true}) ;; => true
Теперь мне нужно возиться с ручным написанием полностью пространства имен для пространств имен, которые даже не существуют
Я также использовал этот подход, и я думаю, что на самом деле он мне нравится больше, чем мне нравится следить за тем, чтобы пространства имен ваших ключевых слов всегда соответствовали реальным формам Clojure NS. Я использую такие ключевые слова, как :business-domain-concept/a-name
скорее, чем :my-project.util.lists/a-name
,
Вы можете создавать ключевые слова с произвольными пространствами имен, которые не отображаются ни на какой Clojure NS. Например, в вашем until
ситуация, вы могли бы определить :date/until
спецификация, которая описывает даты, и (возможно, есть более подходящее название для этого) :list/until
спецификация, которая описывает поле вашей карты обработки списка.
Похоже, вы уже знаете об этом подходе произвольного ключевого слова-пространства имен - в частности, я покупаю, что он чувствителен к ошибкам, так как вы печатаете этот материал вручную, и спецификация, кажется, не задыхается, если вы кормите ваш s/keys
:fate/until
случайно. FWIW, тем не менее, я думаю, что вы в настоящее время чувствуете боль, которую специально предназначены для решения ключевых слов пространства имен: вы находитесь в одном файле Clojure, у вас есть две карты с именами ключей until
и они означают две совершенно разные вещи.
Я сейчас пишу DSL для непрограммистов, но для технических пользователей, и суть в том, что я хочу указать карту с символами в качестве ключей.
Я думаю, что карта- это то, что вы хотите здесь:
user=> (s/def ::valid-symbols #{'foo 'bar 'baz})
:user/valid-symbols
user=> (s/def ::symbol-map (s/map-of ::valid-symbols int?))
:user/symbol-map
user=> (s/valid? ::symbol-map {'foo 1 'bar 3})
true
user=> (s/valid? ::symbol-map {'foo 1 'quux 3})
false