Ключи */ ключи со встроенными значениями

Я хочу написать спецификацию с 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
Другие вопросы по тегам