Чи існує програмно-інженерна методологія функціонального програмування? [зачинено]


203

Інженерія програмного забезпечення, як вона викладається сьогодні, повністю орієнтована на об'єктно-орієнтоване програмування та "природний" об'єктно-орієнтований погляд на світ. Існує детальна методологія, яка описує, як перетворити модель домену в модель класу з декількома кроками та безліччю артефактів (UML), таких як діаграми використання-випадку або діаграми класів. Багато програмістів інтерналізували цей підхід і мають гарне уявлення про те, як спроектувати об’єктно-орієнтований додаток з нуля.

Новий ажіотаж - це функціональне програмування, яке викладається у багатьох книгах та навчальних посібниках. А як щодо функціональної інженерії програмного забезпечення? Читаючи про Ліспа і Клуджуре, я прийшов до двох цікавих тверджень:

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

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

Отже, яка методологія систематичного (на основі моделей?) Проектування функціонального додатку, тобто у Ліспі чи Клоджуре? Які загальні кроки, які артефакти я використовую, як їх відобразити з проблемного простору в простір рішення?


3
Я маю тут коментар: багато програм написано зверху вниз, практична експозиція щодо процесу розробки програмного забезпечення на функціональній мові викладена у книзі "Функціональне програмування в сумісному чистоті" (сама мова дуже академічна, хоча).
Артем Шалхаков

4
1. Парнас стверджує, що більшість програм має бути знизу вгору, а потім підробляти таким чином, щоб виглядати як зверху вниз, тому ці підходи повинні бути змішаними, правильної відповіді немає.
Габріель Шчербак

2
2. Об'єкти забезпечують поведінку залежно від їх інкапсульованого структурованого стану, у FP у вас є всі стану та структури явні, а поведінка (функції) відокремлена від структури. Отже, для моделювання даних ви використовуєте карти для об'єктів, але при проектуванні додатків об'єкти не можуть бути замінені функціями - FP - це велике вираження, що генерується та оцінюється за допомогою трубопроводів, OOP - про створення моделі та надсилання повідомлень між об'єктами.
Габріель Шчербак

1
Я запитав пов'язане питання колись назад: "як одна модель даних з реляційних баз даних в clojure?" stackoverflow.com/questions/3067261/…
Sandeep

4
Хе-хе, на лекціях SICP Хал Абелсон, напівжартуючи, говорить щось таке: "Існує відома методологія, або, я повинен сказати, міфологія, яка називається інженерія програмного забезпечення [...], що робить складні діаграми та вимоги, а потім будує системи з ними; ті люди не дуже програмували ". Я родом із "школи Java", де ми впродовж століть навчали UML, артефакти та інше, і хоча це трохи добре, занадто багато планування та схематизації (каламбур призначено) є шкідливішим, ніж корисним: ти ніколи не знаєш, як програмне забезпечення буде, поки ви не дійдете до фактичного коду.
lfborjas

Відповіді:


165

Слава Богу, що люди з програмного забезпечення ще не виявили функціонального програмування. Ось кілька паралелей:

  • Багато OO «дизайнерські зразки» відображаються як функції вищого порядку. Наприклад, модель відвідувачів у функціональному світі відома як «складка» (або, якщо ви теоретик із цілеспрямованим виглядом, «катаморфізм»). У функціональних мовах типи даних - це переважно дерева або кортежі, і кожен тип дерев пов'язаний із цим природним катаморфізмом.

    Ці функції вищого порядку часто поставляються з певними законами програмування, відомими також як "вільні теореми".

  • Функціональні програмісти використовують діаграми набагато менше, ніж програмісти ОО. Значна частина того, що виражається на діаграмах ОО, замість цього виражається у типах або у «підписах», які слід думати як «типи модулів». У Haskell також є "типи класів", що трохи нагадує тип інтерфейсу.

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

    Не всі функціональні мови використовують явні типи, але книга How to Design Programs - відмінна книга для вивчення схеми / Lisp / Clojure, значною мірою покладається на "описи даних", які тісно пов'язані з типами.

Отже, яка методологія систематичного (на основі моделей?) Проектування функціонального додатку, тобто у Ліспі чи Клоджуре?

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

Інша методологія дизайну, унікальна для Lisp, - це визначити, які розширення мови були б корисні в проблемній області, в якій ви працюєте, а потім використовувати гігієнічні макроси, щоб додати ці конструкції до вашої мови. Гарним місцем для читання про подібний дизайн є стаття Меттью Флатта " Створення мов у ракетці" . Стаття може бути за платною стіною. Ви також можете знайти більш загальні матеріали про подібний дизайн, шукаючи термін "вбудована мова, що залежить від домену"; для конкретних порад та прикладів, що не стосуються Метью Флатта, я б, мабуть, почав із «Гребки» на Ліспе або, можливо, ANSI Common Lisp .

