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

203

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

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

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

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

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

Торстен
источник
3
У меня есть комментарий: многие программы написаны сверху вниз, практическое изложение процесса разработки программного обеспечения на функциональном языке дано в книге «Функциональное программирование в параллельной очистке» (сам язык очень академический, хотя).
Артём Шалхаков
4
1. Парнас утверждает, что большинство программ должны быть восходящими, а затем фальсифицированными, чтобы выглядеть как нисходящие, поэтому эти подходы должны быть смешанными, правильного ответа нет.
Габриэль Шербак
2
2. Объекты обеспечивают поведение в зависимости от их инкапсулированного структурированного состояния, в FP у вас есть все явное состояние и структура, а поведение (функции) отделено от структуры. Поэтому для моделирования данных вы используете карты для объектов, но при разработке приложений объекты не могут быть заменены функциями - FP - это большое выражение, генерируемое и оцениваемое с помощью конвейеров, ООП - это создание модели и отправка сообщений между объектами.
Габриэль Шербак
1
Я задал связанный вопрос однажды назад: «Как можно моделировать данные из реляционных баз данных в clojure?» stackoverflow.com/questions/3067261/…
Сандип
4
Хе-хе, в одной из лекций SICP Хэл Абельсон говорит, наполовину в шутку, что-то вроде «Существует известная методология, или, я бы сказал, мифология, называемая [...] разработка программного обеспечения, создание сложных диаграмм и требований, а затем построение системы с ними, эти люди не запрограммированы много ". Я из «Школы Java», где целую вечность мы преподавали UML, артефакты и прочее, и хотя отчасти это хорошо, слишком много планирования и интриги (каламбур) скорее вредно, чем полезно: вы никогда не знаете, как Программное обеспечение будет, пока вы не получите на самом деле код.
lfborjas

Ответы:

165

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

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

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

  • Функциональные программисты используют диаграммы гораздо реже, чем ОО-программисты. Многое из того, что выражено в диаграммах ОО, вместо этого выражено в типах или в «сигнатурах», которые вы должны рассматривать как «типы модулей». У Haskell также есть «классы типов», которые немного похожи на тип интерфейса.

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

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

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

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

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

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

Общие шаги:

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

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

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

Норман Рэмси
источник
2
ИМО посетитель не сгиб - сгиб это подмножество посетителей. Многократная отправка не (непосредственно) фиксируется сгибом.
Майкл Экстранд
6
@Michael - на самом деле вы можете аккуратно захватывать несколько отправлений с различными видами катаморфизмов более высокого порядка. Работа Джереми Гиббонса - это одно из мест, где можно это искать, но я бы порекомендовал поработать над общим типом программирования - мне особенно нравится композитная статья.
sclv
6
Я согласен с тем, что вижу схемы, используемые гораздо реже для описания функциональных конструкций, и мне кажется, это позор. По общему признанию трудно представить эквивалент диаграммы последовательности при использовании большого количества HOF. Но я хотел бы, чтобы пространство того, как описывать функциональные проекты с изображениями, было лучше изучено. Как бы я ни ненавидел UML (как спецификацию), я считаю, что UML (как набросок) весьма полезен в Java, и хотел бы, чтобы были лучшие практики, как сделать эквивалент. Я немного экспериментировал с протоколами и записями Clojure, но мне ничего не нравится.
Алекс Миллер
22
+1 за «Слава Богу, что разработчики программного обеспечения еще не открыли функциональное программирование». ;)
Aky
1
ОО сам по себе является попыткой программирования с типами, поэтому подходы не столь различны. Проблема с ОО-проектами обычно возникает из-за того, что люди не знают, что делают.
Марчин
46

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

cgrand
источник
Это отличная статья, старые добрые времена в области компьютерных наук, должно быть, были действительно впечатляюще хорошими, когда все эти концепции сохранились до сегодняшнего возрождения. Это, вероятно, из-за сильных основ математики.
Торстен
1
Это. ЭТОТ. ЭТОТ! Я читаю эту статью, и мне действительно интересно, как она охватывает все основы того, что требуется для построения реальных систем, в то же время поддерживая минимальное изменяемое состояние в строго контролируемой манере. Я играю с созданием понга и тетриса в стиле FRelP (извините за странный инициализм, но уже есть еще один более популярный FRP: функциональное реактивное программирование).
Джон Кромарти
После прочтения статьи я думаю, что clojure был бы идеальным языком для FR (el) P, по крайней мере, для основной логики , случайного состояния и управления и других компонентов. Интересно, как сделать реляционное определение существенного состояния в clojure, не изобретая sql (без его недостатков)? Или это идея просто использовать хорошую реляционную (sql) БД и построить поверх нее функциональную программу без концептуального несоответствия, введенного ООП?
Торстен
1
@Thorsten основная идея - set = table, map = index. Самое сложное - это синхронизировать индексы и таблицы, но эту проблему можно решить с помощью лучших типов наборов. Один простой тип набора, который я реализовал, является набором ключей, который представляет собой набор, который использует функцию ключа для проверки уникальности. Это означает, что, соединяя вставку или обновление значения, вызов get с полями первичного ключа возвращает всю строку.
Cgrand
38

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

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

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

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

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

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

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

