Почему Python не очень хорош для функционального программирования? [закрыто]

324

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

Дэвид Джонстон
источник
2
2018 - Coconut (функциональный язык программирования, который компилируется в Python) расширяет функциональное программирование в Python. Смотрите также серию статей из IBM стр.1 стр.2 PAGE3
cssyphus

Ответы:

393

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

Лучший аргумент против функционального программирования в Python - это то, что варианты использования императивного / OO тщательно рассматриваются Guido, а варианты использования функционального программирования - нет. Когда я пишу императивный Python, это один из самых красивых языков, которые я знаю. Когда я пишу функциональный Python, он становится таким же уродливым и неприятным, как ваш обычный язык без BDFL .

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

Вот функциональные вещи, которые я пропускаю в Python:


  • Отсутствие сопоставления с образцом и хвостовая рекурсия означают, что ваши базовые алгоритмы должны быть обязательно написаны. Рекурсия уродливая и медленная в Python.
  • Небольшая библиотека списков и отсутствие функциональных словарей означают, что вам придется писать много вещей самостоятельно.
  • Отсутствие синтаксиса для карри или композиции означает, что стиль без точек почти такой же пунктуации, как и явно передаваемые аргументы.
  • Итераторы вместо ленивых списков означают, что вы должны знать, хотите ли вы эффективность или постоянство, и разбрасывать вызовы listвокруг, если вы хотите постоянство. (Итераторы используются один раз)
  • Простой императивный синтаксис Python, наряду с его простым синтаксическим анализатором LL1, означает, что лучший синтаксис для выражений if и лямбда-выражений в принципе невозможен. Гвидо так нравится, и я думаю, что он прав.
Натан Шивели-Сандерс
источник
5
+1 за отсутствующую хвостовую рекурсию - хотя циклические конструкции заменили ее, ее все же стоит пропустить, переходя между Python и Scheme.
new123456
5
Отличный ответ как для полноты и композиции. Увы, как и во многих ответах с сильным функциональным фоном, в ИМО используется терминологическое злоупотребление. Хотя я понимаю, что вы не можете уточнить каждую концепцию в ответе, мне интересно, хорошо ли информирован OP (с допустимым ограниченным фоном FP) при чтении таких терминов, как «сопоставление с образцом», «функциональные словари», «каррирование» или « ленивые списки ".
ThomasH
4
Хорошая точка зрения; Я думаю, что решение состоит в том, чтобы добавить ссылки. У вас достаточно репутации, чтобы отредактировать мой ответ? Если это так, не стесняйтесь добавлять ссылки на различные концепции. Я начну, когда у меня будет время позже.
Натан Шивели-Сандерс
5
Я понимаю, что это 5 лет, но ... похоже, это больше о вещах, которые вы упускаете из Хаскелла , а не из функциональных языков . Например, большинство диалектов и потомков ML и Lisp не имеют автоматического каррирования, делают слишком многословным стиль без точек, не имеют ленивых списков и т. Д. Поэтому, если итераторы вместо ленивых списков делают Python плохим функциональным языком, также не должен сделать CaML ужасным функциональным языком?
abarnert
4
@abarnert: Caml имеет все точки, кроме ленивых списков, которые доступны в виде библиотеки. Я время от времени использовал Caml, когда писал этот ответ, и в настоящее время использую F #. Они оба очень хорошие функциональные языки.
Натан Шивели-Сандерс
102

У Гвидо есть хорошее объяснение этого здесь . Вот самая важная часть:

Я никогда не думал, что Python находится под сильным влиянием функциональных языков, независимо от того, что люди говорят или думают. Я был намного лучше знаком с императивными языками, такими как C и Algol 68, и хотя я сделал функции первоклассными объектами, я не рассматривал Python как функциональный язык программирования. Однако ранее было ясно, что пользователи хотят делать гораздо больше со списками и функциями.

...

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

И наконец, даже несмотря на то, что за эти годы был введен ряд функциональных функций программирования, в Python все еще отсутствуют некоторые функции, присутствующие в «настоящих» функциональных языках программирования. Например, Python не выполняет определенные виды оптимизации (например, хвостовая рекурсия). В целом, поскольку Python чрезвычайно динамичен, невозможно выполнить оптимизацию во время компиляции, известную из функциональных языков, таких как Haskell или ML. И это нормально.

Я вытащил две вещи из этого:

  1. Создатель языка на самом деле не считает Python функциональным языком. Следовательно, можно увидеть функционально-функциональные особенности, но вряд ли вы увидите что-либо, что является окончательно функциональным.
  2. Динамическая природа Python запрещает некоторые оптимизации, которые вы видите в других функциональных языках. Конечно, Lisp такой же динамический (если не более динамичный), как и Python, так что это только частичное объяснение.