Які загальні кроки, які артефакти я використовую?

Загальні кроки:

  1. Визначте дані у вашій програмі та операції над нею та визначте абстрактний тип даних, що представляє ці дані.

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

  3. Якщо ви використовуєте набрану функціональну мову, використовуйте перевірку типу рано і часто. Якщо ви використовуєте Lisp або Clojure, найкраща практика полягає в тому, щоб спочатку писати контракти з функціями, включаючи одиничні тести - це тестова розробка до максимуму. І ви хочете використовувати будь-яку версію QuickCheck, яка була перенесена на вашу платформу, яка у вашому випадку виглядає так, як називається ClojureCheck . Це надзвичайно потужна бібліотека для побудови випадкових тестів коду, яка використовує функції вищого порядку.


2
Відвідувач IMO не складається - fold - це підмножина відвідувача. Багаторазова відправка не (безпосередньо) вловлюється складкою.
Майкл Екстранд

6
@Michael - насправді ви можете дуже акуратно зафіксувати декілька відправлень з різними видами катаморфізмів вищого порядку. Робота Джеремі Гіббонса - це одне місце для пошуку, але я б рекомендував взагалі працювати над типовим програмуванням даних - мені особливо подобається композиційний папір.
sclv

6
Я згоден, що я бачу, що діаграми використовуються набагато рідше для опису функціональних конструкцій, і я думаю, що це прикро. Справді, важко представити еквівалент діаграми послідовностей, коли використовується багато HOF. Але я б хотів, щоб простір того, як описати функціональні конструкції з малюнками, був краще вивчений. Наскільки я ненавиджу UML (як специфікацію), я вважаю, що UML (як ескіз) є досить корисним на Java, і я хотів би, щоб були найкращі практики, як зробити еквівалент. Я трохи експериментував, роблячи це з протоколами Clojure та записами, але нічого, що мені дуже подобається.
Алекс Міллер

22
+1 за "Слава Богу, що люди з інженерної програми ще не виявили функціонального програмування". ;)
Aky

1
OO - це сам спосіб спроби програмування з типами, тому підходи не такі вже й різні. Проблема з проектами OO, як правило, виникає у людей, не знаючих, що вони роблять.
Марцін

46

Для Clojure я рекомендую повернутися до старого хорошого реляційного моделювання. З-під Tarpit - це натхненне читання.


Ось чудова стаття, старі добрі часи в інформатиці, мабуть, були справді вражаючими хорошими, коли всі ці концепції дожили до сьогоднішнього Відродження. Це, мабуть, пов’язано з міцними основами математики.
Торстен

1
Це. ЦЕЙ. ЦЕ! Я читаю цей документ, і мені дуже цікаво, як здається, охоплює всі основи того, що потрібно для побудови реальних систем, зберігаючи при цьому мінімально змінений стан під контролем. Я граю з побудовою Понга та Тетріса у стилі FRelP (вибачте від дивного ініціалізму, але є вже інше більш популярне FRP: функціональне реактивне програмування).
Джон Кромарті

Прочитавши статтю, я думаю, що clojure буде ідеальною мовою для FR (el) P, принаймні для суттєвої логіки , випадкового стану та контролю та інших компонентів. Цікаво, як зробити реляційне визначення суттєвого стану в clojure без винагороди sql (без його вад)? Або ідея просто використовувати хороший реляційний (sql) БД і побудувати над ним функціональну програму без концептуальної невідповідності, внесеної OOP?
Торстен

1
@Thorsten основна ідея встановлена ​​= таблиця, map = індекс. Важкою частиною є зберігання індексів та таблиць синхронізованими, але цю проблему можна вирішити за допомогою кращих заданих типів. Один з простих реалізованих мною типів наборів - це набір ключів, який є набором, який використовує функцію ключа для перевірки єдиності. Це означає, що зміна вставки або оновлення значення, виклик get з полями первинного ключа повертає весь рядок.
cgrand


38

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

Мій досвід походить від переїзду з Яви до Clojure в останні роки.

Деякі приклади:

  • Розуміння вашої бізнес-домени / моделі даних - не менш важливо, чи збираєтесь ви розробляти об'єктну модель або створити функціональну структуру даних із вкладеними картами. У чомусь FP може бути простішим, оскільки він спонукає вас думати про модель даних окремо від функцій / процесів, але вам все одно потрібно робити і те, і інше.

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

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

  • Прототизування / ітерація - добре працює з FP. Можливо, ви навіть зможете скласти прототип у прямому ефірі з користувачами, якщо вам дуже надзвичайно вдало будувати інструменти / DSL та використовувати їх у системі REPL.


