LINUX.ORG.RU

Вышел 2-й выпуск журнала «Практика функционального программирования»

 , , ,


1

0

Вышел в свет второй выпуск журнала «Практика функционального программирования».

Центральная тема второго выпуска журнала — демонстрация применения функционального программирования в реальных, а не академических проектах.

Первые четыре статьи — Дмитрия Зуйкова, Дмитрия Астапова, Сергея Зефирова в соавторстве с Владиславом Балиным, и Алексея Отта — вытаскивают на поверхность «кухню» нескольких компаний. Статьи демонстрируют, что функциональные языки находят применение в промышленном программировании в самых разных нишах. Конечно, использование «нестандартных» языков накладывает на проекты некоторые сложно оценимые риски, и далеко не все из них рассмотрены в статьях. Но если статьи этого номера позволят развеять хоть часть сомнений, мифов и предрассудков и поднять дискуссию о применимости функциональных языков в промышленном программировании на новый уровень, мы будем считать свою задачу выполненной.

Статья Александра Самойловича рассматривает создание на языке Erlang игрушечного, но практичного проекта — рекурсивного многопоточного обходчика сайтов. К третьему выпуску журнала мы планируем подготовить ещё несколько статей про Erlang.

Завершающая статья Романа Душкина в большей степени ориентирована на теорию: она познакомит вас с концепцией алгебраических типов данных (АТД) в Haskell и других функциональных языках.

>>> Подробности

★★★★★

Проверено: maxcom ()
Последнее исправление: maxcom (всего исправлений: 1)

Ответ на: комментарий от pitekantrop

> Смысл ещё раз готовить? Это будет тот же самый омлет. :)

Ну это верх естественности мышления - несколько раз приготовить один и тот же омлет из одних и тех же яиц.

anonymous
()
Ответ на: комментарий от r

> Берем классический объект на императивном языке с состоянием

>class Egg { >state: {raw | broken | fryed} = raw >}

>Чем он характеризуется? Тем что этот можно присваивать как попало и когда попало.

Ерунда. Делаешь метод set_state или проперть state, и проверяешь.

> Раскажи мне как ты в real world (tm) переведешь яйцо из сосяния разбитое в состояние целое? Или разжаришь обратно?

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

> куча всяких проверок инвариантов в обекте с состоянием возникает именно потому что "неизвестно кто тут побывал до нас".

Тоже ерунда. Проверка инварианта выполняется при переходе. В случае ФП, IIUC, она происходит при создании нового объекта, но проверка всё равно делается.

>>Статическая типизация и выразительные системы типов рулят, кто же спорит.

>Так дело тут именно в таких немаловажных свойствах как иммутабельные данные и чистые функции.

А мне кажется, дело в том, что ФП создает тупо больше типов :) Конечно, ADT дает эффективный инструмент работы с ними, но всё же.

>>20 лет назад примерно такой хайп стоял вокруг ООП.

>"И теперь они везде" (C)....:)

Только вот не сказать, что Ява или Си++ сильно похожи на Смолток. А так да, я хочу pattern matching, ADT в любом языке. Еще лямбды и замыкания, но они к ФП относятся постольку поскольку.

tailgunner ★★★★★
()
Ответ на: комментарий от r

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

Кстати, да. Весьма показателен тот факт, что подавляющее большинство человеков не могут осилить указатели. Это вовсе не означает, что все они глупы (как считают программисты-снобы). Просто эта концепция совершенно противоестественна, так что программисты во многом люди с искалеченным мышлением. В этом согласен с r на 100%. Слава роботам, так сказать. Другое дело, что без понимания низкоуровневых концепций по-прежнему далеко не уедешь. Будешь жестоко бит законом дырявых абстракций. Но эволюция потихоньку идёт, и высокоуровневые языки становятся всё более совершенными. Думаю, будущее всё же за ФП языками с небольшим набором строго изолированных императивных возможностей. Нынешнее засилье императивщины - это скорее инерция индустрии. Тем не менее, идеи ФП ныне активно проникают даже в наипопсовейший мейнстрим. Это о многом говорит.

