Sebagian besar bahasa pemrograman (baik bahasa yang diketik secara dinamis maupun statis) memiliki kata kunci dan / atau sintaksis khusus yang terlihat jauh berbeda dari mendeklarasikan variabel untuk mendeklarasikan fungsi. Saya melihat fungsi sama seperti mendeklarasikan entitas bernama lain:
Misalnya dengan Python:
x = 2
y = addOne(x)
def addOne(number):
return number + 1
Kenapa tidak:
x = 2
y = addOne(x)
addOne = (number) =>
return number + 1
Demikian pula, dalam bahasa seperti Java:
int x = 2;
int y = addOne(x);
int addOne(int x) {
return x + 1;
}
Kenapa tidak:
int x = 2;
int y = addOne(x);
(int => int) addOne = (x) => {
return x + 1;
}
Sintaksis ini sepertinya cara yang lebih alami untuk mendeklarasikan sesuatu (baik itu fungsi atau variabel) dan satu kata kunci yang kurang disukai def
atau function
dalam beberapa bahasa. Dan, IMO, ini lebih konsisten (saya melihat di tempat yang sama untuk memahami jenis variabel atau fungsi) dan mungkin membuat parser / tata bahasa sedikit lebih mudah untuk ditulis.
Saya tahu sangat sedikit bahasa menggunakan ide ini (CoffeeScript, Haskell) tetapi bahasa yang paling umum memiliki sintaks khusus untuk fungsi (Java, C ++, Python, JavaScript, C #, PHP, Ruby).
Bahkan dalam Scala, yang mendukung kedua cara (dan memiliki tipe inferensi), lebih umum menulis:
def addOne(x: Int) = x + 1
Daripada:
val addOne = (x: Int) => x + 1
IMO, setidaknya di Scala, ini mungkin versi yang paling mudah dimengerti tetapi idiom ini jarang diikuti:
val x: Int = 1
val y: Int = addOne(x)
val addOne: (Int => Int) = x => x + 1
Saya sedang mengerjakan bahasa mainan saya sendiri dan saya bertanya-tanya apakah ada jebakan jika saya merancang bahasa saya sedemikian rupa dan jika ada alasan historis atau teknis, pola ini tidak diikuti secara luas?
(int => int) addOne = (x) => {
jauh lebih "istimewa" dan "kompleks" daripadaint addOne(int) {
...Jawaban:
Saya pikir alasannya adalah bahwa sebagian besar bahasa populer berasal atau dipengaruhi oleh keluarga bahasa C sebagai lawan dari bahasa fungsional dan akarnya, kalkulus lambda.
Dan dalam bahasa-bahasa ini, fungsi bukan hanya nilai lain:
const
,readonly
ataufinal
untuk melarang mutasi), tetapi fungsi tidak dapat dipindahkan.Dari perspektif yang lebih teknis, kode (yang terdiri dari fungsi) dan data terpisah. Mereka biasanya menempati bagian memori yang berbeda, dan mereka diakses secara berbeda: kode dimuat sekali dan kemudian hanya dieksekusi (tetapi tidak dibaca atau ditulis), sedangkan data sering secara konstan dialokasikan dan tidak dialokasikan dan sedang ditulis dan dibaca, tetapi tidak pernah dieksekusi.
Dan karena C dimaksudkan sebagai "dekat dengan logam", masuk akal untuk mencerminkan perbedaan ini dalam sintaksis bahasa juga.
Pendekatan "fungsi hanyalah nilai" yang membentuk dasar pemrograman fungsional telah mendapatkan daya tarik dalam bahasa umum yang relatif baru, sebagaimana dibuktikan oleh keterlambatan pengenalan lambdas di C ++, C # dan Java (2011, 2007, 2014).
sumber
Karena penting bagi manusia untuk mengenali bahwa fungsi bukan hanya "entitas bernama lain". Kadang masuk akal untuk memanipulasi mereka seperti itu, tetapi mereka masih bisa dikenali secara sekilas.
Tidak masalah apa yang dipikirkan komputer tentang sintaksis, karena gumpalan karakter yang tidak dapat dipahami baik-baik saja untuk ditafsirkan oleh mesin, tetapi itu hampir mustahil bagi manusia untuk memahami dan memelihara.
Itu benar-benar alasan yang sama seperti mengapa kita memiliki sementara dan untuk loop, beralih dan jika lain, dll, meskipun semuanya akhirnya mendidih ke bawah untuk membandingkan dan melompat instruksi. Alasannya adalah karena itu ada untuk kepentingan manusia menjaga dan memahami kode.
Memiliki fungsi Anda sebagai "entitas bernama lain" seperti yang Anda usulkan akan membuat kode Anda lebih sulit dilihat, dan karenanya lebih sulit untuk dipahami.
sumber
whatsisname
lebih mengarah pada poin pertama (dan mengingatkan tentang bahaya menghapus kegagalan ini), sedangkan komentar Anda lebih terkait dengan bagian kedua dari pertanyaan. Memang mungkin untuk mengubah sintaks ini (dan seperti yang Anda jelaskan, sudah dilakukan berkali-kali ...) tetapi itu tidak cocok untuk semua orang (karena oop juga tidak cocok untuk semua orang.Anda mungkin tertarik untuk mempelajari bahwa, pada masa prasejarah, bahasa yang disebut ALGOL 68 menggunakan sintaks yang dekat dengan apa yang Anda usulkan. Menyadari bahwa pengidentifikasi fungsi terikat pada nilai seperti halnya pengidentifikasi lainnya, Anda dapat menggunakan bahasa tersebut mendeklarasikan fungsi (konstan) menggunakan sintaksis
Contoh nyata Anda akan membaca
Menyadari redundansi bahwa tipe awal dapat dibaca dari RHS deklarasi, dan menjadi tipe fungsi selalu dimulai dengan
PROC
, ini bisa (dan biasanya akan) dikontrak untuktetapi perhatikan bahwa
=
masih ada sebelum daftar parameter. Perhatikan juga bahwa jika Anda menginginkan variabel fungsi (yang nantinya nilai lain dari jenis fungsi yang sama dapat ditetapkan),=
harus diganti dengan:=
, memberikan salah satu dariNamun dalam hal ini kedua bentuk itu sebenarnya singkatan; karena pengidentifikasi
func var
menunjuk referensi ke fungsi yang dibuat secara lokal, formulir yang diperluas akan menjadiBentuk sintaksis khusus ini mudah digunakan, tetapi jelas tidak memiliki banyak pengikut dalam bahasa pemrograman lain. Bahkan bahasa pemrograman fungsional seperti Haskell lebih suka gaya
f n = n+1
dengan=
mengikuti daftar parameter. Saya kira alasannya terutama psikologis; setelah semua ahli matematika bahkan tidak sering suka, seperti yang saya lakukan, f = n ⟼ n + 1 lebih f ( n ) = n + 1.Omong-omong, diskusi di atas menyoroti satu perbedaan penting antara variabel dan fungsi: definisi fungsi biasanya mengikat nama ke satu nilai fungsi tertentu, yang tidak dapat diubah kemudian, sedangkan definisi variabel biasanya memperkenalkan pengidentifikasi dengan nilai awal , tetapi yang bisa berubah nanti. (Ini bukan aturan absolut; variabel fungsi dan konstanta non-fungsi memang terjadi di sebagian besar bahasa.) Selain itu, dalam bahasa yang dikompilasi nilai yang terikat dalam definisi fungsi biasanya merupakan konstanta waktu kompilasi, sehingga pemanggilan fungsi dapat dilakukan. dikompilasi menggunakan alamat tetap dalam kode. Dalam C / C ++ ini bahkan merupakan persyaratan; setara dengan ALGOL 68
tidak dapat ditulis dalam C ++ tanpa memperkenalkan fungsi pointer. Keterbatasan khusus semacam ini membenarkan menggunakan sintaks yang berbeda untuk definisi fungsi. Tetapi mereka bergantung pada semantik bahasa, dan pembenaran tidak berlaku untuk semua bahasa.
sumber
Anda menyebutkan Java dan Scala sebagai contoh. Namun, Anda mengabaikan fakta penting: itu bukan fungsi, itu adalah metode. Metode dan fungsi pada dasarnya berbeda. Fungsi adalah objek, metode milik objek.
Dalam Scala, yang memiliki fungsi dan metode, ada perbedaan berikut antara metode dan fungsi:
Jadi, penggantian yang Anda usulkan tidak berfungsi, setidaknya untuk kasus-kasus itu.
sumber
Alasan yang bisa saya pikirkan adalah:
sumber
Memutar pertanyaan, jika seseorang tidak tertarik untuk mencoba mengedit kode sumber pada mesin yang sangat terbatas RAM, atau meminimalkan waktu untuk membacanya dari floppy disk, apa yang salah dengan menggunakan kata kunci?
Tentu saja lebih baik untuk dibaca
x=y+z
daripadastore the value of y plus z into x
, tetapi itu tidak berarti bahwa karakter tanda baca secara inheren "lebih baik" daripada kata kunci. Jika variabeli
,j
dank
yangInteger
, danx
adalahReal
, mempertimbangkan baris berikut di Pascal:Baris pertama akan melakukan pembagian integer memotong, sedangkan yang kedua akan melakukan pembagian bilangan real. Perbedaannya dapat dibuat dengan baik karena penggunaan Pascal
div
sebagai operator divisi-pemotongan-bilangannya, daripada mencoba menggunakan tanda baca yang sudah memiliki tujuan lain (pembagian bilangan real).Walaupun ada beberapa konteks yang dapat membantu membuat definisi fungsi ringkas (misalnya lambda yang digunakan sebagai bagian dari ekspresi lain), fungsi umumnya dianggap menonjol dan mudah dikenali secara visual sebagai fungsi. Meskipun dimungkinkan untuk membuat perbedaan jauh lebih halus dan tidak menggunakan karakter tanda baca, apa gunanya? Mengatakan
Function Foo(A,B: Integer; C: Real): String
memperjelas nama fungsi, parameter apa yang diharapkan, dan apa yang mengembalikannya. Mungkin seseorang dapat mempersingkatnya dengan enam atau tujuh karakter dengan menggantiFunction
dengan beberapa karakter tanda baca, tetapi apa yang akan diperoleh?Hal lain yang perlu diperhatikan adalah bahwa ada sebagian besar kerangka kerja perbedaan mendasar antara deklarasi yang akan selalu mengaitkan nama dengan metode tertentu atau pengikatan virtual tertentu, dan yang menciptakan variabel yang awalnya mengidentifikasi metode atau pengikatan tertentu, tetapi dapat diubah saat runtime untuk mengidentifikasi yang lain. Karena ini adalah konsep yang sangat berbeda secara semantik dalam kebanyakan kerangka kerja prosedural, masuk akal bahwa mereka harus memiliki sintaks yang berbeda.
sumber
void f() {}
sebenarnya lebih pendek dari pada persamaan lambda dalam C ++ (auto f = [](){};
), C # (Action f = () => {};
) dan Java (Runnable f = () -> {};
). Keringkasan lambda berasal dari tipe inferensi dan penghilanganreturn
, tetapi saya tidak berpikir itu terkait dengan pertanyaan ini.Yah, alasannya mungkin, bahwa bahasa-bahasa itu tidak cukup fungsional, bisa dikatakan. Dengan kata lain, Anda jarang mendefinisikan fungsi. Dengan demikian, penggunaan kata kunci tambahan dapat diterima.
Dalam bahasa-bahasa dari warisan ML atau Miranda, OTOH, Anda paling sering mendefinisikan fungsi. Lihatlah beberapa kode Haskell, misalnya. Secara harfiah sebagian besar urutan definisi fungsi, banyak dari mereka memiliki fungsi lokal dan fungsi lokal dari fungsi-fungsi lokal. Oleh karena itu, kata kunci yang menyenangkan di Haskell akan menjadi kesalahan dan membutuhkan pernyataan penilaian dalam bahasa imperatif untuk memulai dengan penetapan . Sebab penugasan mungkin sejauh ini merupakan pernyataan tunggal yang paling sering.
sumber
Secara pribadi, saya tidak melihat cacat fatal dalam ide Anda; Anda mungkin menemukan bahwa itu lebih sulit daripada yang Anda harapkan untuk mengekspresikan hal-hal tertentu menggunakan sintaks baru Anda, dan / atau Anda mungkin perlu merevisinya (menambahkan berbagai case khusus dan fitur lainnya, dll), tetapi saya ragu Anda akan menemukan sendiri harus meninggalkan ide sepenuhnya.
Sintaks yang Anda usulkan terlihat kurang lebih seperti varian dari beberapa gaya notasi yang kadang-kadang digunakan untuk mengekspresikan fungsi atau jenis fungsi dalam matematika. Ini berarti bahwa, seperti semua tata bahasa, mungkin akan lebih menarik bagi beberapa programmer daripada yang lain. (Sebagai ahli matematika, kebetulan saya menyukainya.)
Namun, Anda harus mencatat bahwa di sebagian besar bahasa,
def
sintaks gaya-(yaitu sintaksis tradisional) tidak berperilaku berbeda dari tugas variabel standar.C
danC++
keluarga, fungsi umumnya tidak diperlakukan sebagai "objek", yaitu potongan data yang diketik untuk disalin dan diletakkan di tumpukan dan yang lainnya. (Ya, Anda dapat memiliki pointer fungsi, tetapi itu masih menunjuk pada kode yang dapat dieksekusi, bukan pada "data" dalam arti umum.)self
(yang, omong-omong, sebenarnya bukan kata kunci; Anda bisa membuat pengidentifikasi valid argumen pertama dari suatu metode).Anda perlu mempertimbangkan apakah sintaks baru Anda secara akurat (dan mudah-mudahan secara intuitif) mewakili apa yang sebenarnya dilakukan oleh kompiler atau juru bahasa. Mungkin membantu untuk membaca, katakanlah, perbedaan antara lambdas dan metode dalam Ruby; ini akan memberi Anda gambaran tentang bagaimana paradigma fungsi-hanya-data Anda berbeda dari paradigma OO / prosedural yang khas.
sumber
Untuk beberapa bahasa fungsi bukan nilai. Dalam bahasa seperti itu dikatakan
adalah definisi fungsi, sedangkan
menyatakan sebuah konstanta, membingungkan karena Anda menggunakan satu sintaks yang berarti dua hal.
Bahasa lain, seperti ML, Haskell, dan Skema memperlakukan fungsi sebagai nilai kelas 1, tetapi memberikan pengguna dengan sintaks khusus untuk mendeklarasikan konstanta bernilai fungsi. * Mereka menerapkan aturan bahwa "penggunaan memperpendek formulir". Yaitu jika sebuah konstruksi adalah umum dan bertele-tele, Anda harus memberikan pengguna sebuah steno. Tidak tepat untuk memberi pengguna dua sintaks yang berbeda yang artinya persis sama; terkadang keanggunan harus dikorbankan untuk keperluan.
Jika, dalam bahasa Anda, fungsi adalah kelas 1, lalu mengapa tidak mencoba mencari sintaksis yang cukup ringkas sehingga Anda tidak akan tergoda untuk menemukan gula sintaksis?
- Edit -
Masalah lain yang belum pernah dibicarakan adalah rekursi. Jika Anda mengizinkan
dan kamu mengizinkan
apakah itu mengikuti yang harus Anda izinkan
Dalam bahasa yang malas (seperti Haskell), tidak ada masalah di sini. Dalam bahasa yang pada dasarnya tidak ada pemeriksaan statis (seperti LISP), tidak ada masalah di sini. Tetapi dalam bahasa yang ingin dicek secara statis, Anda harus berhati-hati tentang bagaimana aturan pemeriksaan statis ditentukan, jika Anda ingin mengizinkan dua yang pertama dan melarang yang terakhir.
- Akhir Pengeditan -
* Dapat dikatakan bahwa Haskell tidak termasuk dalam daftar ini. Ini menyediakan dua cara untuk mendeklarasikan suatu fungsi, tetapi keduanya, dalam beberapa hal, generalisasi sintaksis untuk menyatakan konstanta dari tipe lain
sumber
Ini mungkin berguna pada bahasa dinamis di mana jenisnya tidak begitu penting, tetapi itu tidak bisa dibaca dalam bahasa yang diketik statis di mana selalu Anda ingin tahu jenis variabel Anda. Juga, dalam bahasa berorientasi objek, cukup penting untuk mengetahui jenis variabel Anda, untuk mengetahui operasi apa yang didukungnya.
Dalam kasus Anda, fungsi dengan 4 variabel adalah:
Ketika saya melihat header fungsi dan melihat (x, y, z, s) tapi saya tidak tahu jenis-jenis variabel ini. Jika saya ingin mengetahui tipe
z
yang merupakan parameter ketiga, saya harus melihat bagian awal fungsi dan mulai menghitung 1, 2, 3 dan kemudian melihat jenisnyadouble
. Dahulu saya melihat langsung dan melihatdouble z
.sumber
var addOne = (int x, long y, double z, String s) => { x + 1 }
dalam bahasa yang diketik secara statis yang bukan pilihan Anda (contoh: C #, C ++, Scala). Bahkan inferensi tipe lokal yang sangat terbatas yang digunakan oleh C # sepenuhnya cukup untuk ini. Jadi jawaban ini hanya mengkritik sintaksis tertentu yang meragukan di tempat pertama, dan tidak benar-benar digunakan di mana saja (meskipun sintaksis Haskell memiliki masalah yang sangat mirip).Ada alasan yang sangat sederhana untuk memiliki perbedaan dalam sebagian besar bahasa: ada kebutuhan untuk membedakan evaluasi dan deklarasi . Contoh Anda baik: mengapa tidak suka variabel? Nah, variabel ekspresi segera dievaluasi.
Haskell memiliki model khusus di mana tidak ada perbedaan antara evaluasi dan deklarasi, itulah sebabnya tidak perlu untuk kata kunci khusus.
sumber
Fungsi dideklarasikan secara berbeda dari literal, objek, dll. Di sebagian besar bahasa karena mereka digunakan secara berbeda, didebug secara berbeda, dan menimbulkan sumber kesalahan potensial yang berbeda.
Jika referensi objek dinamis atau objek yang dapat diubah dilewatkan ke fungsi, fungsi tersebut dapat mengubah nilai objek saat dijalankan. Efek samping semacam ini dapat menyulitkan untuk mengikuti apa fungsi akan dilakukan jika bersarang di dalam ekspresi yang kompleks, dan ini merupakan masalah umum dalam bahasa seperti C ++ dan Java.
Pertimbangkan men-debug beberapa modul kernel di Java, di mana setiap objek memiliki operasi toString (). Meskipun mungkin diharapkan bahwa metode toString () harus mengembalikan objek, mungkin perlu membongkar dan memasang kembali objek untuk menerjemahkan nilainya ke objek String. Jika Anda mencoba men-debug metode yang toString () akan memanggil (dalam skenario kait-dan-templat) untuk melakukan tugasnya, dan secara tidak sengaja menyorot objek di jendela variabel dari sebagian besar IDE, itu dapat menyebabkan macet debugger. Ini karena IDE akan mencoba toString () objek yang memanggil kode Anda sedang dalam proses debugging. Tidak ada nilai primitif yang pernah omong kosong seperti ini karena makna semantik dari nilai-nilai primitif didefinisikan oleh bahasa, bukan programmer.
sumber