Джейсон Бейкер
источник
8
Вы можете сделать оптимизацию хвостового вызова в Python просто отлично. Гвидо этого не понимает.
Жюль
26
Кажется, все сводится к тому, что Гвидо ван Россум не любит функциональный стиль.
Сванте
59
Я думаю, точнее будет сказать, что Гвидо ван Россум не понимает функциональный стиль и не понимает, зачем он нужен Python. Вы должны понимать две вещи: 1) языки программирования находятся в нижней части технологического стека и влияют на все, что на них построено, и 2) как и любое другое программное обеспечение, проще добавлять функции, чем удалять их. Поэтому я думаю, что для языкового дизайнера очень важно критиковать такие запросы.
Джейсон Бейкер
8
"Конечно, Лисп так же динамичен" -> и столь же необходим!
Пион
6
@Jules, вы не против поделиться руководством по использованию оптимизации хвостового вызова в Python? Указатель на некоторый источник был бы полезен.
Дэвид Shaked
52

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

  1. Искаженные лямбды. Поскольку лямбда-выражения могут содержать только выражение, и вы не можете делать все так просто в контексте выражения, это означает, что функции, которые вы можете определить «на лету», ограничены.

  2. Если это заявления, а не выражения. Это означает, что, помимо прочего, у вас не может быть лямбды с If внутри. (Это исправлено троичными в Python 2.5, но выглядит некрасиво.)

  3. Гвидо угрожает убрать карту, отфильтровать и уменьшить время от времени

С другой стороны, в Python есть лексические замыкания, лямбды и списки (которые действительно являются «функциональной» концепцией, признает ли это Гвидо). Я много программирую в «функциональном стиле» на Python, но вряд ли скажу, что он идеален.

Джейкоб Б
источник
3
Карта, фильтрация и уменьшение действительно не нужны в Python. Я еще не видел кусок кода, который очень сильно упростил их использование. Кроме того, вызов функций в Python может быть дорогостоящим, поэтому лучше в любом случае просто использовать понимание списка / генератора или цикл for.
Джейсон Бейкер
2
Это именно то, что говорит Натан Сандерс ниже: «Python не продвигает функциональное программирование, даже если он работает довольно хорошо». Если бы Гвидо хотел, чтобы Python стал функциональным языком, он заставил бы реализацию работать достаточно хорошо, чтобы использовать одноразовые функции, и расшифровал бы Lambdas до такой степени, что вы могли бы фактически использовать map / filter / lower полезными способами. С другой стороны, функциональные люди начинают осознавать удивительность понимания списков. Надеюсь, нам не придется выбирать одно или другое.
Джейкоб Б
7
карта и фильтр тривиально заменяются пониманием списка. уменьшить - почти всегда - это настолько неэффективно, что его следует заменить функцией генератора.
С.Лотт
13
@ S.Lott как заменить редуктор на генератор?
Сурьма
17
@JacobB Списки были доступны на функциональных языках примерно за 15 лет до изобретения Python и за 25 лет до того, как Python получил реализацию этой функции. Идея о том, что Python повлиял на их распространение, или что fp узнал об этом из Python, или даже просто о том, что его популярность в мире fp существует после реализации Python, просто ошибочна. Реализация Python была взята непосредственно из Haskell. Может быть, я неправильно вас понял, и вы не это имели в виду, но я озадачен тем, что «функциональные люди начинают осознавать удивительность понимания списков».
брюс
23

Я бы никогда не назвал Python «функциональным», но всякий раз, когда я программирую на Python, код всегда оказывается почти чисто функциональным.

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

Конрад Рудольф
источник
17

Позвольте мне продемонстрировать фрагмент кода, взятый из ответа на «функциональный» вопрос Python о SO

Python:

def grandKids(generation, kidsFunc, val):
  layer = [val]
  for i in xrange(generation):
    layer = itertools.chain.from_iterable(itertools.imap(kidsFunc, layer))
  return layer

Haskell:

grandKids generation kidsFunc val =
  iterate (concatMap kidsFunc) [val] !! generation

Основное отличие в том , что стандартная библиотека Haskell имеет полезные функции для функционального программирования: в этом случае iterate, concatи(!!)

yairchu
источник
7
Вот замена одна линия для grandKids()тела с выражениями генератора: return reduce(lambda a, v: concat((x for x in kidsFunc(v)) for v in a), xrange(generation), [val]).
Lloeki
6
А вот тот, который тоже не нужен concat:return reduce(lambda a, v: (x for v in a for x in kidsFunc(v)), xrange(generation), [val])
Lloeki 22.10.11
9
@Lloeki: итерация значительно упростила бы этот код, и (x для v в a для x в kidsFunc (v)) намного понятнее, чем concatMap (kidsFunc). Отсутствие хороших встроенных функций высшего порядка в Python делает код загадочным и многословным по сравнению с Haskell.
Фоб
2
Конкат может быть заменен наitertools.chain.from_iterable
Сурьма
@ Сурьма: приятно знать. ТНХ
yairchu
14

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

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