anonymous
()
Ответ на: комментарий от tailgunner

>Ну вот такой граф переходов у яйца

Ты как-то свел всю задачу в область автоматного программирования. Хотя пример неудачный.

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

Весь вопрос в том стоит ли так жестоко смотреть на типы данных? Р. Душкин в сабже с таких позиций тоже не смотрит. И Одерский в Scala, например, тоже так не смотрит.

Хочешь смотреть на ADT, как на кортеж ну и смотри, кто ж тебе мешает-то?

Весь сыр-бор он из-за того, что на функции можно смотреть как на кортеж, а можно как на функцию, которая возвращает функцию, которая в конечном итоге чего-то возвращает.

Macil ★★★★★
()
Ответ на: комментарий от Macil

>>Ну вот такой граф переходов у яйца

>Ты как-то свел всю задачу

Всю? O_O

> в область автоматного программирования.

Это какой-то локально знаменитый баззворд?

> Хочешь смотреть на ADT, как на кортеж ну и смотри

Что за бред? С чего ты взял, что я этого хочу?

> ну и смотри, кто ж тебе мешает-то?

Может, то, что я не считаю ADT кортежем?

tailgunner ★★★★★
()
Ответ на: комментарий от tailgunner

>Это какой-то локально знаменитый баззворд?

Да нет. Просто можно рассматривать программу как конечный автомат. ООП которое event-based типа C++, как я понимаю, делает примерно то же самое.

>Что за бред? С чего ты взял, что я этого хочу?


>>А так да, я хочу pattern matching, ADT в любом языке. Еще лямбды и замыкания, но они к ФП относятся постольку поскольку.


Ну типа в этих языках ADT рассматривается как кортеж, а не как функтор.

Macil ★★★★★
()
Ответ на: комментарий от anonymous

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

Можно и ногу гвоздем к полу прибить. Зачем? (в императивном подходет это специально делать не надо - такая лажа идет в упаковке)

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


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

r ★★★★★
()
Ответ на: комментарий от anonymous

>Да блин, может яйцо оказаться не в том состоянии, совершенно спокойно (см предыдущий ответ),- и проверки все-равно нужны.

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

r ★★★★★
()
Ответ на: комментарий от tailgunner

>Делаешь метод set_state или проперть state, и проверяешь.

Не ерунда. Этот подход изолирует только внешний мир - что мешает написать еще десяток методов в этом объекте (без сомнения полезных и функциональных). Как ты будешь дальше гарантировать что эти методы "до нас" никто не вызывал и они не перехерачили объект до неузнаваемости?

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

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

>Ну вот такой граф переходов у яйца, я же не виноват в том, что вы выбрали такой пример.


Wake Up, Neo. Welcome to the Real World :)

>В случае ФП, IIUC, она происходит при создании нового объекта, но проверка всё равно делается.


Вон там пример с яйцами сломать его изнутри можно только в одном месте (надо одну строчку дописать чтобы полечить). Сннаружи он железобетонный. Сколько с++нго кода займет реализовать то же самое?

>А мне кажется, дело в том, что ФП создает тупо больше типов :)


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

Представь что у тебя в обектном языке на уровне языка поддерживалось бы.

e = new RawEgg().break()

где
e instanceOf BrokenEgg
e !instanceOf RawEgg
e hasNot method break //поэтому семантика нового иснстанса d ghbywbgt не позволит все сломать

То есть транзишены объектов (как это и происходит в реальном мире - скорлупа это уже не яйцо). На императивном объектном языке таку шнягу можно реализовать. Вся фигня в том в результате будем иметь немутабельные структуры данных и чистые функции - тадаа!:)) Только в крайне неудобной форме.

То есть декларация иммутабельных структур + набор чистых функций - дает _полное_ описание модели данных. А злобный компилятор камла при паттерн матчинге даже ругаться умеет на тему "перечислены не все возможные варианты".

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

