Apa masalah yang dapat diatasi dengan tidak mengizinkan deklarasi fungsi bertingkat di Go?

87

Lambdas bekerja seperti yang diharapkan:

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

Namun, pernyataan berikut di dalam deklarasi tidak diperbolehkan:

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

Untuk alasan apa fungsi bertingkat tidak diizinkan?

corazza.dll
sumber
hmm, saya tidak tahu apakah Anda bermaksud melakukan ini func main() { func (x int) int { return x+1; }(3) }
ymg
@Yasir. tapi itu lambda juga, bukan? Saya tidak mendapatkan komentar Anda ...
corazza
fungsi apa yang memungkinkan contoh kedua dalam sintaksis, yang tidak didukung oleh kasus pertama?
Not_a_Golfer
@yannbane itu adalah ekspresi lambda, saya rasa Anda tidak dapat mendeklarasikan fungsi di dalam fungsi lain seperti JS. Jadi menurut saya yang paling cocok untuk Anda adalah menggunakan lambda.
ymg
@Not_a_Golfer: Salah satu kemungkinannya adalah mengimplementasikannya seperti yang dilakukan JavaScript, pada dasarnya menetapkan fungsi ke variabel sangat berbeda dari mendeklarasikan fungsi karena aliran kontrol memengaruhi variabel tersebut, sementara fungsi di JavaScript tidak terpengaruh. Itu berarti Anda dapat memanggil inc()contoh kedua sebelum deklarasi sebenarnya. Tapi! Saya mencari alasan, saya tidak tahu banyak tentang Go, tetapi saya ingin mempelajari logika di balik aturan ini.
corazza

Jawaban:

54

Saya rasa ada 3 alasan mengapa fitur yang jelas ini tidak diperbolehkan

  1. Ini akan sedikit mempersulit kompiler. Saat ini kompilator mengetahui bahwa semua fungsi berada di level teratas.
  2. Ini akan membuat kelas baru dari kesalahan programmer - Anda dapat memfaktor ulang sesuatu dan secara tidak sengaja membuat sarang beberapa fungsi.
  3. Memiliki sintaks yang berbeda untuk fungsi dan closure adalah hal yang baik. Membuat penutupan berpotensi lebih mahal daripada membuat suatu fungsi jadi Anda harus tahu Anda sedang melakukannya.

Itu hanya pendapat saya - saya belum melihat pernyataan resmi dari desainer bahasa.

Nick Craig-Wood
sumber
2
Pascal (setidaknya itu inkarnasi Delphi) membuatnya benar dan sederhana: fungsi bertingkat berperilaku seperti biasa tetapi juga memiliki akses ke variabel dalam lingkup fungsi yang melingkupinya. Menurut saya ini tidak sulit untuk diterapkan. Di sisi lain, setelah menulis banyak kode Delphi, saya tidak yakin saya sangat membutuhkan fungsi bersarang: kadang-kadang mereka terasa bagus tetapi mereka cenderung meledakkan fungsi penutup sehingga sulit dibaca. Juga mengakses argumen orang tua mereka dapat membuat program sulit dibaca karena variabel-variabel ini diakses secara implisit (tidak diteruskan sebagai parameter formal).
kostix
1
fungsi lokal sangat bagus sebagai langkah refactoring menengah dalam cara Anda mengekstrak metode. Di c # mereka membuatnya lebih berharga setelah mereka memperkenalkan fungsi lokal statis yang tidak diizinkan untuk menangkap variabel dari fungsi penutup sehingga Anda dipaksa untuk melewatkan apapun sebagai parameter. Fungsi lokal statis membuat poin 3 bukan masalah. Poin 2 juga bukan masalah dari sudut pandang saya.
Cosmin Sontu
47

Tentu mereka. Anda hanya perlu menetapkannya ke variabel:

func main() {
    inc := func(x int) int { return x+1; }
}
Matt Williamson
sumber
5
perlu dicatat, Anda tidak dapat memanggil fungsi seperti itu (inc) secara rekursif.
Mohsin Kale
1
untuk memanggilnya secara rekursif, Anda harus meneruskan menyatakanvar inc func(int) int
Izana
28

