Безопасность формата данных в clojure - PullRequest
6 голосов
/ 11 февраля 2012

Исходя из опыта Java, я очень люблю безопасность статических типов и удивляюсь, как программисты clojure решают проблему определений формата данных (возможно, не только типов, но и общих инвариантов, потому что типы являются лишь частным случаем этого).)

Это похоже на существующий вопрос «Безопасность типов в Clojure», но он больше фокусируется на аспекте проверки типов во время компиляции, в то время как меня больше интересует, как проблема прагматичноaddress.

В качестве практического примера я рассматриваю приложение-редактор, которое обрабатывает определенный формат документа.Каждый документ состоит из элементов, которые бывают нескольких разных разновидностей (графические элементы, элементы шрифта и т. Д.). Существуют редакторы для различных типов элементов, а также, конечно, функции для преобразования документа из / в поток байтов в его исходномформат диска.

Основная проблема, которая меня интересует, заключается в том, что редакторы и функции чтения / записи должны согласовать общий формат данных.В Java я бы смоделировал данные документа в виде графа объектов, например, с одним классом, представляющим документ, и одним классом для каждого разнообразия элементов.Таким образом, я получаю гарантию времени компиляции о том, как выглядит структура моих данных, и что поле "ширина" графического элемента является целым числом, а не с плавающей точкой.Это не гарантирует положительную ширину, но использование интерфейса getter / setter позволило бы соответствующему классу добавить такие инвариантные гарантии.

Возможность полагаться на это упрощает код, работающий с этими данными, инарушения формата могут быть обнаружены во время компиляции или на ранней стадии выполнения (когда некоторый код пытается изменить данные, которые будут нарушать инварианты).

Как вы можете добиться подобной «надежности формата данных» в Clojure?Насколько я знаю, нет никакого способа выполнить проверку во время компиляции, и сокрытие данных домена за функциональным интерфейсом, кажется, не приветствуется как не-идиоматическое (или, может быть, я неправильно понимаю?), Так что разработчики Clojure делают, чтобы чувствовать себя в безопасностиформат данных передан в их функции?Как заставить ваш код выдавать ошибку как можно быстрее, а не после того, как пользователь отредактировал еще 20 минут и пытается сохранить на диск, когда функция сохранения замечает, что в списке шрифтов есть графический элемент из-заошибка редактора?

Обратите внимание, что я заинтересован в Clojure и обучении, но еще не написал никакого реального программного обеспечения с ним, поэтому возможно, что я просто запутался, и ответ очень прост - еслитак что, извините за трату вашего времени:).

Ответы [ 2 ]

8 голосов
/ 12 февраля 2012

Я не вижу ничего плохого или недиоматического в использовании проверяющего API для создания и обработки ваших данных, как показано ниже.

(defn text-box [text height width]
  {:pre [(string? text) (integer? height) (integer? width)]}
  {:type 'text-box :text text :height height :width width})

(defn colorize [thing color]
  {:pre [(valid-color? color)]}
  (assoc thing :color color))

... (colorize (text-box "Hi!" 20 30) :green) ...

Кроме того, ссылки (переменные, ссылки, атомы, агенты) могут иметь ассоциированную функцию проверки , которую можно использовать для обеспечения постоянного состояния.

5 голосов
/ 12 февраля 2012

Хороший вопрос - я также считаю, что переход от статически типизированного языка к динамическому требует немного больше заботы о безопасности типов. К счастью, техники TDD очень помогают здесь.

Обычно я пишу функцию проверки, которая проверяет все ваши предположения о структуре данных. Я часто делаю это и в Java для инвариантных предположений, но в Clojure это важнее, потому что вам нужно также проверять мышление как типы.

Затем вы можете использовать функцию проверки несколькими способами:

  • В качестве быстрой проверки на REPL: (validate foo)
  • В модульных тестах: (is (validate (new-foo-from-template a b c)))
  • В качестве проверки во время выполнения ключевых функций, например, проверка правильности (read-foo some-foo-input-stream)

Если у вас сложная структура данных, которая представляет собой дерево из нескольких различных типов компонентов, вы можете написать функцию проверки для каждого типа компонента и рекурсивно выполнить функцию проверки для всего вызова документа для каждого компонента. Хорошим трюком является использование протоколов или мультиметодов, чтобы сделать функцию проверки полиморфной для каждого типа компонента.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...