>Только вот не сказать, что Ява или Си++ сильно похожи на Смолток.


Они пытаются исправиться:)


r ★★★★★
()
Ответ на: комментарий от r

> То есть декларация иммутабельных структур + набор чистых функций - дает _полное_ описание модели данных.

Забавно, но я кажется только сейчас начинаю осознавать ключевую идею ФП. Вот ведь как бывают полезны флеймы. В терминах ООП получается, что каждый мутатор - это фабрика новых объектов отличного или того же типа. Верно я понимаю? Тогда такое можно на любом ОО-языке замутить. Правда это будет дорого для больших структур. Как это оптимизируют в чистых ФП языках?

//yet another anonymous

anonymous
()
Ответ на: комментарий от r

>А мне кажется, дело в том, что ФП создает тупо больше типов :)

>>Да - иммутабельных типов


В ООП используется концепция класс-как-тип. Наверно самая плохая черта ООП. Правда, можно насоздавать пустых static классов (Александреску так делает) и молиться чтобы компилятор оптимизировал. И ессно в уме программистов возникает цепочка, если класс - значит объект, если объект - то память и накладные расходы на создание.

И самое смешное что в любом уважающем себя ООП проекте типов обычно более 9000, если считать классы, интерфейсы, обертки интерфейсов и т.п. за таковые.

Лично мне было странно осознавать тот факт, что типы в ML языках существуют "просто так" без интерфейса и не определяют поведение.

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

Macil ★★★★★
()
Ответ на: комментарий от anonymous

>осознавать ключевую идею ФП

Ключевая идея ФП - все есть функция.

В ФП нет объектов, в том виде как это понимается в ООП. В ООП класс тройственен: класс-как-тип, класс-как-интерфейс и класс-как-поведение. Классы выстраиваются в иерархию, по "общности" признаков, т.е. иерархическая декомпозиция предметной области.

Соответственно, каждый класс в иерархии определяет тип, часть интерфейса, и часть поведения объекта.

За все ФП языки не скажу, но в ML-подобных такого просто нет. Там тип, интерфейс и поведение существуют абсолютно независимо. Именно поэтому идиомы из мира ООП в таких языках не только бесполезны, но иногда и вредны.

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

Macil ★★★★★
()
Ответ на: комментарий от anonymous

> В терминах ООП получается, что каждый мутатор - это фабрика новых объектов отличного или того же типа. Верно я понимаю?

ИМХО, ключевая идея как раз в том, что мутатор производит объект другого типа, и система типов позволяет отлавливать ошибки, связанные с неправильным использованием.

tailgunner ★★★★★
()
Ответ на: комментарий от anonymous

> Весьма показателен тот факт, что подавляющее большинство человеков не могут осилить указатели. Это вовсе не означает, что все они глупы (как считают программисты-снобы).

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

anonymous
()
Ответ на: комментарий от r

> Инкапсуляция в смысле сокрытия роеализации - это как договор программиста сам с собой что он в функцию факториала просто не будет передавать отрицательные числа - тогда и ошибок не будет.

Да ты чо? Т.е. в функции факториала на фп языке _не надо_ проверять передается ему отрицательное число или нет, там это делается все automagically?

[code]

fact :: Integer -> Integer fact 0 = 1 fact n = n * fact (n-1)

*Main> fact (-5) -5 *Main> fact (5) 120 [/code]

> Вон там пример с яйцами сломать его изнутри можно только в одном месте (надо одну строчку дописать чтобы полечить). Сннаружи он железобетонный. Сколько с++нго кода займет реализовать то же самое?

что значит сломать "изнутри" или "снаружи"? Почему ты все время сравниваешь с намного более низкоуровневым си++ и явой? Я тебе с таким же успехом могу доказать превосходство ооп, сравнивая ассемблер и си++.

> На императивном объектном языке таку шнягу можно реализовать. Вся фигня в том в результате будем иметь немутабельные структуры данных и чистые функции - тадаа!:))

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

