Функции первого класса

11

Я начал серьезно смотреть на Lisp в эти выходные (я имею в виду, что изучал только Lisp и не возвращаюсь к проектам на C #) и должен сказать, что мне это нравится. Я баловался с другими функциональными языками (F #, Haskell, Erlang), но не чувствовал ничьей, которую дал мне Лисп.

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

[Редактировать] Я ценю ответы до сих пор. Поскольку мне было показано, что многие языки теперь поддерживают первоклассные функции, я перефразирую вопрос: почему языкам потребуется так много времени для их реализации? [/Редактировать]

Jetti
источник
3
Я не согласен с тем, что C # может делать "похожие вещи". Я бы сказал, что он имеет первоклассные функции для любых целей и задач (что разрушает вашу предпосылку). Он имеет синтаксис для литералов функций, вы можете
складывать
@Logan - я спорил о том, стоит ли включать примеры на C #, но решил основываться на этой записи в Википедии . Согласно записи, не существует истинных первоклассных функций, поскольку «поскольку объекты, которые содержат динамическое состояние функции, должны создаваться вручную». Однако, если это неверное утверждение, я хотел бы знать, почему! (В конце концов, я здесь, чтобы учиться и не застревать у меня на пути!)
Jetti
2
эта запись в википедии устарела. Современный C # обеспечивает правильную лямбду.
SK-logic
Я думаю, что этот абзац плохо написан. Кажется, говорят, что функторы C ++ не являются первоклассными и не являются делегатами C #? Хотя я мог бы согласиться с аргументом для функторов, я не уверен, что согласился бы с делегатами C #. Он также говорит «(см. Лямбда-лифтинг)», что является утверждением о том, что эти языки поддерживают лексические замыкания, а не «функции как значения». Вы можете иметь одно без другого. Даже если это так, это не так для C #, у него есть лексические замыкания. Часть проблемы - «первоклассная функция» - может быть неопределенным термином.
Логан Капальдо
1
@Logan & SK-Logic - Я ценю ваши отзывы и конструктивность. Я все еще учусь, и очень приятно, что меня не обжигают, поэтому я ценю это. Большое спасибо за комментарии и ответы!
Джетти

Ответы:

5

C #, VB.NET, Python, JavaScript, теперь даже C ++ 0x предоставляет первоклассные функции.

Обновить:

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

И самая важная часть - первоклассные функции едва ли можно использовать, если отсутствует сборщик мусора. Он был внедрен в массовое программирование только недавно, с Java и, следовательно, .NET. И оригинальный Java-подход заключался в том, чтобы упростить язык, чтобы его могли понять обычные программисты. Сначала последовал .NET, и только недавно расстался с этим (ИМХО, вполне оправданным и уместным) направлением.

Все такие концепции требуют времени для усвоения промышленностью.

SK-логика
источник
Я отредактировал оригинальный вопрос, основываясь на ответах.
Джетти
Первоклассные функции! = Замыкания (а GC гораздо более необходим для последних). Хотя, да, они тесно связаны, и вы редко, если вообще когда-либо, видите их разлученными.
@delnan, по крайней мере без какой-либо формы лексических замыканий, функции не являются компонуемыми и не конструируемыми во время выполнения, что является обязательным для определения функций первого класса.
SK-logic
Нет. Вы можете запретить ссылаться на переменные входящих областей (или скопировать значения указанной переменной во время создания функции - не знаю, если это считается закрытием). Результат не особенно полезен, но функции могут все еще быть построены во время выполнения, не будучи замыканиями, и поэтому у вас были бы (странные) функции первого класса.
1
@ SK-logic: C ++ 0x обеспечивает замыкания без сборки мусора с помощью обычного трюка -> если переменная выходит из области видимости, а вы все еще цепляетесь за ссылку, использование ссылки является неопределенным поведением. Так что это возможно ... это просто отдает должное разработчику.
Матье М.
5

Первоклассные функции намного менее интересны без замыканий.

Замыкания на самом деле невозможны без какого-либо динамического управления областью действия (сборкой мусора). Одна из вещей, которая делает C / C ++ настолько высокопроизводительным, заключается в том, что намного проще скомпилировать язык, в котором вы управляете памятью вручную, так что это своего рода выводит C / C ++ из уравнения.

И тогда да, есть вопрос влияния ООП. У вас больше нет разделения методов и свойств. Методы сами по себе являются свойствами, которые принимают функции в качестве значений. В этом контексте, что на самом деле означает перегружать методы, так как вы можете просто поменять глупые вещи в любое время. Можете ли вы добавить это к Java, например, не вводя основной сдвиг парадигмы для всего языка? Где контракт или это чувство (IMO-ложь) безопасности, которое люди получают, зная, что конструкция гарантирует, что она всегда будет работать одинаково каждый раз? Возможно, имеет смысл рассматривать функции, привязанные к объектам, как методы как отдельный организм в языках, которые вначале позволяли функциям существовать независимо, но в Java это на самом деле не вариант.

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

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

Тем не менее, я чувствую, что мне не хватает руки, когда вынуждены кодировать без них.

Эрик Реппен
источник
Вы можете рассматривать все методы как закрытые или просто запечатать те, которые вам наиболее интересны, и все будет в порядке.
Алексей Аверченко
@AlexeiAverchenko Я рад сказать, что не уверен на 100%, что вы подразумеваете под этим. Извините, я пропустил ваш комментарий до сих пор. Погуглив "запечатанными методами".
Эрик Реппен
4

Это не совсем невозможно. Но это еще одна особенность, а бюджет сложности ограничен. Разработчик языка может счесть ненужным (или даже не подозревать об этом) - «какого черта нам нужно передавать функции? Этот язык предназначен для программирования ОС!». Может также потребоваться значительное усилие, чтобы слиться с остальной частью языка. Например, тип функции может потребовать серьезных дополнений к системе типов (например, в Java), даже больше, если у вас есть перегрузка (спасибо @Renesis за указание на это). Вы также должны предоставить некоторый библиотечный код для использования жизнеспособных - никто не хочет определять mapсебя.

Это также может затруднить достижение других целей языка:

  • Оптимизация становится «труднее» (на самом деле не сложнее во многих случаях, но требует иных подходов, чем те, к которым вы привыкли). Например, если глобальные функции могут быть переназначены, вы должны доказать, что они fникогда не переназначаются, прежде чем встроить их.
  • Аналогичным образом, если вы делаете функции полнофункциональными объектами, вам понадобится умный компилятор и JIT, чтобы удалить связанные с этим накладные расходы и сделать вызов настолько же эффективным (по скорости и пространству), как выдвижение аргументов и адреса возврата и переход к какой-то адрес.
  • Как уже упоминалось, это еще одна полноценная функция и первые шаги к поддержке еще одной парадигмы - если вы хотите простой язык, возможно, вы не можете позволить себе добавлять первоклассные функции, не бросая другие вещи (которые вы можете считать гораздо более важными) из.
  • Возможно, это просто плохо сочетается с остальным языком. Если вы разрабатываете язык, чтобы, например, быть лучшим воплощением ООП со времен Smalltalk, вы можете сосредоточиться на этом, вместо того, чтобы предлагать другую, совсем другую парадигму. Особенно, если их сложно объединить (см. Выше). N хорошо выполненных парадигм программировать приятнее, чем плохо выполненных N + 1.

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


источник
1

Я думаю, что первая проблема - это неправильное понимание того, что объектно-ориентированное и функциональное невозможно смешать. Краткий список языков, описанных, по крайней мере в Википедии, как: JavaScript , ActionScript , Python , C # , F # .

Вторая проблема, по-видимому, заключается в определении функций первого класса - почему вы не считаете, что в C # они есть, например?

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

Какой реальный вопрос?

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

Перегрузка функций

Одной из основных причин этого является сложность определения функций первого класса, когда язык также решил поддерживать перегрузку функций (пример в Java):

public int dispatchEvent(Event event);
public int dispatchEvent(String event, Object data);

К какой dispatchEventфункции будет относиться переменная?

Программирование на основе классов

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

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

Когда вы добавляете (не динамические) классы, у вас есть функции, которые неотъемлемо связаны с экземпляром, а не только с самим классом. Это приводит к появлению нескольких складок в спецификации, Function.applyкоторую я, честно говоря, не зарыл, чтобы узнать, как они справились с этим.

Николь
источник
3
«Включение первоклассных функций само по себе более или менее делает язык функциональным». - Неправильно. Это обязательное условие, да. Но это намного больше, например, избегать изменяемого состояния (например, избегать изменяемого состояния), фокусироваться на выражениях и применении функций, а не на последовательных операторах и акцентировать прозрачность ссылок.
@delnan Тогда вы должны исправить Википедию ... Если «функциональный язык» и «язык, поддерживающий функциональную парадигму» - это не две разные вещи.
Николь
1
@Rensis: это разные вещи. Вы можете создать что-то вроде объектов (даже включая vtables) в C, но это не красиво. Переход от «имеет первоклассные функции» к «функциональному языку» не так уж и плох, но это все же разница. Кроме того, вики (правильно) указывает на странице, на которую вы ссылаетесь, «Эти функции необходимы для стиля функционального программирования [...]». (то есть: не «определяющая функция») и перечисляет несколько других функций на странице на FP.
@delnan, Хорошо, я исправил свою формулировку. Я просто имел в виду условие, что язык должен рассматриваться как поддерживающий функциональную парадигму - как и страница Википедии для каждого из перечисленных языков. Я не хотел сказать, что это все, что является частью стиля функционального программирования .
Николь
0

Я сам удивлялся тому же самому. Как только я нахожусь на языке с лямбдами, я использую их МНОГО. Даже PHP становится лучше, когда вы понимаете, что вы можете сделать замыкание с помощью php 5.3 (это не так хорошо, как lisp, но все же лучше)

Захари К
источник