Comments 102
Спасибо за перевод. Правда, мне лично этот материал показался тяжеловатым для введения в «функциональное мышление» и как следствие ставящим под сомнение ценность изложенной в нем идеи. Думаю, можно объяснить намного проще.
Думаю, статья направлена на ООП программистов со «средним» уровнем, которые читая это могут сразу примерить освещенные недостатки привычной парадигмы на себе, исходя из личного опыта.
Более плавное начало, думаю, можно найти в Functional Programming for Java Developers
В этой книге тоже Java-подобные языки используются? Мне кажется, в статье совсем не видно преимуществ ФП, поскольку код получается такой же загроможденный. Для изучения ФП стоит взять более удобный язык, чья семантика не тонет в шелухе необходимых конструкций кода. Ну, я лично рекомендую Haskell, однако есть еще Lisp, F# и целый ворох прочих ФЯ.
Полностью согласен, только я бы посоветовал как-раз Common Lisp, потому что на Haskell могут быть проблемы с императивными реализациями примеров.
Никаких проблем — Лисп так Лисп. :) А вот если бы функциональное программирование нормально преподавалось в том же моем ЧитГУ, то язык был бы неважен. Важнее — как преподаватель его преподнесет.
>> Проблема в совершенно новой парадигме программирования — не изучение нового языка.

А тут я бы на месте автора сделал акцент на том, что парадигма нова _для_программиста_. В самом функциональном программировании нет ничего нового, просто раньше интереса к нему было меньше. Лично я это связываю с тем, что репутация ООП со временем подпортилась, а также с появлением многоядерных процессоров.
UFO landed and left these words here
«функциональная парадигма требует постоянного выделения\собирания памяти» — а ООП не требует?
«что, все-таки имеет некоторый эффект как на быстродействие системы, так и на требование к количеству используемой памяти» — выделение памяти и сборка мусора в функциональной среде куда быстрее и экономнее, чем в императивной, так как не неизменяемые данные не нуждаются в копировании и контроле за изменениями. Проблема в другом — на нижнем уровне архитектура таки императивная и нет универсального эффективного интерпретатора.
UFO landed and left these words here
«Естественно требует, но не на каждый чих.»
Именно, что на каждый — создание-уничтожение объекта требует выделение-освобождение памяти.
«Да, она может выделяться и собираться быстрее, нежели в ООП, однако это отнюдь не означает, что данной проблемы нет.»
Есть, но стоит менее остро из-за отсутсвия потребности в копировании сущностей. Сборка мусора для функциональных сред появилась раньше и сейчас более эффективно, чем для ООП. Разница есть только на задачах, которые длительное время используют фиксированные ресурсы — но это уже не ООП, а процедурный подход.
На больших объемах ООП сливает быстрее, чем функциональщина — вторая может требовать нетривиальной оптимизации, а первая рушится под грузом миллионов объектов и синхронизации между потоками. Груз массы объектов лечится только шагом назад — возвратом на критичном уровне к массивами, процедурам и прелестям чистого C.
А груз синхронизации — устранением где только возможно изменяемого состояния, т.е. использованием функциональщины.
Думать, что ООП кушает меньше ресурсов, чем ФП — принципиальная ошибка.
UFO landed and left these words here
Создание-уничтожение на стеке — штука одинаково стремительная. Создание-уничтожение в куче — в ФП быстрее, причем не в одной операции, а в целом. Сущности в ФП далеко не столь краткоживущи как кажется, благодаря неограниченному повторному использованию по ссылкам.
И если приложение по своей природе обладает зверским аппетитом, то кормить придется либо возвратом к процедурами, либо задействуя функциоанльный подход, либо используя и то и другое. ООП же экономии ресурсов железа не обещает ни в каком варианте. От него нет плюсов в плане производительности приложения — только в плане производительности разработки по сравнению с процедурным подходом.
UFO landed and left these words here
Очень спорно все, что вы тут говорите. А говорите вы, в основном, о вещах зависимых (производительность программы, скорость разработки, «аппетит» программы). Они все зависят от конкретной программы и программиста. А вот создание/уничтожение объектов, действительно, критерий, по которому можно судить.
Я говорю о вещах, которые имеют практическое значение. По счетчику созданий-уничтожений судить ни о чем, корме реализации RTL нельзя.
Разумеется, практическое значение эти вещи (которые я назвал «зависимыми») имеют. Однако вот вам пример.