anonymous
()
Ответ на: комментарий от anonymous

> Да ты чо? Т.е. в функции факториала на фп языке _не надо_ проверять передается ему отрицательное число или нет, там это делается все automagically?

Перечитай еще раз то что я сказал.

>Почему ты все время сравниваешь с намного более низкоуровневым си++ и явой?


Я сравниваю с императивными языками. Межешь попробовать более "высокоуровневыми". Суть в том что реализация должна быть парадигматическая.

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


Ты можешь хооть одну фразу обосновать - а не оборвать на месте "это не так". А как?

r ★★★★★
()
Ответ на: комментарий от r

> Не ерунда. Этот подход изолирует только внешний мир - что мешает написать еще десяток методов в этом объекте (без сомнения полезных и функциональных). Как ты будешь дальше гарантировать что эти методы "до нас" никто не вызывал и они не перехерачили объект до неузнаваемости?

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

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

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

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

anonymous
()
Ответ на: комментарий от r

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

Для меня функция в данной ситуации - полностью черный ящик, т.е. я не могу менять внутренности функций и должен что-то подавать на вход?

anonymous
()
Ответ на: комментарий от anonymous

>Точно также, тебе нужно будет это проверять.

О май факин год.

10 x = some
20 y = f1(x)
30 z = f2(x, y)

Если это хаскел я тебе точно скажу чему равен X в третьей строчке. Если это императивный язык - ты мне в жизни этого не гарантируешь. Доходчиво?

>Я тебе уже приводил пример с функцией unfry - почему в твоем любимом функциональном хаскелле даже просто возможна фукнция, противорящая т.н. тобой инварианту?


потому что для этого надо _специально_ выстрелить в ногу написав специальную функцию - что ты и сделал. А если приведенный выше код на императивном языке - чему равен X в третьей строче? Правильный ответ - "хрен его знает".

>Причем это будет даже сильнее,


Жду с нетерпением!

>PS. я так и не понял твоего примера с "транзишеном"


Время. Возвращаясь к образу мышления человеку и реальному миру - тут есть такой параметр как время и мир обратно не возвращается. Ты не можешь в реальности вернуть яйцу состояние "целое". А в императивной модели - можешь. Именно потому она лдч человека неестественна - процессы которые проистекают вокруг человека - невозвращаемы. В общем случае объекты не переходят в любые "состояния" в которых потенциально могут быть. Потому что по сути это _не состояния_. Это превращение одних объектов в другие.

r ★★★★★
()
Ответ на: комментарий от r

> А зачем ты при этом хочешь чтобы это был один объект с двумя состояниями? Ты можешь это откатить? Ты представь какой ужас с точки зрения валижной инвариантности - уже три приведенных состояния (целые, разбиты, пожарены) - это просто немеряно надо контрактов энкапсулировать чтобы проконтролировать валидность транзишена этих объектов. Что жаренные не могут быть неразбитыми, или стать обратно целыми. Это ж целый жизненный цикл - немеряно кода.

Тебе точно также надо это контролировать и в фп (например, в функции broke нужно контролировать, чтобы были поданы вход именно сырые яйца, если они сырые, то так их и оставить, и тд, а вот жаренные не пройдут; в функции нагревания тебе нужно контролировать была ли нагрета до этого сковорода или нет - чтобы не нагреть ее еще раз). Ну не различаются в этом отношении функциональная и императивная парадигма. Они отличны в самом понимании процесса вычислений - для фп - всякая программа - это вычислительный процесс в его математическом понимании (фукнция, которую можно сколько угодно раз вычислять, она будет всегда давать один и тот же результат; объекты (entity) - числа, отличающиеся только своим значением, т.е. не чисел 7 - только одно, и одно число 7 не отличается от другого числа 7, и нельзя его изменить, оно всегда существует и существовало до это в математической вселенной, можно только получить из одного числа другое - функции), в императивном же - все не так - там как в жизни - одно яйцо отличается от другого и их не одно в императивной вселенной - а множество, причем ограниченное, и каждое обладает состоянием, можно одно яйцо разбить, а другое оставить сырым, состояние и identity представляют собой вместе не что иное, как деструктивное присваивание.

