Подтвердить что ты не робот

Как эффективно управлять базой кода Clojure?

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

Теперь мы начинаем новый, но связанный с ним проект и переносим много старого кода. Но мы снова ударяем о стену.

Нам интересно, как мы эффективно управляем проектом Clojure, тем более что мы вносим изменения в существующий код?

Что мы придумали:

  • либеральное использование модульных тестов
  • либеральное использование пре-, пост-условий
  • объявления неформального типа в комментариях функций
  • используйте defrecord/defstruct/defprotocol для реализации модели данных, которая действительно упростит тестирование.

Но post-, pre-conditions, кажется, не используются очень часто. Модульное тестирование + комментарии помогут только так. И похоже, что программисты Clojure обычно не реализуют формальные модели данных.

Мы просто не получаем Clojure? Как программисты Clojure знают, что их код надежный и правильный?

4b9b3361

Ответ 1

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

Некоторые предложения по моему опыту:

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

  • Тесты как примеры - тестовый код в Clojure отлично работает как для проверки работоспособности вашего кода, так и для документации (например, "какой параметр ожидает эта функция?" ), Если вы нажмете ошибку, обратитесь к вашим тестам, чтобы проверить свои предположения и написать пару новых тестов, чтобы очистить то, что не так.

  • Сохранять функции просто и составлять их - вид расширения принципа единой ответственности" к функциональному программированию. Я рассматриваю более 5-10 строк в функции Clojure как основной запах кода (если это кажется экстремальным, просто помните, что вы, вероятно, можете достичь столько же в 5-10 строк Clojure, сколько сможете с 50-100 строки Java/С#)

  • Следите за "императивными привычками" . Когда я впервые начал использовать Clojure, я написал много псевдо-императивного кода в Clojure. Примером может быть эмуляция цикла for с "точками" и накопление некоторого результата в атоме. Это может быть болезненным - это не идиоматично, это сбивает с толку и обычно существует гораздо более умный, простой и менее подверженный ошибкам функциональный способ его выполнения. Это требует практики, но это того стоит в конечном итоге...

  • Отладка в REPL - обычно, когда я сталкиваюсь с проблемой, кодирование на REPL - это самый простой способ его очистки. Как правило, это означает, что некоторые определенные части более крупного алгоритма проверяют предположения и т.д.

  • Общие функции утилиты Refactor - вы, вероятно, найдете множество общих или структур, повторяющихся во многих функциях. Хорошо стоит вытащить это в функцию или макрос, который вы можете повторно использовать в других местах или проектах - таким образом вы можете протестировать его гораздо более строго и получить преимущества в нескольких местах. Бонусные очки, если вы можете получить его полностью вверх по течению в Clojure себя! Если вы сделаете это достаточно хорошо, то ваша основная база кода будет чрезвычайно кратким и, следовательно, простым в управлении, не содержащей ничего, кроме подлинно кода, специфичного для домена.

Ответ 2

простые составные абстракции

"Лучше иметь 100 функций, работающих на одной структуре данных, чем иметь 10 функций, работающих на 10 структурах данных" . - Алан Дж. Перлис

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

D.R.Y, Серьезно, просто не повторяйте себя

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

Ответ 3

Извините, что выкалываете этот старый вопрос, ответы от mikera и Arthur превосходны, но это то, о чем я тоже подумал, изучая Clojure, и подумал, что я бы упомянул, как мы организуем файлы.

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