Pertanyaan yang Sering Diajukan (FAQ)

Mengapa Go tidak memiliki fitur X?

Setiap bahasa berisi fitur baru dan menghilangkan fitur favorit seseorang. Go dirancang dengan memperhatikan kesesuaian pemrograman, kecepatan kompilasi, ortogonalitas konsep, dan kebutuhan untuk mendukung fitur seperti konkurensi dan pengumpulan sampah. Fitur favorit Anda mungkin hilang karena tidak sesuai, karena memengaruhi kecepatan kompilasi atau kejelasan desain, atau karena akan membuat model sistem dasar terlalu sulit.

Jika Anda merasa terganggu karena Go tidak memiliki fitur X, maafkan kami dan selidiki fitur yang dimiliki Go. Anda mungkin menemukan bahwa mereka mengimbangi dengan cara yang menarik untuk kurangnya X.

Apa yang membenarkan kerumitan dan biaya penambahan fungsi bersarang? Apa yang ingin Anda lakukan yang tidak dapat Anda lakukan tanpa fungsi bertingkat? Dan lain-lain.

peterSO
sumber
19
Agar adil, menurut saya tidak ada orang yang menunjukkan kerumitan tertentu yang memungkinkan fungsi bersarang akan menyebabkannya. Plus, meskipun saya setuju dengan filosofi yang dikutip, saya tidak yakin masuk akal untuk menyebut fungsi bertingkat sebagai "fitur", sama seperti merujuk pada penghilangannya sebagai fitur. Apakah Anda mengetahui adanya komplikasi yang memungkinkan fungsi bersarang? Saya berasumsi mereka hanya akan menjadi gula sintaksis untuk lambda (saya tidak bisa memikirkan perilaku masuk akal lainnya).
joshlf
Sejak go dikompilasi, satu-satunya cara untuk melakukan AFAIK ini, akan membuat sintaks lain untuk mendefinisikan lambda. Dan saya benar-benar tidak melihat kasus penggunaan untuk itu. Anda tidak dapat memiliki fungsi statis dalam fungsi statis yang dibuat secara real time - bagaimana jika kita tidak memasukkan jalur kode spesifik yang mendefinisikan fungsi tersebut?
Not_a_Golfer
Cukup teruskan antarmuka lambda {} dan ketikkan assert. Misalnya. f_lambda: = lambda (func () rval {}) atau apa pun yang menjadi prototipe. Sintaks func dec tidak mendukung ini tetapi bahasa sepenuhnya mendukung.
BadZen
8

Fungsi bertingkat diperbolehkan di Go. Anda hanya perlu menetapkannya ke variabel lokal di dalam fungsi luar, dan memanggilnya menggunakan variabel tersebut.

Contoh:

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")

Fungsi bagian dalam sering kali berguna untuk goroutine lokal:

func outerFunction(...) {
    innerFunction := func(...) {
        ...
    }
    go innerFunction(...)
}
vthorsteinsson.dll
sumber
Closure in go berbeda dalam beberapa aspek dari fungsi biasa. Misalnya Anda tidak dapat memanggil closure secara rekursif.
Michał Zabielski
7
@ MichałZabielski: Anda dapat memanggilnya secara rekursif jika Anda mendeklarasikannya sebelum Anda mendefinisikannya.
P Daddy
1

Anda hanya perlu segera memanggilnya dengan menambahkan di ()bagian akhir.

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

Sunting: tidak dapat memiliki nama fungsi ... jadi itu hanya fungsi lambda yang dipanggil langsung:

func main() {
    func(x int) int { return x+1; }()
}
Nick
sumber
1
Uhh itu tidak sesuai dengan apa yang diharapkan dari definisi fungsi
corazza
1
@corazza Ah, saya melewatkan kata "deklarasi" ketika saya membaca pertanyaan itu. Salahku.
Nick
1
@ Corazza Juga, saya mengacaukan sintaks juga. Diperlukan untuk menghapus nama fungsi. Jadi, pada dasarnya ini adalah fungsi lambda yang dipanggil segera.
Nick