anonymous
()
Ответ на: комментарий от anonymous

>Для меня функция в данной ситуации - полностью черный ящик, т.е. я не могу менять внутренности функций и должен что-то подавать на вход?

Скажем можешь делать что угодно кроме вмешательства в последовательность - то есть должны сохранится потоки данных.

В том то и дело!

Допустим у нас не хаскель а императивный язык но данные иммутабельны а функции чистые:

egg = break eggs
pan = heat pan
omelet = cook eggs pan

Что тут можно сделать? Ничего - даже механизмов таких нет.

Теперь представим что яйцо и сковорода это объекты с состоянием:

egg = break eggs {eggs.state = broken}
eggs.state = raw //ах я нехороший
pan = heat pan {pan.state = hot}
pan.state = cold //я злобный параллельный тред
omelet = cook eggs pan {if (pan.hot) {eggs.state = fried} else WTF}
omelet.state = протухли //плюнем в яичницу клиента который не дает чаевых

Понял в чем суть? Потоков данных - алгоритма - я не нарушал. Я делаю валидные издевательства над сайд эффектами.

Более реальный пример:

есть код

l = 1,2,3,4,5 //список чисел
l1 = выбрать_четные(l)

задача: добавить строку котора отбирает каждое третье число.

"Функциональный ответ": можем использовать тот же l? - да:
l2 = thirds(l)

"Императивный ответ": можем использовать тот же l? - а хрен его знает - смотреть надо что выбрать_четные с ним делает - вдруг он его мутирует?

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

Знаешь что я первое сделал в первой версии своего фреймворка коллекций для жабы? Аналог статических методов Collections.sort,reverse и Arrays.sort, и прочие sublist которые _не портят_ исходный список! Потому что задолбался заниматься клонированием всяких коллекций в "данных" перед каждой сортировкой для вывода на экран.

"А вы знаете такой паттерн - когда возвращаете коллекции наружу надо оборачивать их другой коллекцией - чтобы наружа вам не попортила внутренние данные".

Тьфу!

r ★★★★★
()
Ответ на: комментарий от anonymous

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

Ты похоже в корне не понимаешь каким свойствами обладает состояние и какие с этим связаны проблемы. При чем тут нужно ли проверять это в функциях? В жизни ты можешь точно так же попытаться пожарить яйца не разбивая их.

После того как ты разобьешь яйцо - ты не можешь нарушить его инвариант в жизни. Ну никак!!! Все баста!

Точно так же
e <- broke Egg Raw = Egg Broken

Все - e - это разбитое яйцо - хана - капут - ничего не вернуть назад следующей операцией для значения которое баунд в символ e! Все как в жизни.

А в императивной модели я злобно пишу e.state = целое - и обана - яйцо целое!

Это что - не очевидно?


r ★★★★★
()
Ответ на: комментарий от r

> в императивной модели я злобно пишу e.state = целое - и обана - яйцо целое!

> Это что - не очевидно?

Это просто клевета.

tailgunner ★★★★★
()
Ответ на: комментарий от r

> А в императивной модели я злобно пишу e.state = целое - и обана - яйцо целое!

Это легко обходится, но конечно же, не в ужасной яве. На остальное завтра отвечy, когда протрезвею и приду в себя.

anonymous
()
Ответ на: комментарий от tailgunner

set_state?

Возражения:

