Функції першого класу


11

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

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

[Редагувати] Я оцінюю відповіді поки що. Оскільки мені показали, що зараз багато мов підтримують першокласні функції, я переформулюю питання, чому б це могло зайняти стільки часу, щоб мови їх реалізували? [/ Редагувати]


3
Я не згоден, що C # може робити "подібні речі". Я б сказав, що він має першокласні функції для всіх намірів і цілей (що ніби руйнує ваше приміщення). У ньому є синтаксис функціональних літералів, ви можете
складати

@Logan - Я обговорював питання про те, чи включати приклади C #, але вирішив грунтуватися на цій статті у Вікіпедії . Згідно із записом, не існує справжніх функцій першого класу, оскільки "оскільки об'єкти, що утримують динамічний стан функції, повинні бути побудовані вручну". Однак якщо це неправильне твердження, я хотів би знати чому! (Зрештою, я тут, щоб вчитися і не зациклюватися на моїх шляхах!)
Jetti

2
що запис у Вікіпедії застаріла. Сучасний C # забезпечує належну лямбда.
SK-логіка

Я думаю, що цей абзац написаний погано. Здається, говорять, що функціонери C ++ не є першокласними, а також не є делегати C #? Хоча я можу погодитись з аргументом для функторів, я не впевнений, що був би для делегатів C #. Він також говорить "(див. Підняття лямбда)", що є твердженням про ці мови, що підтримують лексичне закриття, а не "функції як значення". Можна мати одне без іншого. Навіть якщо це так, це не вірно для C #, він має лексичні закриття. Частина проблеми полягає в тому, що "функція першого класу" може бути невиразним терміном.
Логан Капальдо,

1
@Logan & SK-Logic - Я ціную ваші відгуки та залишаюся конструктивними. Я все ще вчусь, і це дуже приємно, щоб не спалахнути, тому я ціную це. Дуже дякую за коментарі та відповіді!
Jetti

Відповіді:


5

C #, VB.NET, Python, JavaScript, тепер навіть C ++ 0x забезпечує першокласні функції.

Оновлення:

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

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

Усі такі концепції потребують часу, щоб галузь засвоїла.


Я відредагував оригінальне запитання на основі відповідей.
Jetti

Першокласні функції! = Закриття (а GC набагато більш необхідні для останнього). Хоча, так, вони тісно пов'язані, і ви рідко, якщо взагалі, бачите їх окремо.

@delnan, не маючи принаймні певної форми лексичних функцій закриття, не підлягає компонуванню і не може бути сконструйований під час виконання, що є обов'язковим для визначення функцій першого класу.
SK-логіка

Ні. Ви можете заборонити повторне використання змінних вкладених діапазонів (або скопіювати значення згаданої змінної на момент створення функції - не знаю, якщо це вважається закриттям). Результат не є особливо корисним, але функції все ще можуть бути побудовані під час виконання без закриття, і тому у вас будуть (дивні) першокласні функції.

1
@ SK-логіка: C ++ 0x забезпечує закриття без збирання сміття звичайним трюком -> якщо змінна виходить із сфери застосування, а ви все одно чіпляєтесь за посиланням, використовуючи посилання, це невизначене поведінка. Так це можливо ... це просто ставить хонус на розробника.
Матьє М.

5

Функції першого класу набагато менш цікаві без закривань.

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

І тоді так, є питання впливу OOP. У вас більше немає поділу методів та властивостей. Методи - самі властивості, які приймають функції як значення. У цьому контексті, що насправді означає перевантажувати методи, оскільки ви можете просто поміняти дурні речі в будь-який час. Чи можете ви дійсно додати це, наприклад, до Java, не вводячи основного зміщення парадигми на всю мову? Звідки у контракті або в тому, що особа (IMO false) охоронні люди отримують від знання конструкції, гарантує, що вона завжди працюватиме однаково кожен раз? Можливо, має сенс трактувати функції, пов'язані з об'єктами, як методи як різні організми в мовах, які дозволяють функціям існувати незалежно в першу чергу, але в Java це насправді не є можливим.

У JavaScript, OOP та функціонал дуже переплітаються, і мені особисто важко буде бачити функцій першокласного класу нічим іншим, окрім як зафіксованими мовами, де їх не було. Але для цього потрібен ряд інших змін, що зміщують парадигму, включаючи високий рівень змінності об'єктів та самих їх методів, а також можливість викликати функції так, ніби вони є членами будь-якого об'єкта на льоту.

Мови - це не просто набори інструментів, наповнені гігієнічними квітами, для того, щоб в більшості своїй виконати справи. Вони парадигми. Пачки ідей, стратегій та думок, які всіляко поєднуються у такий спосіб, який пов'язаний (або здається пов'язаним). У багатьох випадках функції як іменник і дієслово насправді просто не відповідають парадигмі або в кращому випадку є недосконалим пристосуванням.

Однак, я відчуваю, що мені не вистачає руки, коли змушений кодувати без них.


Ви можете ставитися до всіх методів як до запечатаних, або просто запечатати ті, які вам найбільше важливі, і ви будете добре.
Олексій Аверченко

@AlexeiAverchenko Я радий сказати, що я не впевнений на 100%, що ти маєш на увазі під цим. Вибачте, що я пропустив ваш коментар дотепер. Гуглінг "герметичними методами".
Ерік Реппен

4

Це насправді неможливо. Але це ще одна особливість, а бюджет складності обмежений. Можливо, це вважатиме непотрібним дизайнером мови (або навіть не трапляється) - "для чого, чорт, нам потрібно передавати функції? Ця мова призначена для програмування ОС!". Також може знадобитися значне зусилля для злиття з рештою мови. Наприклад, тип функції може вимагати серйозних доповнень до системи типів (наприклад, на Java), навіть більше, якщо у вас є перевантаження (дякую @Renesis, що вказав на це). Вам також потрібно надати код бібліотеки, щоб скористатися життєздатністю - ніхто не хоче визначати mapсебе.

Це також може ускладнити досягнення інших цілей мови:

  • Оптимізувати стає «важче» (насправді не важче у багатьох випадках, але потрібні інші підходи, ніж ті, до яких ви звикли). Наприклад, якщо глобальні функції можуть бути перепризначені, вам доведеться довести, що fніколи не перепризначається перед тим, як вбудувати їх.
  • У подібному руслі, якщо ви робите функції повнофункціональних об'єктів, вам потрібен розумний компілятор і JIT, щоб видалити накладні витрати, пов'язані з цим, і зробити виклик таким же ефективним (швидким і просторовим), як натискання аргументів і повернення адреси та перехід до якась адреса.
  • Як уже згадувалося, це ще одна повноцінна функція та перші кроки до підтримки ще однієї парадигми - якщо ви хочете простої мови, можливо, ви не можете дозволити собі додавати першокласні функції, не кидаючи інші речі (які ви можете вважати набагато важливішими) з.
  • Можливо, він просто не добре поєднується з рештою мови. Якщо ви спроектуєте мову, наприклад, щоб бути найкращим втіленням OOP з 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, але все ж краще)

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.