У себя на работе я занимаюсь процессингом телефонного трафика, биллингом, тарификациями и прочим. Появилась задача: распарсить трафик с новых станций. Отлично! Задача для ФП. Поскольку именно в то время я начал изучать Haskell, написал программу на нем. Я точно знаю, что разработка этой программы на С/С++ заняла бы у меня уйму времени. Гораздо больше, чем это ушло на Haskell-программу. Однако у программы был существенный недостаток: на 10000 текствых файлах (в которых примерно по 1000 строк) она жрала полгигабайта памяти и выполнялась минут пять-десять. Что бы вы думали, это виноват язык? И на С/С++ у меня бы получилось лучше? Отнюдь. Просто через два месяца, когда я уже поднабрался опыта, я переписал программу полностью все на том же Haskell. Она получилась в три раза короче, гораздо выразительнее, а самое главное, уже не требовала памяти (ну, мегабайта 4) и выполнялась за 10 секунд все на том же наборе данных. Вывод очевиден: эти показатели (скорость разработки, производительность) — не показатели вовсе. На них разницу в ООП, функциональном и процедурном программировании не покажешь.
было бы интересно узнать, какие ошибки вы совершили по неопытности, и какие приёмы помогли добиться такого ускорения.
Я и так собирался описать это, а тут, пожалуй, даже отвертеться не смогу. :)
в реальной жизни никого не устроит классифицировать число каждый раз. для повышения производительности нужно будет кэширование результатов классификации. а теперь реализуйте это кеширование без использования состояния.
гы. там еще внизу примечание-ссылка на статью с говорящим названием «Совершенная красота и совершенная бесполезность совершенных чисел»

:)
я думаю, во второй части будет сказано, что результат выполнения чистой функции зависит только от ее аргументов и поэтому может быть кеширован самой средой исполнения.
как среда поймёт каков необходимый объём кэша? или кэш будет динамически раздуваться от 0, до всей доступной памяти? а как она поймёт когда кэширование лучше отключить? а когда кеш нельзя чистить? а когда параметров несколько да еще и являются структурами — будет сериализовывать их и склеивать в сложный ключ? а когда кеш нужно сбрасывать на диск чтобы не потерять при перезагрузке?
Ну, я думаю имелось ввиду что-то вроде
g x = f x + f x
то в случае и чистых функций в конкретном выражении можно применить оптимизацию и считать f x только один раз.
ну а в императивном стиле это придётся писать так:
g x = 2 * f x

из этого можно сделать вывод, что функциональная парадигма поощряет копипасту, а императивная — нет %-)
Неправда. Это не императивный стиль, а тоже вполне функциональный. Вот это будет императивный, да и то условно:

g (var x)
{
var tmp1 = f*x
tmp1 = tmp1 * 2
return tmp1
} 
в таком случае функциональный будет так:

g (var x)
{
var tmp1 = f*x
var tmp2 = tmp1 * 2
return tmp2
}

все 3 кода эквивалентны. единственное отличие — мы либо надеемся, что компилятор закеширует вызов функции, либо мы просто не делаем одинаковых вызовов. будем ли мы при этом именовать полученное значение сохраняя в переменную или не будем — не важно.
Да, зачем читать тред, лучше сразу ответить.
Почитайте что такое чистые функцие.
я имел в виду оптимизацию сложных выражений. Заданные же вами задачи, разумеется, не имеют общего решения :)
А как сборщик мусора в ООП-языках понимает, когда пора собирать урожай? Точно также выделяется фиксированный объем памяти.
«а когда параметров несколько да еще и являются структурами — будет сериализовывать их и склеивать в сложный ключ»
у вас определенно устойчивое императивное мышление. функциональные программы не так работают
Думаю, менеджер кеша, поддерживающий работу функциональной парадигмы, вполне может собирать и учитывать статистику времени вычисления значения, частоты вызовов, повторяемость аргументов (ключа), учитывать объёмы этого ключа и результата, сравнивая их с объёмом памяти и диска…

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

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

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

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