1. В хаскельном коде ты в принципе ничего не сделаешь значению, точно так же как не можешь ничего сделать со значением x = 2 (тут к стати напрашивается вопрос - почему обращение с примитивным данными в таком виде естественно, а возможность создавать такие типы самому звучит странно? Вы ж не задаетесь вопросом в коде x = 2 + y / b что это за указатели и чего там за объекты и в каком они состоянии? И уже почти не задаетесь вопросами в "aaaa" + "bbbb". И практически уже не беспокоитесь что собственно с объектом range в for x in xrange(1,10).... Так почему же долбаные яйца нужны вам с состоянием? Покажите мне хоть один плюс этого.

2. Что насчет "внутри" скоупа в котором определен set_state? Там ничего плохого с this.state случится не может разве? - это про этот момент я говорил к про договор программиста сам с собой. Инкапсуляция для проверки инвариантов это как раз такой договор. Я сам с собой договорюсь что в этом скоупе я разрушать ничего не буду - и ладушки?:)

3. Я злобный рефлективный интроспектор....прячусь в параллельном треде и жду своего часа чтобы сделать setAccessible(true) и...муа-ха-ха....

PS: серьезные только первые 2:)

r ★★★★★
()
Ответ на: комментарий от anonymous

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

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

anonymous
()
Ответ на: комментарий от anonymous

> декомпозиция программы на части - это все свойственно _только_ функциональщине, неужели?

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

ugoday ★★★★★
()
Ответ на: комментарий от anonymous

> Я могу перекрасить кровать - и для меня это будет все та же кровать, просто изменившая свое состояние.

Вот в упор не понимаю в чём разница между "та же кровать, только покрашенная" и "точно такая же кровать, только покрашенная".

> в моей программе можно одно "яйцо" (переменную, объект) от другого отличить благодаря identity, т.е. ведут они себя более естественно для человека,

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

ugoday ★★★★★
()
Ответ на: комментарий от anonymous

> Функциональный повар понимает, что он делает, а императивный - нет.

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

ugoday ★★★★★
()
Ответ на: комментарий от ugoday

> Когда встречается конструкция вида x++ инкримент ничего не знает об объекте, состояние которого он модифицирует, не знает когда он введён и когда понадобится

А истинно-функциональный факториал по-вашему, знает, зачем его вычисляют? Знает, когда ввели его аргумент и когда он понадобиться? То есть это функция факториала решает вычислиться или всё-таки кто-то внешний решает её вычислить?

Вообще, что значит "оператор не знает ничего об объекте", "не знает когда он введён и когда понадобиться"? Иными словами, яйцо знает, что оно когда-то попадёт в продакшн к повару?

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

Императивный повар знает ровно столько же сколько и функциональный. Просто мыслят они по-разному. Вы почему-то считаете, что взгляд на вещи сверху присущ только ФП-подходу, а императивный подход ограничиваете до взгляда "из конкретной строчки кода".

f(x) = x + 2 g(x) = x + 3 f(g(x))

Вот тут вы говорите, что повар (f) уже всё знает о том что и зачем он делает. А точно такой же императивный повар вами ограничивается до f = .., g = .. и только потом вы ему даёте право знать про f(g(x)). Императивная же программа так не исполняется, если она написана нормально.

kkw
()
Ответ на: комментарий от tailgunner

> Потому что Real World (tm) так работает.

У вас извращённое (я даже догадываюсь чем) представление об оном RW.

ugoday ★★★★★
()
Ответ на: комментарий от kkw

> Вы почему-то считаете, что взгляд на вещи сверху присущ только ФП-подходу, а императивный подход ограничиваете до взгляда "из конкретной строчки кода".

Истинно так. Для ФП взгляд сверху органично вытекает из подхода к написанию программ. В императивном случае это нечто наносное, внешнее, заимствованное. Даже прямо скажу, заимствованное скорее всего из ФП.

ugoday ★★★★★
()
Ответ на: комментарий от ugoday

> В императивном случае это нечто наносное, внешнее, заимствованное.

Но это же враньё чистой воды. Императивные программы точно также можно разрабатывать сверху-вниз.

kkw
()

Пожалуй следует пояснить

Человек мыслит декларативно. Это аксиома, лежащая в основе всех последующих рассуждением.

1. Человек делит задачи на подзадачи.

2. Каждая задача мыслится как процесс преобразования некоего ресурса в результат.

