it-swarm.com.ru

Существует ли методология разработки программного обеспечения для функционального программирования?

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

Новый обман - это функциональное программирование, которому учат во многих книгах и учебных пособиях. Но как насчет функциональной разработки программного обеспечения? Читая о LISP и Clojure, я пришел к двум интересным утверждениям:

  1. Функциональные программы часто разрабатываются снизу вверх, а не сверху вниз ("На LISP", Пол Грэм)

  2. Функциональные программисты используют Карты, где ОО-программисты используют объекты/классы ("Clojure для Java Программистов", доклад Рича Хикли).

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

200
Thorsten

Слава Богу, что разработчики программного обеспечения еще не открыли функциональное программирование. Вот некоторые параллели:

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

    Эти функции высшего порядка часто приходят с определенными законами программирования, такими как "свободные теоремы".

  • Функциональные программисты используют диаграммы гораздо реже, чем программисты OO. Большая часть того, что выражено в диаграммах OO, вместо этого выражается в типах или в "сигнатурах", которые вы должны рассматривать как "типы модулей". У Haskell также есть "классы типов", которые немного похожи на тип интерфейса.

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

    Не все функциональные языки используют явные типы, но книга How To Design Programs , превосходная книга для изучения Scheme/LISP/Clojure, в значительной степени опирается на "описания данных", которые тесно связаны с типами.

Так, какова методология для систематического (основанного на модели?) Дизайна функционального приложения, то есть в LISP или Clojure?

Любой метод проектирования, основанный на абстракции данных, работает хорошо. Мне кажется, что это проще, когда язык имеет явные типы, но работает даже без него. Хорошей книгой о методах проектирования для абстрактных типов данных, которая легко адаптируется к функциональному программированию, является абстракция и спецификация в разработке программ Барбары Лисков и Джона Гуттага , первое издание. Лисков получил премию Тьюринга частично за эту работу.

Другая методология проектирования, которая является уникальной для LISP, состоит в том, чтобы решить, какие языковые расширения будут полезны в проблемной области, в которой вы работаете, и затем использовать гигиенические макросы для добавления этих конструкций в ваш язык. Хорошее место, чтобы прочитать об этом типе дизайна - статья Мэтью Флэтта Создание языков в ракетке . Статья может быть за платным доступом. Вы также можете найти более общий материал по этому виду дизайна, выполнив поиск по термину "встроенный язык для конкретного домена"; За конкретными советами и примерами, выходящими за рамки того, что рассматривает Мэтью Флэтт, я бы, вероятно, начал с Грэма на LISP или, возможно, ANSI Common LISP .

Каковы общие шаги, какие артефакты я использую?

Общие шаги:

  1. Определите данные в вашей программе и операции с ней, а также определите абстрактный тип данных, представляющий эти данные.

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

  3. Если вы используете типизированный функциональный язык, используйте средство проверки типов рано и часто. Если вы используете LISP или Clojure, лучше всего сначала писать контракты функций, включая модульные тесты - это разработка на основе тестов по максимуму. И вы захотите использовать любую версию QuickCheck, перенесенную на вашу платформу, которая в вашем случае выглядит так, как будто она называется ClojureCheck . Это чрезвычайно мощная библиотека для построения случайных тестов кода, использующая функции высшего порядка.

161
Norman Ramsey

Для Clojure я рекомендую вернуться к старому доброму реляционному моделированию. Out of the Tarpit это вдохновляющее чтение.

44
cgrand

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

Мой опыт связан с тем, что в последние годы я перешел из Java в Clojure.

Некоторые примеры:

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

  • Ориентация сервиса в дизайне - на самом деле очень хорошо работает с точки зрения FP, поскольку типичный сервис - это просто функция с некоторыми побочными эффектами. Я думаю, что взгляд "снизу вверх" на разработку программного обеспечения, иногда поддерживаемый в мире LISP, на самом деле является просто хорошими сервис-ориентированными принципами проектирования API в другом облике.

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

  • Прототипирование/итерация - работает так же хорошо с FP. Возможно, вы даже сможете создать прототип с пользователями, если вы очень хорошо умеете создавать инструменты/DSL и использовать их в REPL.

38
mikera

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

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

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

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

Теперь у вас есть домен, над которым у вас есть функции, которые сочетаются в соответствии с хорошо себя ведущими законами. Простой встроенный DSL!

Да, и учитывая данные свойства, вы, конечно, можете написать автоматические рандомизированные тесты их (аля QuickCheck) .. и это только начало.

13
sclv

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

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

7
Kaz

Смотрите мой ответ на другой пост:

Как Clojure подходит к разделению проблем?

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

5
drcode

Один из подходов заключается в создании внутреннего DSL на выбранном языке функционального программирования. "Модель" - это набор бизнес-правил, выраженных в DSL.

5
James Kingsbery

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

Здесь несколько ссылок:

http://www.northeastern.edu/magazine/0301/programming.html

http://citeseerx.ist.psu.edu/viewdoc/summary?doi=10.1.1.86.8371

3
Artyom Shalkhakov

Я недавно нашел эту книгу: Функциональное и реактивное моделирование домена

Я думаю, что полностью соответствует вашему вопросу.

Из описания книги:

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

2
elviejo79

Я обнаружил, что Behavior Driven Development подходит для быстро развивающегося кода как в Clojure, так и в SBCL. Реальная польза от использования BDD с функциональным языком заключается в том, что я склонен писать гораздо более тонкие модульные тесты, чем обычно, когда использую процедурные языки, потому что я гораздо лучше разбираю проблему на более мелкие фрагменты функциональности.

2
Marc

Существует стиль "расчет программы"/"дизайн по расчету", связанный с профессором Ричардом Бердом и группой "Алгебра программирования" в Оксфордском университете (Великобритания), я не думаю, что это слишком надуманно, чтобы считать это методологией.

Лично мне нравится работа, проделанная группой AoP, но у меня нет дисциплины, чтобы практиковать дизайн таким образом. Однако это мой недостаток, а не программный расчет.

1
stephen tetley

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

Хороший пример - fmap. Эта функция принимает функцию в качестве аргумента и применяет ее ко всем "элементам" второго аргумента. Поскольку он является частью класса типов Functor, любой экземпляр Functor (например, список, график и т.д.) Может быть передан в качестве второго аргумента этой функции. Он отражает общее поведение применения функции к каждому элементу второго аргумента.

1
nightski