Трудно точно определить, что такое «функциональный язык» - из перечисленных вами языков только Haskell является чисто функциональным (все остальные используют какой-то гибридный подход). Тем не менее, существуют определенные языковые функции, которые очень полезны для функционального программирования, и у Ruby и Python их недостаточно, чтобы быть очень хорошей средой для FP. Вот мой личный контрольный список, в порядке важности:
- Первоклассные функции и замыкания (Ruby, Python и все остальные, которые вы перечислили, имеют это).
- Гарантированная оптимизация хвостового вызова (Erlang, Haskell, Scala и Scheme имеют это, но не Python, Ruby или Clojure (пока)).
- Поддержка неизменяемости в языке и стандартных библиотеках (это большая поддержка, которую имеют все «функциональные языки», которые вы перечислили (кроме Scheme), но Ruby и Python не имеют).
- Поддержка на уровне языка для ссылочно прозрачных (или чистых) функций (насколько я знаю, только Haskell имеет это в настоящее время).
Необходимость (1) должна быть очевидна - функции высшего порядка чрезвычайно сложны без функций первого класса. Когда люди говорят, что Ruby и Python являются хорошими языками для FP, они обычно говорят об этом. Тем не менее, эта особенность необходима, но не достаточна, чтобы сделать язык хорошим для FP.
(2) была традиционная необходимость в FP с тех пор, как была изобретена схема. Без TCO невозможно программировать с глубокой рекурсией, которая является одним из краеугольных камней FP, потому что вы получаете переполнение стека. Единственный «функциональный» (по общему определению) язык, который не имеет этого, - это Clojure (из-за ограничений JVM), но у Clojure есть множество хаков для имитации TCO. (К сведению, Ruby TCO зависит от реализации , но Python специально не поддерживает его .) Причина, по которой TCO должен быть гарантирован, заключается в том, что если это зависит от реализации, глубокие рекурсивные функции будут порваться с некоторыми реализациями, поэтому вы не сможете по-настоящему использовать их на всех.
(3) еще одна важная вещь, которую современные функциональные языки (особенно Haskell, Erlang, Clojure и Scala) имеют, чего нет в Ruby и Python. Не вдаваясь в подробности, гарантированная неизменность устраняет целые классы ошибок, особенно в параллельных ситуациях, и допускает такие аккуратные вещи, как постоянные структуры данных . Этими преимуществами очень сложно пользоваться без поддержки на уровне языка.
(4) для меня самая интересная вещь в чисто функциональных языках (в отличие от гибридных). Рассмотрим следующую чрезвычайно простую функцию Ruby:
def add(a, b)
a + b
end
Это похоже на чистую функцию, но из-за перегрузки оператора она может изменить либо параметр, либо вызвать побочные эффекты, такие как печать на консоль. Маловероятно, что кто-то перегружает +
оператора, чтобы иметь побочный эффект, но язык не дает никаких гарантий. (То же самое относится и к Python, хотя, возможно, не с этим конкретным примером.)
В чисто функциональном языке, с другой стороны, существуют гарантии уровня языка, что функции прозрачны по ссылкам. Это имеет многочисленные преимущества: чистые функции легко запоминаются; их можно легко протестировать, не полагаясь ни на какое глобальное состояние; и значения внутри функции могут оцениваться лениво или параллельно, не беспокоясь о проблемах параллелизма. Haskell в полной мере использует это, но я не знаю достаточно о других функциональных языках, чтобы знать, делают ли они.
При этом можно использовать технику FP практически на любом языке (даже на Java). Например, Google MapReduce вдохновлен функциональными идеями, но, насколько я знаю, они не используют никаких «функциональных» языков для своих больших проектов (я думаю, что они в основном используют C ++, Java и Python).