3. Поскольку результат одной подзадачи может восприниматься как потребный ресурс для другой задачи взаимоотношение подзадач можно описать с помощью орграфа.

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

5. Как правило (хотя и не обязательно) иерархия строится таким образом, что родительский узел "знает" что нужно сделать, а дочерний --- как это сделать. При этом на нижнем уровне отношение сохраняется. То что для родительского узла явлется "знанием как", для дочерних дочернего явлеяется "знанием что".

6. Дочерний узел всё, что нужно для его исполнения, получает от родительского узла. И весь результат своей работы отдаёт ему же.

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

8. Откуда же тогда возникли переменные, циклы, счётчики и прочие артефакты нечеловеческого мышления? Дело в том, что первые компьютеры были беспредельно тупыми реализациями машины Тьюринга. Оная же МТ читает инструкции одну-за-одной. И для того, чтобы можно было с ней работать приходится человеческий-древовидный алгоритм проецировать на линию ленты МТ. И теперь, чтобы эмулировать граф-подзадач посредством цепочки операторов, приходится вводить понятие объектов с изменяемым состоянием.

9. Всё.

ugoday ★★★★★
()
Ответ на: комментарий от kkw

> Императивные программы точно также можно разрабатывать сверху-вниз.

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

ugoday ★★★★★
()
Ответ на: комментарий от tailgunner

> Я хочу, чтобы они перешли в состояние "разбиты" :)

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

А для приготовления яишницы лучше подходят функциональный программы.

ugoday ★★★★★
()
Ответ на: комментарий от r

После того как ты разобьешь яйцо - ты не можешь нарушить его инвариант в жизни. Ну никак!!! Все баста!

ага

Все - e - это разбитое яйцо - хана - капут - ничего не вернуть назад следующей операцией для значения которое баунд в символ e! Все как в жизни.

А в императивной модели я злобно пишу e.state = целое - и обана - яйцо целое!

Это что - не очевидно?

Нет.

Вот реализация яйца, которое нельзя из скорлупы превратить в целое.

class Egg(object):                                                             
    def __init__(self):                                                        
        self._states = ['raw', 'broken', 'fried', None]                        
                                                                               
    @property                                                                  
    def state(self):                                                           
        return self._states[0]                                                 
                                                                               
    @state.setter                                                              
    def state(self, value):                                                    
        if value and value == self._states[1]:                                 
            self._states.pop(0)                                                
        else:                                                                  
            raise AttributeError ("wrong state")                               
                                                                               
    def break_eggs(self): self.state = 'broken'   

>>> e = Egg()                                                                  
>>> e.state                                                                    
'raw'                                                                          
>>> e.state = "foo"                                                            
Traceback (most recent call last):                                             
  File "<stdin>", line 1, in <module>                                          
  File "/tmp/python-29636EdM.py", line 14, in state                            
AttributeError: wrong state                                                    
>>> e.state = 1                                                                
Traceback (most recent call last):                                             
  File "<stdin>", line 1, in <module>                                          
  File "/tmp/python-29636EdM.py", line 14, in state                            
AttributeError: wrong state               
>>> e.state = "broken"                                                         
>>> e.state = "raw"                                                            
Traceback (most recent call last):                                             
  File "<stdin>", line 1, in <module>                                          
  File "/tmp/python-29636EdM.py", line 14, in state                            
AttributeError: wrong state          
>>> e.state                                                                    
'broken'                                                                       
>>> e.state = "fried"                                                          
>>> e.state                                                                    
'fried'                                                                        
>>>                

Думаю полностью код приготовления яичницы (омлета) приводить не стоит?

anonymous
()
Ответ на: комментарий от r

> точно так же как не можешь ничего сделать со значением x = 2

> Вы ж не задаетесь вопросом в коде x = 2 + y / b что это за указатели и чего там за объекты и в каком они состоянии?

Да блин, потому что это числа, у них нет никакого состояния, я это уже не раз повторял.

> И практически уже не беспокоитесь что собственно с объектом range в for x in xrange(1,10)