Функциональное программирование во многом похоже на это; вы ничего не меняете, вы просто оцениваете уравнение (или, в данном случае, «программа») и выясняете, каков ответ. Программа все еще там, без изменений. То же самое с данными.

Я бы оценил следующее как наиболее важные особенности функционального программирования: а) ссылочная прозрачность - если вы оцениваете одно и то же утверждение в другое время и в другом месте, но с теми же значениями переменных, оно все равно будет означать то же самое. б) без побочных эффектов - независимо от того, как долго вы смотрите на доску, уравнение, на которое другой парень смотрит на другую доску, не изменится случайно. в) функции тоже значения. которые могут быть переданы и применены, или к другим переменным. г) состав функции, вы можете сделать h = g · f и, таким образом, определить новую функцию h (..), которая эквивалентна вызову g (f (..)).

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

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

ксено
источник
2
Функции являются первоклассными в Python.
Карл Смит
@CarlSmith Тот, но оставляет 3/4, чего нет у Python. : - \
Арья
1
Я не думаю, что Python является хорошим языком для функционального программирования. Я даже не уверен, что я имел в виду под этим комментарием, чтобы быть честным. Это не похоже на ответ. Я бы удалил его, но тогда ваш комментарий был бы вне контекста.
Карл Смит
1
Ссылочная прозрачность и неизменность на самом деле не являются особенностями языка. Да, некоторые языки (Haskell) подчеркивают их и затрудняют их отсутствие, но вы можете сделать ссылочно-прозрачную функцию или неизменный объект практически на любом языке. Вам просто нужно обойти стандартную библиотеку, которая часто будет их нарушать.
Кевин
Кроме того, в Python есть поддержка как каррирования, так и композиции, но не на уровне языка, а в стандартной библиотеке.
Кевин
10

Python - это почти функциональный язык. Это «функциональный лайт».

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

В ней также отсутствуют некоторые функции, поэтому для некоторых она не является полной.

Недостающие функции относительно легко написать. Проверьте сообщения , как это на FP в Python.

С. Лотт
источник
2
По большей части я согласен с этим постом. Но я не могу согласиться с тем, что Python - это функциональный язык. Это очень поощряет императивное программирование, и с теми «дополнительными функциями», о которых вы упомянули, сложно отказаться. Я думаю, что лучше называть Python как «функциональный облегченный», как вы это делали в другом посте. :-)
Джейсон Бейкер
8
-1 Извините, нет. Я имею в виду, просто нет. Функциональные языки предоставляют конструкции, облегчающие формальные рассуждения: индуктивный (ML), эквациональный (Haskell). Только замыкания и анонимные функции являются просто синтаксическим сахаром для шаблона стратегии.
Пион
8

Другая причина, не упомянутая выше, заключается в том, что многие встроенные функции и методы встроенных типов модифицируют объект, но не возвращают измененный объект. Если бы эти измененные объекты были возвращены, это сделало бы функциональный код чище и более кратким. Например, если some_list.append (some_object) вернул some_list с добавленным some_object.

DVD Avins
источник
4

В дополнение к другим ответам, одна из причин, по которой Python и большинство других мультипарадигмальных языков не подходят для настоящего функционального программирования, заключается в том, что их компиляторы / виртуальные машины / среды выполнения не поддерживают функциональную оптимизацию. Такая оптимизация достигается компилятором, понимающим математические правила. Например, многие языки программирования поддерживают mapфункцию или метод. Это довольно стандартная функция, которая принимает функцию в качестве одного аргумента и итеративную в качестве второго аргумента, а затем применяет эту функцию к каждому элементу в итерируемой.

В любом случае получается, что так map( foo() , x ) * map( foo(), y )же, как map( foo(), x * y ). Последний случай на самом деле быстрее, чем первый, потому что первый выполняет две копии, где последний выполняет одну.

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


источник
map( foo() , x ) * map( foo(), y ) == map( foo(), x * y )не верно для всех функций. Например, рассмотрим случай, когда fooвычисляется производная.
Эли Корвиго
1
Я думаю, что он имел в виду +вместо *.
user1747134
Вы предполагаете, что foo () является линейной?
Хуан Исаза
это свойство НЕ верно для foo (x) = x + 1. As (x + 1) * (y + 1)! = X * y + 1.
Хуан Исаза