mikera
источник
3
Эти практики звучат довольно знакомо для меня. Я все еще думаю, что кто-то должен написать функциональный эквивалент «Объектно-ориентированной программной инженерии с использованием UML, Patterns и Java» Брюгге / Дютуа вместо шестой книги «Программирование на языке Clojure». Его можно назвать «Функциональная разработка программного обеспечения с использованием Clojure и что». Используют ли они UML и шаблоны в FP? Я помню, как Пол Грэм писал, что шаблоны являются признаком отсутствия абстракции в Лиспе, что должно быть исправлено введением новых макросов.
Торстен
3
Но если вы переводите шаблоны как лучшие практики, в мире FP также могут быть шаблоны, которыми стоит поделиться с неинициализированными.
Торстен
2
В книге PAIP есть несколько интересных принципов. norvig.com/paip.html
mathk
1
существуют также шаблоны функционального программирования (схемы рекурсии и т. д.)
Габриэль Шербак,
13

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

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

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

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

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

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

sclv
источник
1
Подход, делающий невозможные значения непредставимыми, менее применим к языкам с динамической типизацией, таким как Clojure и Scheme, чем к языкам со статической типизацией, таким как Haskell и ML.
Зак
@Zak - ну, вы не можете статически проверить, что они непредставимы, но вы все равно можете строить свои структуры данных одинаково.
SCLV
7

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

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

Kaz
источник
5

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

Джеймс Кингсбери
источник
1
Я понимаю подход сначала построить язык в направлении проблемной области, пока не будет достигнут уровень абстракции, чтобы в коде больше не возникало повторяющихся шаблонов, чем решение проблемы с этими абстракциями.
Торстен
1
Но как это выглядит, когда «модель представляет собой набор бизнес-правил, выраженных в DSL»? В приложении Java EE модель записывается как POJO-сущности, которые вызываются из контроллеров-EJB, которые, в свою очередь, обновляют JSP-представления - например. Существуют ли аналогичные архитектурные шаблоны (например, MVC-шаблоны) в FP? Как это выглядит?
Торстен
2
Нет причины, по которой вы не можете иметь шаблон MVC в FP, именно так. FP по-прежнему позволяет создавать богатые структуры данных, и, возможно, с помощью ADT и сопоставления с образцом, позволяет создавать гораздо более богатые . Во всяком случае, поскольку FP разделяет данные и поведение, системы типов MVC возникают гораздо более естественно.
SCLV
5

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

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

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

drcode
источник
3
Мне нравится 90% конвейер и 10% макроподход. Кажется вполне естественным рассматривать функциональную программу как конвейер преобразований неизменяемых данных. Я не уверен, понимаю ли я, что вы подразумеваете под «вкладывать весь интеллект в данные, а не в код», поскольку подход, предусматривающий 100 функций, работающих с 1 структурой данных (а не 10 функций с 10 структурами данных), предполагает противоположный. Разве структуры данных в ООП не более интеллектуальны, чем в ФП, поскольку они имеют собственное встроенное поведение?
Торстен
3

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

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

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

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

Артём Шалхаков
источник
Ссылка на северо-восточную страницу кажется мертвой.
Джеймс Кингсбери
1
Джеймс, ты прав, и я не помню, что там было, к сожалению, чтобы это исправить. Я только знаю, что авторы HtDP продолжали создавать язык Pyret (и, вероятно, пересматривают 2-е издание HtDP, чтобы использовать его вместо Racket, ранее PLT Scheme).
Артём Шалхаков
3

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

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

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

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

elviejo79
источник
2

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

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

Стивен Тетли
источник
2

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

Марк
источник
Какие инструменты вы используете, чтобы сделать BDD в ближайшем будущем?
Муртаза52
Мне нравится Мидже. Это актуально и очень выразительно. Проверьте это: github.com/marick/Midje
Марк
1

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

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

nightski
источник
-7

Хорошо,

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

Сейчас они становятся все более популярными, поскольку ООП испытывает трудности с «параллельным программированием» из-за «состояния». И иногда функциональный стиль лучше подходит для таких задач, как Google MapReduce.

Я уверен, что когда ребята из functioanl столкнутся со стеной [попытаются внедрить системы, содержащие более 1 000 000 строк кода], некоторые из них получат новые методологии разработки программного обеспечения с модными словечками :-). Они должны ответить на старый вопрос: как разделить систему на куски, чтобы мы могли «кусать» каждый кусочек по одному? [работать итеративно, инцераментально и эволюционно] с использованием функционального стиля.

Уверен, что функциональный стиль повлияет на наш объектно-ориентированный стиль. Мы «по-прежнему» многие понятия из функциональных систем и адаптированы к нашим языкам ООП.

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

И никто не может прийти с реалистичной методологией, не внедрив такие большие системы, не испачкав руки. Сначала вы должны испачкать руки, а затем предложить решение. Решения-предложения без «настоящих болей и грязи» будут «фантазией».

Хиппиас Малый
источник
В настоящее время было создано достаточно крупномасштабных систем с функциональными языками. Даже если бы не было, это не аргумент вообще.
Сванте
Ну, назовите некоторые из них? Я просто знаю очень мало систем "Erlang". [среднего размера] Но Хаскель? Clojure? Lisp?
Хиппиас Малый
И это [написание больших систем] является реальным аргументом. Потому что это контрольный пример. Этот тестовый пример показывает, что, если этот функциональный стиль полезен, и можем ли мы сделать с ним практические вещи в реальном мире.
Хиппиас Малый
2
Самое смешное в языках, которые не являются «ООП», это то, что они часто дают вам свободу от «методологических методологий проектирования», думать самостоятельно и сокращать вашу программу наиболее подходящим способом, вместо того, чтобы слепо следовать установленному шаблону и жить с бюрократический шаблон. Извините, нет 10-балльного 3-недельного курса здесь.
Сванте
1
Я видел вещи, в которые ты не поверишь.
Сванте