Я не беспокоюсь, но x там каждую итерацию мутирует :)

> Что насчет "внутри" скоупа в котором определен set_state? Там ничего плохого с this.state случится не может разве? - это про этот момент я говорил к про договор программиста сам с собой. Инкапсуляция для проверки инвариантов это как раз такой договор. Я сам с собой договорюсь что в этом скоупе я разрушать ничего не буду - и ладушки?:)

Это иллюзия, скажите спасибо гипножабе.

anonymous
()
Ответ на: комментарий от anonymous

> Дык, это проблемы, которые решает статическая типизация.

Дык, это проблемы, которые решает статическая типизация вкупе с ADT, а я только за статическую типизацию и ADT в языках программирования.

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

Вот конкретно хаскель выигрывает у жабы за счет таких мощных вещей как ADT, pattern matching, HOF и list comprehansion - он просто содержит больше более высокоуровневых абстракций, чем жаба.

> Вопрос ведь не в том, можно ли отстрелить себе ногу. Это можно всегда и везде. Вопрос в том, как максимально защититься от таких выстрелов.

Вопрос в том, что фп, immutable data and pure functions overrated. Писать говеные программы на хаскелле точно также легко, как и на любом другом языке.

anonymous
()
Ответ на: комментарий от r

> egg = break eggs

> pan = heat pan

> omelet = cook eggs pan

> Скажем можешь делать что угодно кроме вмешательства в последовательность - то есть должны сохранится потоки данных.

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

> egg = break eggs {eggs.state = broken} > eggs.state = raw //ах я нехороший

Почему ты считаешь, что не нарушаешь здесь алгоритм? ты его нарушаешь самым прямым образом, я точно также могу

egg = break eggs

превратить в

egg = unbreak $ break eggs

И кстати, в варианте, который я запостил раньше - такая штука не пройдет.

> pan.state = cold //я злобный параллельный тред

Вот тут не могу не согласиться, concurrent programming без действительно immutable данных частенько PITA.

> "Императивный ответ": можем использовать тот же l? - а хрен его знает - смотреть надо что выбрать_четные с ним делает - вдруг он его мутирует?

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

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

Да, надо, только проверка входных данных нужна в любом случае - хоть в фп, хоть в ип.

> Знаешь что я первое сделал в первой версии своего фреймворка коллекций для жабы? Аналог статических методов Collections.sort,reverse и Arrays.sort, и прочие sublist которые _не портят_ исходный список! Потому что задолбался заниматься клонированием всяких коллекций в "данных" перед каждой сортировкой для вывода на экран.

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

anonymous
()
Ответ на: комментарий от anonymous

> приводить не стоит?

В приведённом выше коде уровень bondage and discipline вплотную приблизил яйцо к классу неизменяемых объектов.

ugoday ★★★★★
()
Ответ на: комментарий от anonymous

> Да блин, потому что это числа, у них нет никакого состояния, я это уже не раз повторял.

Почему это верно для чисел и ложно для пользовательских (в т.ч. алгебраических) типов данных?

ugoday ★★★★★
()
Ответ на: комментарий от ugoday

> Добавьте туда ещё алгебраические типы данных и механизмы сопоставления с образцом.

Ну не является это исключительными признаками функциональных языков.

> И я не буду иметь ничего против такого "императивного" программирования.

Никто не против хорошего высокоуровнего императивного языка :)

anonymous
()
Ответ на: комментарий от ugoday

> Вот в упор не понимаю в чём разница между "та же кровать, только покрашенная" и "точно такая же кровать, только покрашенная".

Т.е. вы не сможете отличить одну кровать от другой, если они стоят рядом?

anonymous
()
Ответ на: комментарий от anonymous

Если они абсолютно одинаковы (и перемешаны, чтобы исключить координаты из рассмотрения), то не смогу. А вы сможете?

ugoday ★★★★★
()
Ответ на: комментарий от ugoday

> ФП программа --- етсь композиция функций.

Это композиция чистых (математических) функций.

anonymous
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.