Для поддержки изменения первички можно сделать механизм инвалидации кеша при изменении первичных даных.
По-моему, если судить о функциональном подходе в таком ключе, то подойдет любой хороший императивный код. Модульность, разделение на более мелкие функции, разграничение ответственности (классов, функций, модулей), — все это ведет к функциональному виду кода. Я уж молчу про паттерны проектирования: большая их часть под собой имеет функциональную природу. Вывод моих рассуждений, в общем, такой: любой хороший код стремится быть более функциональным и аккуратным. То есть, получается, вы движитесь в правильном направлении. :)
ну, по большому счёту, внутри моих функций может быть и императивные фрагменты будут, но тут бонус в том, что они должны быть простыми, чтоб можно было любой результат наглядно раскрыть на его составляющие. И все эти составляющие при желании разглядывать, анализировать и сравнивать.
> результат наглядно раскрыть на его составляющие. И все эти составляющие при желании разглядывать, анализировать и сравнивать.

Функциональщики говорят: построить элементную базу для комбинаторной обработки информации. :)
именно!

так что, без тех вышеупомянутых фундаментов во что я уткнусь?
Не знаю, вам виднее. Вы даже язык не назвали, на котором хотите это делать.
Этот диалект я не знаю, не приходилось с ним работать. Знаю T-SQL и PL/pgSQL. Могу предположить, что главной трудностью будет вопрос, как перекидывать большие объемы данных между функциями. Через аргументы? Через временные таблицы? Передавая запрос? Возникнут вопросы производительности и удобства использования. Но мне кажется, в конечном счете структура БД и логика, реализованная на функциях, будут проекту только на пользу. Я и сам на своей работе многое выношу на сервер, функций всяких полно, они друг друга вызывают. В принципе, это не сказать чтобы настоящий функциональный подход, но уж точно никак не вредительский.
Чисто в фукциональном стиле сохранять кешы в базе не получится — это операция с побочными эффектами. И зачем такая операция, если функция на заданных аргументах и так вычисляется только один раз? Кешы потом где-то используются?
Кеш нужен, лишь если функция на одинаковых аргументах вызывается неоднократно (как минимум, во второй раз она будет вызываться, когда кто-то захочет посмотреть её результат, использовавшийся первый раз при вычислении зависимой от неё функции).

И кеш возможен, лишь если сама функция побочных эффектов не имеет.

В этом случае кеширование в результат функции побочных эффектов не привнесёт.
Побочные эффекты возникают из-за операций ввода-вывода, т.е. обращения к БД. Если его нет — функция чиста. А если кеширование самоцель, т.е. нужен только чтоб восстанавливать результаты вычисления на одинаковых аргументах во время работы программы — то кеш вобще не нужен. Функциональные программы и так два раза не вычисляют одно и то же значение, вызывайте сколько угодно — вычисления будут проводиться только один раз
каким образом эта однократность вычисления реализована? ИМХО, ровно тем кешированием, о котором я говорю.
Во-многом зависит от компилятора. Но если возможность реализуется на уровне компилятора — зачем ее повторно реализовывать?
так речи и не шло о том, чтоб реализовывать повторно. Речь шла ровно об устройстве среды исполнения.
Поинт в том, что без него обычно можно жить и часто гораздо приятнее. А не в том что надо всем бросить заниматься делом и строить бесполезные программы в лямбда-исчислении.
Чистые функциональные языки вычисляют значение функции только один раз. Изменяемые состояния для в самой программе для этого не нужны
Разумеется. В Haskell random вычисляет одно случайное число для конкретного ГПСЧ и возвращает новый генератор ГПСЧ. С этим новым генератором можно вычислить следующее случайное число.
классный костыль. вместо меняющегося числа у нас будет меняющаяся функция. функции ввода-вывода тоже так лицемерно реализованы?
Ваш комментарий просто смешон. Какой костыль? Под этим решением есть строгое математическое обоснование. А решение само — очень элегантное.
да-да, стыдливо прячем изменяемое состояние за невъебенной абстракцией «монада» и вместо простого кода

