2012-02-11 3 views
6

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

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

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

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

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

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

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

ответ

8

Я не вижу ничего неправильного или унииоматичного в отношении использования проверяющего 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) ... 

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

+0

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

5

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

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

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

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

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

Смежные вопросы