Пишет тебе старый твой поклонник, скромный труженик клавиатуры, канаццкий программист Борис.
Вот уже много лет я являюсь тайным поклонником функционального программирования (FP). Чего-то там писал, чего-то там изучал, проникался. Наконец, решился написать в (максимально) функциональном стиле некую систему Х нетривиальной сложности.
Подновил свои знания в области Clojure, прикинул что и как можно сделать, из каких компонент система Х будет состоять. Мысленно сказал себе: "ну, давай, Доббс, действуй!".... и впал в кому.
Причиной комы, как выяснилось при вскрытии, был экстремально скоротекущий процесс Analysis Paralysis, симтоматика которого хорошо изучена и диагностика может быть сделана на глаз любым медиком средней руки — достаточно взглянуть на бессмысленно выпученные глаза и медленно шевелящуюся нижнюю челюсть больного.
Суть паралича в данном конкретном случае была в следующем:
1) Пациент хорошо владеет OOD (Object Oriented Design) и прекрасно представляет себе как спроектировать упомянутую систему Х используя ОО подход. Может разрисовать диаграммы сколь угодно глубокой детализации начиная от диаграмм классов и заканчивая sequence диаграммами.
2) Пациент имеет смутное представление о функциональном дизайне (SA/SD), а также помнит что данный способ разбиения систем на запчасти был признан убогим еще до его (пациента) рождения.
3) Пациент в состоянии пойти на компромисс с совестью и спроектировать систему Х крупноблочно в OOD стиле, а затем (закрыв глаза и очертя голову) реализовать каждую компоненту в функциональном стиле. Однако, такой подход кажется пациенту крайне неестественным. К тому же некоторые компоненты (в частности, компонента, реализующя persistence layer) настолько stateful что пациенту просто страшно представить как они будут выглядеть при чисто функциональном подходе. Уж лучше сразу махнуть рукой и сделать ОО компоненту чем так корячить FP.
Всвязи с этим пациенту требуется срочная госпитализация и помощь опытного хирурга по следующим главным направлениям:
4) У пациента складываетя прочное мнение что FP хорошо на микроуровне, там где модель поведения программы тривиальна либо заранее известна.
5) У пациента возникает ощущение что на глобальном архитектурном уровне FP не может решить его, пациента, проблем, а лишь добавит ему, пациенту, головной боли из-за попыток симулировать OOP на FP языке (да, дайте пациенту замыкания и он в три счета сделает вам из них ООП, но смысл?)
6) Пациент все больше разуверивается в FP как в парадигме, позволяющей ему, пациенту противостоять неизбежным изменениям в архитектуре, модели поведения, модели данных.
Re: OOD vs SA/SD (ну или OO vs FP раз уж на то пошло)
И вот если только можно то поясните канкретна простому канаццкому парню вот че делать ? Вот откуда начинать архитектурить приложение ?
Следует ли ему выкорчевать все состояние из всех компонент приложения и сконцентрироваться на функциях ? Но ведь это приведет к известным проблемам (see http://www.slac.stanford.edu/BFROOT/www/doc/workbook_kiwi/coding/OOvsSASD.html), свзяанным с тем что структуры данных могут мутировать ОЧЕНЬ быстро и непредсказуемо в процессе разработки.
И как тут быть ? Откуда взяться ?
Re[2]: OOD vs SA/SD (ну или OO vs FP раз уж на то пошло)
Здравствуйте, borisman3, Вы писали:
B>И вот если только можно то поясните канкретна простому канаццкому парню вот че делать ? Вот откуда начинать архитектурить приложение ?
Я бы сначала сосредоточился на задаче и на наиболее подходящих инструментах, а уж затем использовал выбранные инструменты общепринятым способом.
B>Следует ли ему выкорчевать все состояние из всех компонент приложения и сконцентрироваться на функциях ?
Ради чего?
Здравствуйте, borisman3, Вы писали:
B>Пишет тебе старый твой поклонник, скромный труженик клавиатуры, канаццкий программист Борис.
Допускается использование программистского и околопрограммистского арго. Падоначье, медицинское, юридическое, военное, и пр. и пр. арго не допускаются.
Посыл не верный изначально. Причем еще на уровне противопоставления OOP и FP.
Твое сообщение но как бэ другими словами:
Люди, я аццкий специалист в области изготовления мебели стамесками.
Но вот увидел в Канадской Шине еще рубанки. И появилось у меня желание сделать мебельный
гарнитур X (большой) сугубо рубанком. Но что-то меня берут сомнения вырезания пазов оным инстрУментом.
Я в отчаянии. Или у вас таки будет бальзам для меня или получается фигня этот ваш рубанок.
Re: OOD vs SA/SD (ну или OO vs FP раз уж на то пошло)
Здравствуйте, borisman3, Вы писали:
B>Вот уже много лет я являюсь тайным поклонником функционального программирования (FP). Чего-то там писал, чего-то там изучал, проникался. Наконец, решился написать в (максимально) функциональном стиле некую систему Х нетривиальной сложности. B>Подновил свои знания в области Clojure, прикинул что и как можно сделать, из каких компонент система Х будет состоять. Мысленно сказал себе: "ну, давай, Доббс, действуй!".... и впал в кому.
Сразу — динамически типизированные языки требуют бОльших телодвижений при создании "систем нетривиальной сложности". Как минимум, необходимость писать огромное количество тестов. А в случае быстрого мутирования данных, о котором ты пишешь в следующем комментарии, так и вообще требуют особой внимательности.
В случае статически типизированных языков (особенно с правильной системой типов) можно ослабить внимательность, переложив часть задач на компилятор.
Подобные системы типов позволяют сильно ограничивать типы (читай — выражать больше, чем обычные типы). Это ведёт к таким интересным и полезным вещам, как
1. Parametricity. Схожий тип -> схожая семантика. Подробнее тут. Важные следствия — да и просто красиво рассказано — в знаменитой статье Theorems for free. deniok про это много писал и делал доклад на spbhug.
2. Значительная уверенность в проведённом equational reasoning, вызванная лёгкостью
его использования. Простота рефакторинга. Читаем классику. (и не надо в сотый раз мне говорить про перевёрнутые страницы! переверните в своём вьювере обратно.)
Дальше — больше. Читаем зависимые типы. Ссылок не дам, кажется, что ещё рано.
B>2) Пациент имеет смутное представление о функциональном дизайне (SA/SD), а также помнит что данный способ разбиения систем на запчасти был признан убогим еще до его (пациента) рождения.
Я не буду здесь вдаваться в споры. Просто приведу ссылки.
Ссылки на приёмы и паттерны приводить не буду — это уже больше методология, а не теория. Хотя, надо заметить, они гораздо более высокоуровневы, чем тот же GoF. Например, сам GoF просто не нужен в ФП, т.к. покрывается им самим большей частью. Делай вывод.
Второе, чистота и ленивость — единственно верный ФП Иначе от ФП мы просто получаем огрызки, сокращающие код (замыкания, паттерн матчинг), но важнейшие его преимущества остаются за бортом. Помимо перечисленного, ленивость ведёт к большей модульности
. Да и вообще нормальный порядок вычислений вычисляет выражение, если оно может быть вычисленно, всегда.
Дизайн обычно строится на более высоких абстракциях, чем принято в обычном императивном мире. Например, крупноблочная нарезка может основываться на монадах и монад-трансформерах. Это позволяет жёстко разделять участки кода, отвечающие за разную функциональность и при этом легко соединять их в единообразном виде. Жёстко — значит компилятор даст по рукам.
B>3) Пациент в состоянии пойти на компромисс с совестью и спроектировать систему Х крупноблочно в OOD стиле, а затем (закрыв глаза и очертя голову) реализовать каждую компоненту в функциональном стиле. Однако, такой подход кажется пациенту крайне неестественным. К тому же некоторые компоненты (в частности, компонента, реализующя persistence layer) настолько stateful что пациенту просто страшно представить как они будут выглядеть при чисто функциональном подходе. Уж лучше сразу махнуть рукой и сделать ОО компоненту чем так корячить FP.
Смешанный стиль (крупные абстракции — ОО, реализация методов — ФП) не даст тебе особых преимуществ, ну кроме как код сократит. Есть в Java, скажем Runnable или там Comparator — по сути замыкания. Кода чуть больше и только. В C# так и кода меньше. Паттерн-матчинг — вещь, конечно, удобная, но помогает тоже только на низком уровне. Да и детали открывает часто больше чем надо, правда, тут есть неплохие решения.
B>4) У пациента складываетя прочное мнение что FP хорошо на микроуровне, там где модель поведения программы тривиальна либо заранее известна.
Это не настоящий ФП, так, сахар.
B>5) У пациента возникает ощущение что на глобальном архитектурном уровне FP не может решить его, пациента, проблем, а лишь добавит ему, пациенту, головной боли из-за попыток симулировать OOP на FP языке (да, дайте пациенту замыкания и он в три счета сделает вам из них ООП, но смысл?)
У ОО свои проблемы. Самая главная — каждый его понимает как хочет. ОО сложнее, он менее формализован, чем ФП (см. мучения Луки Карделли). Все прочие проблемы вытекают из этого.
B>6) Пациент все больше разуверивается в FP как в парадигме, позволяющей ему, пациенту противостоять неизбежным изменениям в архитектуре, модели поведения, модели данных.
И снова я! "ленивость -> модульность -> проще делать изменения". Или вот "чистота и типы -> equational reasoning -> проще делать изменения". Ну и конечно "строгая типизация -> компилятор покажет места, где сломалось -> проще делать изменения".
Re[2]: OOD vs SA/SD (ну или OO vs FP раз уж на то пошло)
Здравствуйте, lomeo, Вы писали:
L>И снова я! "ленивость -> модульность -> проще делать изменения". Или вот "чистота и типы -> equational reasoning -> проще делать изменения". Ну и конечно "строгая типизация -> компилятор покажет места, где сломалось -> проще делать изменения".
Почти любая крупная состоит из нескольких частей:
1)UI может быть stateless (Web) или stateful (desktop), может быть interactive (консоль, веб) или reactive (формочки) или смесь.
2)Хранилище данных — файлы, бд (SQL), еще ченить
3)"Бизнес логика" — то что занимается преобразованием данных между UI и хранилищем
4)Application services — компоненты, не занимающиеся непосредственно данными, например отправка почты
5)Механизм увязывания всего этого межу собой, желательно чтобы можно было менять конфигурацию и состав компонент без пересборки приложения
То что я видел в примерах на haskell, это слабоинтерактивное (консольное) приложение со stateless бизнес-логикой.
Хотелось бы увидеть всетаки приложение на haskell, которое имеет как минимум все части, указанные выше, о чтобы весь код не был завернут в IO.
Хотябы один пример какой-нить записной книжки с двумя таблицами в БД и формочкой.
Re: OOD vs SA/SD (ну или OO vs FP раз уж на то пошло)
Здравствуйте, borisman3, Вы писали:
B>6) Пациент все больше разуверивается в FP как в парадигме, позволяющей ему, пациенту противостоять неизбежным изменениям в архитектуре, модели поведения, модели данных.
Здравствуйте, lomeo, Вы писали:
L>Сразу — динамически типизированные языки требуют бОльших телодвижений при создании "систем нетривиальной сложности". Как минимум, необходимость писать огромное количество тестов. А в случае быстрого мутирования данных, о котором ты пишешь в следующем комментарии, так и вообще требуют особой внимательности.
Ты тут не совсем прав, вернее совсем не прав. В прототипировании динамика гораздо лучше статики.
"Огромное" количество тестов в реальных системах оказывается вполне сопоставимым с количеством тестов
для статических языков.
Быстрое мутирование данных как раз проще в динамике за счет того что там эти данные более гибкие.
Re[2]: OOD vs SA/SD (ну или OO vs FP раз уж на то пошло)
Здравствуйте, borisman3, Вы писали:
B>Следует ли ему выкорчевать все состояние из всех компонент приложения и сконцентрироваться на функциях ? Но ведь это приведет к известным проблемам (see http://www.slac.stanford.edu/BFROOT/www/doc/workbook_kiwi/coding/OOvsSASD.html), свзяанным с тем что структуры данных могут мутировать ОЧЕНЬ быстро и непредсказуемо в процессе разработки.
Ему следует стараться минимизировать число компонент с изменяемым состоянием.
Re[3]: OOD vs SA/SD (ну или OO vs FP раз уж на то пошло)
Здравствуйте, gandjustas, Вы писали:
G>То что я видел в примерах на haskell, это слабоинтерактивное (консольное) приложение со stateless бизнес-логикой. G>Хотелось бы увидеть всетаки приложение на haskell, которое имеет как минимум все части, указанные выше, о чтобы весь код не был завернут в IO.
G>Хотябы один пример какой-нить записной книжки с двумя таблицами в БД и формочкой.
Хм.. gitit? Вообще на happstack периодически встречал приложения. Ну web и web. Всё перечисленное присутствует. Неинтересно же.
С десктопным UI встречал чисто функционального мало, обычно это что-то на gtk2hs.
Если неважно, чтобы было всё и сразу, то есть куча проектов с замечательным кодом (см. например, xmonad).
Вообще, чего хочется? Приложений, написанных на Haskell, или увидеть, как писать код на Haskell, разбивая на куски?
Но это грубо. Если второе, то зачем "минимум все части"? Достаточно понять как пишется каждая часть и как они объединяются. Каждая часть — см. RWH. Объединять — я уже говорил про трансформеры.
Монады — всего лишь удобный в Haskell механизм декомпозиции. Сама типизация позволяет уже делать многое. Написал ты, например,
newtype ConnectionMonad a = ConnectionMonad { runConnection :: ReaderT DB.Connection IO a }
deriving (Functor, Monad)
и конструктор закрыл. Теперь внутри этой монады ты можешь использовать только функции работающие с ней. А ими ты ограничиваешь то, что тебе нужно. Например, не даёшь доступ к смене соединения. Или позволяешь запускать запросы только в транзакции (определив монаду транзакции и написав, метод, грубо — TransactionMonad a -> ConnectionMonad a). Если последнее неважно, то можно определить метод inTransaction :: ConnectionMonad a -> ConnectionMonad a.
То же с сетью, обработкой ошибок, своим протоколом, ui, бизнес-логикой. Монада может использовать другую монаду, они могут нанизываться друг на друга с помощью трансформеров, не зная о том, какая конкретно монада используется.
Re: OOD vs SA/SD (ну или OO vs FP раз уж на то пошло)
Мне понравилось, как Армстронг изложил подход к программированию (на Эрланге) в своей диссертации: http://citeseerx.ist.psu.edu/viewdoc/summary?doi=10.1.1.3.408 . Вкратце:
— Отделяем функциональные требования от нефункциональных (ну и вообще разделяем все ортогональные требования).
— Функциональные требования реализуем в чистом функциональном стиле, преимущественно в виде автоматов, попутно содомируя себя тайпчекером.
— Максимум нефункциональных требований реализуем декларативно, с использованием встроенных возможностей OTP: применяем к автоматам поведения, строим иерархию супервизоров и кладем все это в "приложения" и "релизы".
— Если припрет, пишем свои поведения с применением максимального количества грязных хаков и оптимизаций. Покрываем тестами.
— Армстронг еще говорил что-то насчет интеграционного тестирования, но я пока не готов от себя пересказать его мысль — лучше прочитать оригинал.
Если вернуться к проектированию, то в таком сценарии вся функциональщина действительно отправляется на самое дно — на уровень деталей реализации. Остальная часть системы, по-моему, вполне может уложиться в твое видение ООП.
Re[3]: OOD vs SA/SD (ну или OO vs FP раз уж на то пошло)
Здравствуйте, FR, Вы писали:
FR>Ты тут не совсем прав, вернее совсем не прав. В прототипировании динамика гораздо лучше статики.
Я пробовал — длительное время прототипировал в python. Нет у динамики преимуществ, по сравнению со статическими языками, позволяющими так же кратко записывать мысль. Сейчас для прототипирования использую Haskell и Scala.
FR>"Огромное" количество тестов в реальных системах оказывается вполне сопоставимым с количеством тестов FR>для статических языков.
Даже для Java с generics не надо писать столько тестов, сколько для python.
FR>Быстрое мутирование данных как раз проще в динамике за счет того что там эти данные более гибкие.
И огребаем кучу проблем, потому что expression problem никуда не девается, а компилятор уже не показывает ошибки Или покажи пример.
Re[2]: OOD vs SA/SD (ну или OO vs FP раз уж на то пошло)
Здравствуйте, lomeo, Вы писали: L>Сразу — динамически типизированные языки требуют бОльших телодвижений при создании "систем нетривиальной сложности".
Существует мнение, что единственный ФЯ, на котором создана система нетривиальной сложности — это эрланг. Полностью с ним соглашаться, наверное, не стоит: тогда пришлось бы признать, например, ghc, тривиальной системой и попутно нагенерить с десяток страниц в КСВ. Но принять во внимание, наверное, стоило бы.
Re[3]: OOD vs SA/SD (ну или OO vs FP раз уж на то пошло)
Здравствуйте, Mr.Cat, Вы писали:
L>>Сразу — динамически типизированные языки требуют бОльших телодвижений при создании "систем нетривиальной сложности". MC>Существует мнение, что единственный ФЯ, на котором создана система нетривиальной сложности — это эрланг.
Не противоречит.
Re[4]: OOD vs SA/SD (ну или OO vs FP раз уж на то пошло)
Здравствуйте, lomeo, Вы писали:
L>Здравствуйте, gandjustas, Вы писали:
G>>То что я видел в примерах на haskell, это слабоинтерактивное (консольное) приложение со stateless бизнес-логикой. G>>Хотелось бы увидеть всетаки приложение на haskell, которое имеет как минимум все части, указанные выше, о чтобы весь код не был завернут в IO.
G>>Хотябы один пример какой-нить записной книжки с двумя таблицами в БД и формочкой.
L>Хм.. gitit? Вообще на happstack периодически встречал приложения. Ну web и web. Всё перечисленное присутствует. Неинтересно же.
(после беглого просмотра) Конечно неинтересно, там все завернуто в IO. Все описанные выше преимущества идут лесом. Остается чистое ФП на уровне тел методов.
L>С десктопным UI встречал чисто функционального мало, обычно это что-то на gtk2hs.
Ну а ссылки на пример?
L>Если неважно, чтобы было всё и сразу, то есть куча проектов с замечательным кодом (см. например, xmonad).
Там далеко не все к сожалению.
L>Вообще, чего хочется? Приложений, написанных на Haskell, или увидеть, как писать код на Haskell, разбивая на куски?
Ну я написал выше какие части хочется увидеть. Без них ни одно крупное приложение не существует.
L>Если второе, то зачем "минимум все части"? Достаточно понять как пишется каждая часть и как они объединяются.
Важно все вместе. Потому что иначе получается "ФП на уровне тел методов". Посмотри форум по архитектуре. Самые интересные вопросы возникают не в том как написать BL, а в том как совместить BL, DAL и много другие аббривеатур из умных книжек.
L>Монады — всего лишь удобный в Haskell механизм декомпозиции. Сама типизация позволяет уже делать многое. Написал ты, например, L>
L>newtype ConnectionMonad a = ConnectionMonad { runConnection :: ReaderT DB.Connection IO a }
L> deriving (Functor, Monad)
L>
L>и конструктор закрыл. Теперь внутри этой монады ты можешь использовать только функции работающие с ней. А ими ты ограничиваешь то, что тебе нужно. Например, не даёшь доступ к смене соединения. Или позволяешь запускать запросы только в транзакции (определив монаду транзакции и написав, метод, грубо — TransactionMonad a -> ConnectionMonad a). Если последнее неважно, то можно определить метод inTransaction :: ConnectionMonad a -> ConnectionMonad a.
L>То же с сетью, обработкой ошибок, своим протоколом, ui, бизнес-логикой. Монада может использовать другую монаду, они могут нанизываться друг на друга с помощью трансформеров, не зная о том, какая конкретно монада используется.
Ну это теория, нужен прмиер. Что-то вроде MVC Music Store или Nerd Dinner
Re[5]: OOD vs SA/SD (ну или OO vs FP раз уж на то пошло)
Здравствуйте, gandjustas, Вы писали:
L>>Хм.. gitit? Вообще на happstack периодически встречал приложения. Ну web и web. Всё перечисленное присутствует. Неинтересно же. G>(после беглого просмотра) Конечно неинтересно, там все завернуто в IO. Все описанные выше преимущества идут лесом. Остается чистое ФП на уровне тел методов.
Ты про gitit или happstack? В gitit используются свои монады. В happstack, кажется, для обработчиков тоже были свои. Плюс посмотри на другие фреймворки — turbinado, snap.
L>>С десктопным UI встречал чисто функционального мало, обычно это что-то на gtk2hs. G>Ну а ссылки на пример?
Зачем тебе это? Там обычный код, как и в других приложениях с использованием qt/wx/gtk.
Я не могу тебе в этом помочь. Встречающийся мне код ничем не отличался от, например, питоновского. Потом, я не сильно интересуюсь GUI, у меня из GUI-шных прог то только оракловый клиент, im, да браузер.
L>>Вообще, чего хочется? Приложений, написанных на Haskell, или увидеть, как писать код на Haskell, разбивая на куски? G>Ну я написал выше какие части хочется увидеть. Без них ни одно крупное приложение не существует.
Это я понял. Цель какая?
L>>Если второе, то зачем "минимум все части"? Достаточно понять как пишется каждая часть и как они объединяются. G>Важно все вместе. Потому что иначе получается "ФП на уровне тел методов". Посмотри форум по архитектуре. Самые интересные вопросы возникают не в том как написать BL, а в том как совместить BL, DAL и много другие аббривеатур из умных книжек.
Здравствуйте, Sinix, Вы писали:
S>Я бы сначала сосредоточился на задаче и на наиболее подходящих инструментах, а уж затем использовал выбранные инструменты общепринятым способом.
Вот и объясните какой общепринятый способ архитектурного дизайна для функциональных программ. А уж сосредоточиться на задаче — это моя забота.
Re[2]: OOD vs SA/SD (ну или OO vs FP раз уж на то пошло)
Здравствуйте, c-smile, Вы писали:
CS>Здравствуйте, borisman3, Вы писали:
CS>Посыл не верный изначально. Причем еще на уровне противопоставления OOP и FP.
CS>Твое сообщение но как бэ другими словами: CS>
CS>Люди, я аццкий специалист в области изготовления мебели стамесками.
CS>Но вот увидел в Канадской Шине еще рубанки. И появилось у меня желание сделать мебельный
CS>гарнитур X (большой) сугубо рубанком. Но что-то меня берут сомнения вырезания пазов оным инстрУментом.
CS>Я в отчаянии. Или у вас таки будет бальзам для меня или получается фигня этот ваш рубанок.
Все ПОЧТИ так как Вы описали. Только почему Вы воспринимаете все так негативно ? Ну умеет человек изготавливать мебель стаместками. Ну хочет попробовать рубанок. Ну объясните ему куда ему не следует совать руки.
И вообще, если можно — по существу. Вопрос был все таки не "Как мне писать функциональные программы ?". Вопрос (упрощенно) был : "Как мне создавать архитектуру функциональных программ ?"
Re[2]: OOD vs SA/SD (ну или OO vs FP раз уж на то пошло)
Спасибо за развернутый ответ, но, к сожалению, Вы в основном отвечали не на мой вопрос. Уж извините.
Мне главное понять КАК я должен строить АРХИТЕКТУРУ функциональных приложений и возможно ли это в принципе. Пока что (почитав примеры дизайна из Ваших ссылок) я вижу что для построения крупномасштабной архитектуры функциональщики вынуждены бить систему на модули с внутренним состоянием и передавать абстрактные типы данных. Все это очень хорошо, но это типичный OOD подход. Как он кореллирует с FP — я без понятия.