var state= 0
function gen( ){
return ++state
}

генерируем что-то типа

var state= StateMonada( 0 )
gen= GeneratorHelper({
getState: function(){ return state },
nextValue: function( value ){ return value + 1 }
})

или ещё какую серьёзную математически обоснованную загогулину
Не темните своей некомпетенцией. Поизучайте накануне матчасть; то, что вы написали, не соотносится с функциональной природой ФЯ, потому что это исключительно императивный стиль. Какое изменяемое состояние? От него отказались не потому, что так захотелось, а потому, что это несет прямые выгоды в языках, подобных Haskell.
Да. random есть функция от генератора ПСЧ. Если два раза вызвать функцию на одном генераторе — получите один и тот же результат.
Скажу Вам больше. В императивных языках это работает также, просто Вам это не показывается.
… при поступлении на вход положительного числа больше 1, Вам нужно классифицировать его как perfect, abundant или deficient...

Не понимаю. Скажите кто-нибудь, что общего эта задача имеет с реальным миром?
В реальной жизни использовать какую-либо чистую парадигму никогда не удается. Даже ООП в реальности приходится постоянно разбавлять другими парадигмами, ибо святого грааля программирования не существует. Инструмент должен соответствовать задаче. Ну и как было правильно замечено, инструмент должен соответствовать человеку. Но в реальности пока еще не одна парадигма не показала существенных преимуществ над другими. В каких-то задачах лучше одно, в других другое. Невозможно с помощью одной парадигмы быть эффективным во всем… И для инженеров это хорошо, потому что если бы в технологии программирования все было бы так просто, то программная инженерия не оплачивалась бы так высоко :)
круто. было
static public Set<Integer> factors(int number) {
HashSet<Integer> factors = new HashSet<Integer>();
for (int i = 1; i <= sqrt(number); i++)
if (isFactor(number, i)) {
factors.add(i);
factors.add(number / i);
}
return factors;
}


стало
public List<Integer> factors(final int number) {
return range(1, number+1).filter(new F<Integer, Boolean>() {
public Boolean f(final Integer i) {
return number % i == 0;
}
});
}

Ну вообще никаких различий производительности… Что за жалкие подтасовки?

странно что в конце везде остались
sum(factors(number)) - number

по сути это неплохо бы выделить в отдельную функцию (reducedFactorSum), после чего писать вроде
isPerfect = (== number).reducedFactorSum
isAbundant = (> number).reducedFactorSum
isDeficiend= (< number).reducedFactorSum

Всё-таки демонстрация работы с функциями высшего порядка (использован синтаксис хаскеля для создания композиции функций, так как синтаксиса Groovy я не знаю).
Хуже стало :) вот если бы
range(1, sqrt(number)+1)
тогда да. И почему должны быть различия в производительности, если алгоритм не меняется?
Если алгоритм не меняется, то изменения производительности зависят от компилятора. Но в данном случе был изменён алгоритм.
то есть вместо того чтобы перебирать sqrt(n) мы стали перебирать n чисел. Это весьма существенное изменение сложности.

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

