Чому Go не дозволяє вкладені оголошення оголошень (функції всередині функцій)?


87

Редагувати: Якщо було незрозуміло, про що я запитував: які проблеми пом'якшуються, якщо не дозволяти вкладені декларації функцій?

Лямбди працюють, як очікувалося:

func main() {
    inc := func(x int) int { return x+1; }
}

Однак наступна декларація в декларації не допускається:

func main() {
    func inc(x int) int { return x+1; }
}

З якої причини вкладені функції заборонені?


хм, я не знаю, чи ти мав це зробити func main() { func (x int) int { return x+1; }(3) }
ymg

@YasirG. але це також лямбда, чи не так? Я не отримую вашого коментаря ...
corazza

яку функціональність дозволить увімкнення другого прикладу в синтаксисі, який не підтримується першим випадком?
Not_a_Golfer

@yannbane це лямбда-вираз, я не думаю, що ти можеш оголосити функцію всередині іншої функції, як JS. Тож я б сказав, що вам найкраще підходити до використання лямбдас.
ymg

@Not_a_Golfer: Однією з можливостей було б реалізувати це так, як це робить JavaScript, по суті, присвоєння функції змінній сильно відрізняється від оголошення функції через те, що потік контролю впливає на такі змінні, тоді як функції в JavaScript не впливають. Це означає, що ви можете зателефонувати inc()у другому прикладі до фактичної декларації. Але! Я шукаю причини, я не дуже багато знаю про Go, але хотів би дізнатися, яка логіка цього правила.
corazza

Відповіді:


54

Я думаю, що є 3 причини, чому ця очевидна особливість не дозволена

  1. Це трохи ускладнило б компілятор. На даний момент компілятор знає, що всі функції знаходяться на найвищому рівні.
  2. Це призведе до нового класу помилок програміста - ви можете щось переформатувати і випадково вкласти деякі функції.
  3. Інший синтаксис функцій та закриття - це добре. Виконання закриття потенційно дорожче, ніж створення функції, тому ви повинні знати, що робите це.

Це лише мої думки - я не бачив офіційного виступу дизайнерів мов.


2
Паскаль (принаймні це втілення Delphi) зробив їх правильними та простими: вкладені функції поводяться так само, як і звичайні, але також мають доступ до змінних в області їх укладання. Я не думаю, що їх важко здійснити. З іншого боку, написавши багато коду Delphi, я не впевнений, що мені вкрай потрібні вкладені функції: іноді вони відчувають себе елегантними, але вони, як правило, збивають функцію, що обгороджує, що робить її важко читабельною. Також доступ до аргументів батьків може ускладнити читання програми, оскільки ці змінні отримують неявний доступ (не передаються як формальні параметри).
kostix

1
локальні функції чудово підходять як проміжний крок рефакторингу на шляху до вилучення методів. У c # вони зробили це більш цінним, як тільки вони ввели статичні локальні функції, яким заборонено захоплювати змінні з функції, що обгороджує, тому ви змушені передавати що-небудь як параметр. Статичні місцеві функції роблять пункт 3 неважливим. З моєї точки зору пункт 2 також не є проблемою.
Космін

47

Звичайно, вони є. Вам просто потрібно призначити їх змінній:

func main() {
    inc := func(x int) int { return x+1; }
}

4
Варто зазначити, ви не можете викликати такі функції (вкл.) рекурсивно.
Мохсін Кале

26

Поширені запитання (FAQ)

Чому Go не має функції X?

Кожна мова містить нові функції та не містить улюблених рис. Go був розроблений з урахуванням привабливості програмування, швидкості компіляції, ортогональності концепцій та необхідності підтримки таких функцій, як паралельність та збір сміття. Можливо, ваша улюблена функція відсутня, оскільки вона не підходить, оскільки вона впливає на швидкість компіляції або чіткість дизайну або через те, що це ускладнить основну модель системи.

Якщо вас турбує те, що в Go відсутня функція X, пробачте нас і вивчіть функції, які Go має. Ви можете виявити, що вони цікавими способами компенсують відсутність X.

Що може виправдати складність та витрати на додавання вкладених функцій? Що яу хоче зробити, чого не можна обійтися без вкладених функцій? І так далі.


19
Справедливості заради, я не думаю, що хтось продемонстрував якусь особливу складність, яку може спричинити дозволення вкладених функцій. Плюс, хоча я погоджуюсь із наведеною філософією, я не впевнений, що доцільно називати вкладені функції "функцією", настільки ж, як посилатися на їх пропуск як функцію. Чи знаєте ви про якісь ускладнення, які дозволили б вкладені функції? Я припускаю, що вони просто будуть синтаксичним цукром для лямбд (я не можу придумати жодної іншої розумної поведінки).
joshlf

Оскільки go компілюється, єдиний спосіб зробити це AFAIK створить інший синтаксис для визначення лямбда. І я справді не бачу випадку використання цього. ви не можете мати статичну функцію всередині статичної функції, створеної в режимі реального часу - що, якщо ми не введемо конкретний шлях коду, який визначає функцію?
Not_a_Golfer

Просто введіть лямбда-інтерфейс {} і введіть assert. Напр. f_lambda: = лямбда (func () rval {}) або як би не було прототипом. Синтаксис func decl цього не підтримує, але мова повністю підтримує.
BadZen


8

Вкладені функції дозволяються в Go. Вам просто потрібно призначити їх локальним змінним у зовнішній функції і викликати їх, використовуючи ці змінні.

Приклад:

func outerFunction(iterations int, s1, s2 string) int {
    someState := 0
    innerFunction := func(param string) int {
        // Could have another nested function here!
        totalLength := 0
        // Note that the iterations parameter is available
        // in the inner function (closure)
        for i := 0; i < iterations; i++) {
            totalLength += len(param)
        }
        return totalLength
    }
    // Now we can call innerFunction() freely
    someState = innerFunction(s1)
    someState += innerFunction(s2)
    return someState
}
myVar := outerFunction(100, "blah", "meh")

Внутрішні функції часто корисні для місцевих програм:

func outerFunction(...) {
    innerFunction := func(...) {
        ...
    }
    go innerFunction(...)
}

Закриття в go в деяких аспектах відрізняється від звичайної функції. Наприклад, ви не можете викликати закриття рекурсивно.
Michał Zabielski

7
@ MichałZabielski: Ви можете викликати це рекурсивно, якщо заявите про це перед тим, як визначити.
P Daddy

1

Вам просто потрібно зателефонувати йому негайно, додавши ()до кінця.

func main() {
    func inc(x int) int { return x+1; }()
}

Редагувати: не може мати назви функції ... так що це просто лямбда-функція, яку викликають відразу:

func main() {
    func(x int) int { return x+1; }()
}

1
Е-е, це не відповідає тому, що можна було б очікувати від визначення функції
corazza

1
@corazza Ах, я пропустив слово "декларація", коли прочитав питання. Моє ліжко.
Нік

1
@corazza Крім того, я також зіпсував синтаксис. Потрібно для видалення назви функції. Отже, це в основному лямбда-функція, яка викликається негайно.
Нік
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.