3
Ці практики мені звучать досить звично. Я все ще думаю, що хтось повинен написати функціональний еквівалент "Об'єктно-орієнтована інженерія програмного забезпечення за допомогою UML, шаблонів та Java" від Bruegge / Dutoit замість шостої книги "Програмування в Clojure". Це можна було б назвати "Функціональна інженерія програмного забезпечення за допомогою Clojure і що таке". Чи використовують вони UML та шаблони у FP? Пам’ятаю, Пол Грехем писав, що візерунки є ознакою відсутності абстракції в Ліспі, що слід усунути шляхом впровадження нових макросів.
Торстен

3
Але якщо ви перекладаєте шаблони як найкращі практики, то у світі FP можуть бути і такі шаблони, які варто ділитися з неініціалізованими.
Торстен

2
У книзі PAIP є кілька цікавих принципових конструкцій. norvig.com/paip.html
mathk

1
є також функціональні схеми програмування (схеми рекурсії тощо)
Габріель Шчербак

13

Програмування ОО щільно поєднує дані з поведінкою. Функціональне програмування розділяє два. Отже, у вас немає діаграм класів, але у вас є структури даних, і ви особливо маєте алгебраїчні типи даних. Ці типи можна записати так, щоб вони дуже відповідали вашому домену, включаючи усунення неможливих значень шляхом побудови.

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

Роблячи це, ви можете робити вибір щодо представлення певних типів даних як функцій, і навпаки, представляючи певні функції замість цього об'єднання типів даних, щоб ви могли отримати, наприклад, серіалізацію, більш жорстку специфікацію, оптимізацію тощо .

Потім, враховуючи це, ви записуєте функції над рекламою так, щоб встановити якусь алгебру - тобто є фіксовані закони, які відповідають цим функціям. Деякі можуть бути ідентичними - те саме після декількох застосувань. Деякі асоціативні. Деякі є транзитивними тощо.

Тепер у вас є домен, над яким у вас є функції, які складаються згідно з добре поведеними законами. Простий вбудований DSL!

О, і з урахуванням властивостей, ви, звичайно, можете писати автоматизовані рандомізовані тести з них (ала QuickCheck) .. і це лише початок.


1
Підхід щодо неможливості подання неможливих значень менш застосовний до мов з динамічним введенням типу Clojure та Scheme, ніж до мов зі статичним введенням типу Haskell та ML.
Зак

@Zak - ну ви не можете статично перевірити, що вони не представлені, але ви можете так само будувати свої структури даних однаково.
sclv

7

Об'єктно-орієнтований дизайн - це не те саме, що інженерія програмного забезпечення. Інженерія програмного забезпечення пов'язана з усім процесом того, як ми переходимо від вимог до робочої системи, вчасно та з низьким рівнем дефектів. Функціональне програмування може відрізнятися від OO, але воно не усуває вимог, високого рівня та детальних конструкцій, перевірки та тестування, метрики програмного забезпечення, оцінки та всього іншого "інженерного забезпечення програмного забезпечення".

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


5

Один із підходів полягає у створенні внутрішнього DSL в обраній мові функціонального програмування. Тоді "модель" - це набір бізнес-правил, виражених у DSL.


1
Я розумію підхід спочатку побудувати мову до проблемної області, поки не буде досягнуто рівня абстракції, що в коді більше не виникає повторюваних зразків, ніж вирішити проблему з цими абстракціями.
Торстен

1
Але як це виглядає, коли "модель - це сукупність бізнес-правил, виражених в DSL"? У додатку Java EE модель записується як POJO-Entities, які викликаються від контролерів-EJB, які, в свою чергу, оновлюють перегляд JSP - наприклад. Чи є подібні архітектурні зразки (як MVC-зразок) у ПП? Як це виглядає?
Торстен

2
Немає причин, щоб у вас не було малюнку MVC у FP, саме так. FP все ще дозволяє створювати багаті структури даних, і, мабуть, за допомогою ADT та узгодження зразків, дозволяє створювати набагато багатші . Якщо що, оскільки FP розділяє дані та поведінку, системи типу MVC виникають набагато природніше.
sclv

5

Дивіться мою відповідь на інший пост:

Як Clojure підходить до поділу проблем?

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