Пример такой некрасивости (язык хаскель)
[1… n] — местный range
[1… (fromInteger.toInteger.round.sqrt.fromIntegral) n] — это уже с корнем.
Поскольку вначале надо сделать преобразование в рациональное, чтобы сосчитать корень, потом округлить, потом преобразовать в Integer и только потом уменьшить до типа Int.
Сложно, долго и непонятно.
Тут нужно чтобы n был правильного типа. А то
No instance for (RealFrac Int)
arising from a use of `ceiling'
Possible fix: add an instance declaration for (RealFrac Int)
In the expression: ceiling
In the expression: ceiling $ sqrt n
In the second argument of `filter', namely
`[1 .. ceiling $ sqrt n]'
$ cat test.hs
range n = [1… ceiling $ sqrt n]
main = putStrLn $ show $ range 24
$ ghci test.hs
GHCi, version 6.12.1: www.haskell.org/ghc/ :? for help
Loading package ghc-prim… linking… done.
Loading package integer-gmp… linking… done.
Loading package base… linking… done.
[1 of 1] Compiling Main ( test.hs, interpreted )
Ok, modules loaded: Main.
*Main> main
[1,2,3,4,5]
*Main>
Я думаю, в данном случае опечатка просто ,) Там еще в listing 6 функция isFactor зачем-то затесалась непонятно. И еще опечатки есть, сейчас уже не вспомню, лень перечитывать.
Мне кажется в функциональном стиле тяжело будет написать реально большую систему. Взгляните на программы из мира бизнеса, баз данных, офисных приложений и прочего. В них весьма немного хитрых алгоритмов. Основное — это данные. Очень легко в функциональном стиле переписать класс с одним полем данных (как в примере в начале статьи). А если их десяток, да еще каждое является на простым типом, а вложенным классом? Код будет плохо читаем, мне кажется.
Мне вместе с Вами просто, возможно, пока сложновато это представить в полной мере. Взять веб-фреймворк для Scala — Lift. «Lift way», в отличие от своих Scala-аналогов, исключительно опирается на большое количество функциональных конструкций и уже довольно успешно используется в крупных компаниях.
Также DSL для работы с базой данных от Foursquare — Rogue
Большая это какого уровня примерно?

Вообще, странно такое слышать, ведь назначение ФЯ и типизации — это уменьшение сложности и сокращение ошибок (при помощи типов можно сокращать количество мест, где возможна ошибка).

Дело в том, что мы пишем, например. Но, конечно, возможно системы недостаточно большие для вас. Таким образом, насколько большие системы считаются?
А кто Вам сказал, что в функциональную программу будут переносить иерархию классов? Другая парадигма — другие подходы к решению задачи. Все будет выглядит совсем по-другому.
Основой всех систем являются две сущности: состояния и операции. Состояния обычно описываются структурой данных: иерархической, реляционной и т.д. Операции — это действия над данными, т.е. то, что изменяет состояние. Основная проблема всех времен и народов — как максимально локализовать данные и код, который оперирует с этими данными, в отдельные и независимые куски (свободные от нелюбимых side-effects). Отсюда берет начала куча парадигм программирования.

В частности инкапсуляция в ООП дает неплохую изоляцию: методы внутри объекта оперируют только с состояниями этого объекта. Но у него есть определенные проблемы, например расширить функциональность объекта типа «список» можно только при помощи наследования. Операции, затрагивающие изменения состояний многих объектов, требуют сложных архитектурных решений. И пр…

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

Тем не менее ФП было бы несправедливо называть отдельной и независимой парадигмой. Скорей это некий метод или прием, который может использоваться для перехода от императивного стиля описания к декларативному.
SQL можно было бы назвать функциональным, но как-то язык не поворачивается. Уж очень он ограниченный в этом плане, очень. Вот декларативный — да, вполне.
Это очень круто, все понятно, спасибо большое за перевод. Ждем продолжения!
А в Haskell еще легче. ;) Теперь мы с вами обязаны похоливарить!

А если серьезно, то да. От языка к языку та или иная парадигма программирования может так измениться, что любой, кто это увидит, скажет: «Какой ужас, эта ваша парадигма!». Инструмент все же имеет значение. А если рассматривать руки за самый что ни на есть полезный инструмент, то и без них никуда.
Не в обиду автору — но несколько утомили дифирамбы функциональному подходу. Его особенности (отстутсвие сайд-эффектов и его следствия — возможность «ленивого» выполнения, легкость распараллеливания и т.д.) думаю, известны каждому более-менее опытному программисту. Но вот рекламный тон каждый раз смущает — не попадалось пока ни одной статьи, где были бы указаны области применения, где ФП даёт хороший эффект, а где где оно создаёт проблемы.

Как мне кажется, именно статьи вида «ФП в реальном мире» были бы куда интереснее и, кстати, полезнее для его популяризации. Чтобы затрагивались вопросы — когда ФП стоит использовать в первую очередь, какие пдводные камни, как интегрироватьс императивным кодом, какие идеи у обоих подходов общие (вон, функциональный map и объектный foreach — близнецы-братья), какие элементы из ФП можно позаимствовать, не переходя на него полностью — и так далее, и тому подобное.
Вы правы: нужны практические примеры. Такой есть у меня. Можете увидеть здесь. Подумываю развернуть его в статью.

> (вон, функциональный map и объектный foreach — близнецы-братья)
А это вы, конечно, загнули. Нет, map, он, конечно, «делает в цикле» что от него хотят, но…
Они близнецы-братья в том, что отделяют задачу — сделать нечто с каждым отдельным элементом коллекции — от способа, которым это делается. Гарантий перебора в определённом порядке я не припомню ни в одном языке (хотя могу ошибаться, конечно). Из этого, к пример естественным образом вырастают параллельные реализации foreach.

Делегаты и коллбеки — другой древнючий пример одного и того же действия (передачи функции как значения), существующего в обеих парадигмах. Надо ещё? Пожалуйста: qsort из stdlib — вполне себе пример ФВП — разумеется, учётом возможностей языка. На плюсах это уже шаблонами решается впрочем, там уже скорее заимствование из ФП.

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

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

foreach в Qt (тот, который пришел с шаблонами, а не тот, который пришел с C++00x) все же обычный цикл, следовательно, задачи в нем выполняются последовательно. За другие языки не скажу.
Гарантию перебора в определенном порядке (что для foreach, что для map) даёт, вроде как, сама коллекция методами типа rewind и next или, грубо говоря, реализуя интерфейс итератора (или их низкоуровневыми аналогами для примитивных типов — массивы, объекты и т. п., где гарантии уже на уровне референса языка или транслятора).
хм… и впрямь, подстава. А реализаций foreach, не опирающихся на последовательные итераторы, нет?

Хотя — пусть даже и через next — спецификация же не запрещает реализации получить все элементы разом, и обрабатывать их одновременно?
Не встречал. Вроде везде именно на последовательных итерациях реализуется и это строго регламентировано для foreach. Для map могут быть варианты.
Не надо их интегрировать. Они базируются на разных предпосылках, разных идеях. Лично я противопоставления с ООП не вижу, есть задачи, которые элегантней решаются средствами ООП, но есть и те, для которых ФП — то, что доктор прописал. Вон, в Питоне, Руби и прочих сделана попытка интеграции — и получилась, простите, кастрация ФП, чтоб его фичи в принципы ООП влезли.
ТАк в том-то и дело. Если у меня в рамках одного рпоекта часть задач удобно решается через FP, а часть — императивно — то либо два языка тащить либо всё же подбирать один, который может то и другое. И подозреваю что в подавляющем большинстве случаев «кастрированного» ФП будет достаточно. Вон в D примерно так — и довольно красиво получается. Что до питона/руби — не знаю я их, так что судить не могу.
Может автор и профессионал, но почитал его первый листинг (Classifier6) и рождается естественный скепсис к человеку, кто написал код:
— который вернет «Can't classify negative numbers» на число 0;
— имеет public метод getFactors — мало того, что ненужный, так еще и позволяющий испортить состояние программы вызовом classifier.getFactors().insert(42);
— делающий кучу вычислений на каждый вызов isPerfect/isAbundant/isDeficient;
— работающий неправильно для больших number по причине переполнения;
И немного ворчания:
— «static public», а не наоборот, несмотря на java.sun.com/docs/books/jls/third_edition/html/classes.html#78091;
— поля не final, хотя могли бы быть таковыми;
— не нравятся подчеркивания в именах полей.

За перевод и пост спасибо. Особенно за теорию. Но как-то ожидал большего в листингах и комментах к ним. Уж сильно всё завязано на Java, в костылях, имхо, суть теряется.
>В самом центре функционального программирования находится (звучит барабанная дробь) функция, также как и классы являются основной абстракцией в объектно-ориентированном программировании (ООП).

Основной абстракцией в ООП являются объекты. Классы есть и в ФП.
А одному мне показалось, что _factors.add(_number); в первом листинге в конструкторе не нужно делать? Надо же исключить само число из суммы его делителей.
Only those users with full accounts are able to leave comments. Log in, please.