3
Мені подобається 90% конвеєр і 10% макро підхід. Цілком природно виглядати функціональну програму як конвеєр перетворень на незмінних даних. Я не впевнений, чи розумію, що ви маєте на увазі під "вкладати весь інтелект у дані, а не в код", оскільки підхід до 100 функцій, що працюють на 1 структурі даних (а не 10 функцій на 10 структурах даних), мабуть, означає протилежність. Чи не є структури даних в OOP більш розумними, ніж у FP, оскільки в них побудована власна поведінка?
Торстен

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 (і, мабуть, переглядають друге видання HtDP, щоб використовувати його замість Racket, раніше PLT Scheme).
Артем Шалхаков

3

Нещодавно я знайшов цю книгу: Функціональне та реактивне моделювання доменів

Я думаю, що це повністю відповідає вашому питанню.

З опису книги:

Функціональне та реактивне моделювання домену вчить вас думати про модель домену з точки зору чистих функцій та як складати їх для створення великих абстракцій. Ви почнете з основ функціонального програмування і поступово переходите до передових концепцій та моделей, які вам потрібно знати, щоб реалізувати складні доменні моделі. Книга демонструє, як передові схеми ПП, такі як алгебраїчні типи даних, дизайн на основі класного класу та ізоляція побічних ефектів, можуть зробити вашу модель складною для читабельності та перевірки.


2

Існує стиль "розрахунку програми" / "проектування за допомогою розрахунку", пов'язаний з професором Річардом Бердом та групою "Алгебра програмування" в Оксфордському університеті (Великобританія), я не думаю, що це занадто надумано вважати цю методологію.

Особисто мені подобаються роботи, створені групою AoP, я не маю дисципліни, щоб самому займатися дизайном таким чином. Однак це мій недолік, і не один з програмних розрахунків.


2

Я виявив, що поведінка, керована розвитком, є природним пристосуванням для швидко розвивається коду як в Clojure, так і в SBCL. Справжня перевага використання BDD з функціональною мовою полягає в тому, що я, як правило, пишуть набагато тонші зернові одиниці тестів, ніж я зазвичай роблю при використанні процедурних мов, тому що я роблю набагато кращу роботу з розкладання проблеми на менші шматки функціональності.


які інструменти ви використовуєте для створення BDD в clojure?
murtaza52

Мені подобається Міддже. Це сучасне та дуже виразне. Перевірте це: github.com/marick/Midje
MARC

1

Якщо чесно, якщо ви хочете розробити рецепти функціональних програм, подивіться на стандартні бібліотеки функцій, такі як Прелюдія Haskell. У FP шаблони зазвичай фіксуються самими процедурами вищого порядку (функціями, що функціонують над функціями). Отже, якщо видно візерунок, часто використовується функція вищого порядку для захоплення цього шаблону.

Хороший приклад - fmap. Ця функція приймає функцію як аргумент і застосовує її до всіх "елементів" другого аргументу. Оскільки він є частиною класу типу Functor, будь-який примірник Functor (наприклад, список, графік тощо) може бути переданий як другий аргумент цій функції. Він фіксує загальну поведінку застосування функції до кожного елемента другого аргументу.


-7

Ну,

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

Вони набувають все більшої популярності зараз, оскільки у OOP виникають труднощі з "паралельним програмуванням" через "стан". І колись функціональний стиль краще для проблем, як Google MapReduce.

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

Впевнений, що функціональний стиль вплине на наш об’єктно-орієнтований стиль. Ми "все ще" багато концепцій з функціональних систем та адаптовані до наших мов OOP.

Але чи будуть функціональні програми використовуватися для таких великих систем? Вони стануть основним потоком? Це питання .

І ніхто не може придумати реалістичну методологію без застосування таких великих систем, роблячи руки брудними. Спочатку слід забруднити руки, а потім запропонувати рішення. Рішення-пропозиції без "справжніх болів і бруду" будуть "фантазією".


Зараз було створено досить масштабні системи з функціональними мовами. Навіть якщо цього не було, це зовсім не аргумент.
Svante

Ну, назвіть деякі з них? Я просто знаю дуже небагато систем "Ерланг". [середній розмір] Але Хаскель? Clojure? Лісп?
Гіппій Малий

І це [написання великих систем] - справжній аргумент. Тому що це тестовий випадок. Цей тестовий випадок показує, що якщо цей функціональний стиль є корисним, і чи можемо ми зробити з ним практичні речі в реальному світі.
Гіппій Малий

2
Найцікавіше, що в мовах не є аналогічно "OOP", це те, що вони часто дають вам свободу від "методологій дизайну", думати про себе і скорочувати свою програму найбільш відповідним чином, а не сліпо слідувати заданій схемі і жити з бюрократична котельня. Вибачте, тут немає тритижневого тритижневого курсу.
Svante

1
Я бачив речі, яким ви не повірите.
Svante
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.