Mengapa sebagian besar bahasa pemrograman memiliki kata kunci atau sintaksis khusus untuk mendeklarasikan fungsi? [Tutup]


39

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 defatau functiondalam 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?


9
Alasannya kemungkinan historis. Siapa pun yang pertama kali melakukannya dengan cara itu, dan semua orang menyalin. Tapi saya ragu kita bisa tahu pasti.

29
Saya pikir itu karena fungsi atau metode tidak hanya sekedar entitas bernama lain . Dalam bahasa fungsional, mereka (Anda dapat mengedarkannya dll.) Tetapi dalam bahasa lain (seperti Jawa) fungsi atau metode adalah sesuatu yang sama sekali berbeda dari variabel sederhana dan tidak dapat diperlakukan seperti itu (saya akui Java 8 jenis melemahkan ini pernyataan), jadi masuk akal untuk mendefinisikan fungsi / metode secara berbeda, karena mereka berperilaku berbeda.
11684

15
Saya tidak setuju bahwa proposal Anda adalah cara yang lebih alami untuk mendeklarasikan fungsi. Saya benar-benar tidak suka sintaks Coffeescript dan bagian dari itu berkaitan dengan sintaks deklarasi fungsinya. Faktanya adalah, jika tidak rusak jangan memperbaikinya. Menulis 'def' atau 'fungsi' bukanlah masalah besar dan jauh lebih jelas dibandingkan dengan beberapa simbol mewah seperti Perl yang dengan mata tanpa berpikir atau lelah dapat dengan mudah dikira sebagai sesuatu yang lain. Pada catatan yang sepenuhnya pribadi saya juga berpikir sintaks yang Anda sarankan dalam contoh terlihat jauh lebih jelek daripada sintaksis saat ini.
Roy

22
Bagaimana '=>' bukan "kata kunci atau sintaks khusus untuk mendeklarasikan fungsi"?
TML

11
Saya tidak tahu tentang Anda, tetapi bagi saya (int => int) addOne = (x) => {jauh lebih "istimewa" dan "kompleks" daripada int addOne(int) {...
Bogdan Alexandru

Jawaban:


48

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:

  • Di C ++, C # dan Java, Anda dapat membebani fungsi: Anda dapat memiliki dua fungsi dengan nama yang sama, tetapi berbeda tanda tangan.
  • Dalam C, C ++, C # dan Java, Anda bisa memiliki nilai yang mewakili fungsi, tetapi pointer fungsi, functors, delegasi dan antarmuka fungsional semuanya berbeda dari fungsi itu sendiri. Sebagian alasannya adalah bahwa sebagian besar dari mereka sebenarnya bukan hanya fungsi, mereka adalah fungsi bersama dengan beberapa (dapat berubah) keadaan.
  • Variabel dapat diubah secara default (Anda harus menggunakan const, readonlyatau finaluntuk 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).


12
C # memiliki fungsi anonim - yang hanya lambda tanpa inferensi tipe dan sintaks yang kikuk - sejak tahun 2005.
Eric Lippert

2
"Dari perspektif yang lebih teknis, kode (yang terdiri dari fungsi) dan data terpisah" - beberapa bahasa, yang paling diketahui adalah LISP tidak membuat pemisahan itu dan tidak benar-benar memperlakukan kode dan data terlalu berbeda. (LISP adalah contoh paling terkenal, tetapi ada banyak bahasa lain seperti REBOL yang melakukan ini)
Benjamin Gruenbaum

1
@BenjaminGruenbaum saya berbicara tentang kode yang dikompilasi dalam memori, bukan tentang tingkat bahasa.
svick

C memiliki pointer fungsi yang, setidaknya sedikit, dapat dianggap sebagai nilai lain. Nilai yang berbahaya untuk dipastikan, tetapi hal-hal aneh telah terjadi.
Patrick Hughes

@ PatrickHughes Ya, saya menyebutkannya di poin kedua saya. Tetapi fungsi pointer sangat berbeda dari fungsi.
svick

59

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.


12
Saya tidak setuju bahwa memperlakukan fungsi sebagai entitas yang dinamai tentu membuat kode lebih sulit untuk dipahami. Itu hampir pasti benar untuk kode apa pun yang mengikuti paradigma prosedural, tetapi mungkin tidak benar untuk kode yang mengikuti paradigma fungsional.
Kyle Strand

29
"Itu benar-benar alasan yang sama seperti mengapa kita memiliki waktu dan untuk loop, beralih dan jika lain, dll, meskipun semuanya akhirnya bermuara pada instruksi perbandingan dan lompati. " Jika semua programmer merasa nyaman dengan bahasa mesin, kita tidak akan memerlukan semua bahasa pemrograman mewah ini (level tinggi atau rendah). Tetapi kenyataannya adalah: setiap orang memiliki tingkat kenyamanan yang berbeda dengan seberapa dekat dengan mesin yang mereka inginkan untuk menulis kode mereka. Setelah Anda menemukan level Anda, Anda hanya perlu tetap menggunakan sintaks yang diberikan kepada Anda (atau menulis spesifikasi dan kompiler bahasa Anda sendiri jika Anda benar-benar terganggu).
Hoki

12
+1. Versi yang lebih ringkas mungkin adalah "Mengapa semua bahasa alami membedakan kata benda dari kata kerja?"
msw

2
"gumpalan karakter yang tidak dapat dipahami adalah baik untuk ditafsirkan oleh mesin, tapi itu tidak mungkin bagi manusia untuk memahami dan mempertahankan" Sungguh suatu kualifikasi yang terburu-buru untuk perubahan sintaksis sederhana seperti yang diusulkan dalam pertanyaan. Satu cepat beradaptasi dengan sintaks. Proposal ini adalah keberangkatan yang jauh lebih radikal dari konvensi daripada metode OO memanggil sintaks objek. Metode (args) pada masanya, namun yang terakhir belum terbukti tidak mungkin bagi manusia untuk memahami dan memelihara. Terlepas dari kenyataan bahwa dalam kebanyakan bahasa OO metode tidak boleh dianggap sebagai disimpan dalam instance kelas.
Marc van Leeuwen

4
@MarcvanLeeuwen. Komentar Anda benar dan masuk akal, tetapi beberapa kebingungan dipicu oleh cara posting asli disunting. Sebenarnya ada 2 pertanyaan: (i) Kenapa begini? dan (ii) Mengapa tidak sebaliknya? . Jawabannya whatsisnamelebih 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.
Hoki

10

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

function-type name = ( daftar parameter ) result-type : body ;

Contoh nyata Anda akan membaca

PROC (INT)INT add one = (INT n) INT: n+1;

Menyadari redundansi bahwa tipe awal dapat dibaca dari RHS deklarasi, dan menjadi tipe fungsi selalu dimulai dengan PROC, ini bisa (dan biasanya akan) dikontrak untuk

PROC add one = (INT n) INT: n+1;

tetapi 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 dari

PROC (INT)INT func var := (INT n) INT: n+1;
PROC func var := (INT n) INT: n+1;

Namun dalam hal ini kedua bentuk itu sebenarnya singkatan; karena pengidentifikasi func varmenunjuk referensi ke fungsi yang dibuat secara lokal, formulir yang diperluas akan menjadi

REF PROC (INT)INT func var = LOC PROC (INT)INT := (INT n) INT: n+1;

Bentuk 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+1dengan = mengikuti daftar parameter. Saya kira alasannya terutama psikologis; setelah semua ahli matematika bahkan tidak sering suka, seperti yang saya lakukan, f = nn + 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

PROC (REAL) REAL f = IF mood=sunny THEN sin ELSE cos FI;

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.


1
"Matematikawan tidak suka f = nn + 1 lebih dari f ( n ) = n + 1" ... Belum lagi fisikawan, yang suka menulis hanya f = n + 1 ...
leftaroundabout

1
@leftaroundtentang itu karena ⟼ sulit untuk menulis. Secara jujur.
Pierre Arlaud

8

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:

  • metode bisa generik, fungsinya tidak bisa
  • metode tidak dapat memiliki, satu atau beberapa daftar parameter, fungsi selalu memiliki tepat satu daftar parameter
  • metode dapat memiliki daftar parameter implisit, fungsinya tidak bisa
  • metode dapat memiliki parameter opsional dengan argumen default, fungsi tidak bisa
  • metode dapat memiliki parameter berulang, fungsi tidak bisa
  • metode dapat memiliki parameter dengan nama, fungsi tidak bisa
  • metode dapat dipanggil dengan argumen bernama, fungsi tidak bisa
  • fungsi adalah objek, metode tidak

Jadi, penggantian yang Anda usulkan tidak berfungsi, setidaknya untuk kasus-kasus itu.


2
"Fungsi adalah objek, metode milik objek." Apakah itu seharusnya berlaku untuk semua bahasa (seperti C ++)?
svick

9
"Metode dapat memiliki parameter dengan nama, fungsi tidak bisa" - ini bukan perbedaan mendasar antara metode dan fungsi, itu hanya kekhasan Scala. Sama dengan kebanyakan (semua?) Yang lain.
user253751

1
Metode pada dasarnya hanyalah jenis fungsi khusus. -1. Perhatikan bahwa Java (dan dengan ekstensi Scala) tidak memiliki fungsi jenis apa pun. Ini "fungsi" adalah objek dengan satu metode (secara umum fungsi mungkin bukan objek atau bahkan nilai-nilai kelas pertama; apakah mereka tergantung pada bahasa).
Jan Hudec

@ JanHudec: Secara semantik, Java memiliki fungsi dan metode; ia menggunakan terminologi "metode statis" ketika merujuk ke fungsi, tetapi terminologi seperti itu tidak menghapus perbedaan semantik.
supercat

3

Alasan yang bisa saya pikirkan adalah:

  • Lebih mudah bagi kompiler untuk mengetahui apa yang kami nyatakan.
  • Penting bagi kita untuk mengetahui (dengan cara sepele) apakah ini suatu fungsi atau variabel. Fungsi biasanya kotak hitam dan kami tidak peduli dengan implementasi internal mereka. Saya tidak suka inferensi tipe pada tipe kembali di Scala, karena saya percaya bahwa lebih mudah untuk menggunakan fungsi yang memiliki tipe kembali: seringkali hanya satu-satunya dokumentasi yang disediakan.
  • Dan yang paling penting adalah mengikuti strategi kerumunan yang digunakan dalam merancang bahasa pemrograman. C ++ dibuat untuk mencuri programmer C, dan Java dirancang dengan cara yang tidak menakuti programmer C ++, dan C # untuk menarik programmer Java. Bahkan C #, yang saya pikir adalah bahasa yang sangat modern dengan tim yang luar biasa di belakangnya, menyalin beberapa kesalahan dari Jawa atau bahkan dari C.

2
"... dan C # untuk menarik programmer Java" - ini dipertanyakan, C # jauh lebih mirip dengan C ++ daripada Java. dan kadang-kadang sepertinya dibuat dengan sengaja tidak sesuai dengan Jawa (tidak ada wildcard, tidak ada kelas batin, tidak ada kovarian jenis kembali ...)
Sarge Borsch

1
@SargeBorsch Saya tidak berpikir itu sengaja dibuat tidak kompatibel dengan Java, saya cukup yakin tim C # hanya mencoba untuk melakukannya dengan benar, atau melakukannya dengan lebih baik, namun Anda ingin melihatnya. Microsoft sudah menulis Java & JVM mereka sendiri dan dituntut. Jadi tim C # kemungkinan besar sangat termotivasi untuk melampaui Jawa. Ini tentu tidak cocok, dan lebih baik untuk itu. Java dimulai dengan beberapa batasan dasar dan saya senang bahwa tim C # memilih, misalnya, untuk melakukan generik secara berbeda (terlihat pada sistem tipe dan bukan hanya gula).
codenheim

2

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+zdaripada store the value of y plus z into x, tetapi itu tidak berarti bahwa karakter tanda baca secara inheren "lebih baik" daripada kata kunci. Jika variabel i, jdan kyang Integer, dan xadalah Real, mempertimbangkan baris berikut di Pascal:

k := i div j;
x := i/j;

Baris pertama akan melakukan pembagian integer memotong, sedangkan yang kedua akan melakukan pembagian bilangan real. Perbedaannya dapat dibuat dengan baik karena penggunaan Pascaldiv 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): Stringmemperjelas nama fungsi, parameter apa yang diharapkan, dan apa yang mengembalikannya. Mungkin seseorang dapat mempersingkatnya dengan enam atau tujuh karakter dengan mengganti Functiondengan 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.


3
Saya tidak berpikir pertanyaan ini tentang keringkasan. Terutama karena misalnya 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 penghilangan return, tetapi saya tidak berpikir itu terkait dengan pertanyaan ini.
svick

2

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.


1

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, defsintaks gaya-(yaitu sintaksis tradisional) tidak berperilaku berbeda dari tugas variabel standar.

  • Dalam C dan C++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.)
  • Dalam sebagian besar bahasa OO, ada penanganan khusus untuk metode (yaitu fungsi anggota); yaitu, mereka bukan hanya fungsi yang dideklarasikan di dalam lingkup definisi kelas. Perbedaan yang paling penting adalah bahwa objek tempat metode ini dipanggil biasanya dilewatkan sebagai parameter pertama implisit untuk metode tersebut. Python membuat ini eksplisit denganself (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.


1

Untuk beberapa bahasa fungsi bukan nilai. Dalam bahasa seperti itu dikatakan

val f = << i : int  ... >> ;

adalah definisi fungsi, sedangkan

val a = 1 ;

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

{ 
    val f = << i : int ... g(i-1) ... >> ;
    val g = << j : int ... f(i-1) ... >> ;
    f(x)
}

dan kamu mengizinkan

{
    val a = 42 ;
    val b = a + 1 ;
    a
} ,

apakah itu mengikuti yang harus Anda izinkan

{
    val a = b + 1 ; 
    val b = a - 1 ;
    a
} ?

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


0

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:

(int, long, double, String => int) addOne = (x, y, z, s) => {
  return x + 1;
}

Ketika saya melihat header fungsi dan melihat (x, y, z, s) tapi saya tidak tahu jenis-jenis variabel ini. Jika saya ingin mengetahui tipe zyang merupakan parameter ketiga, saya harus melihat bagian awal fungsi dan mulai menghitung 1, 2, 3 dan kemudian melihat jenisnya double. Dahulu saya melihat langsung dan melihat double z.


3
Tentu saja ada hal luar biasa yang disebut inferensi jenis, yang memungkinkan Anda untuk menulis sesuatu seperti 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).
amon

4
@amon Jawabannya mungkin mengkritik sintaksis, tetapi juga menunjukkan bahwa sintaksis yang dimaksudkan dalam pertanyaan mungkin tidak "alami" untuk semua orang.

1
@amon Kegunaan inferensi tipe bukan hal yang luar biasa bagi semua orang. Jika Anda membaca kode lebih sering dari yang Anda tulis kode, maka ketik inferensi buruk karena Anda harus menyimpulkan di kepala Anda jenis ketika membaca kode; dan jauh lebih cepat untuk membaca jenis daripada benar-benar berpikir tentang jenis apa yang digunakan.
m3th0dman

1
Kritik itu tampaknya sah bagi saya. Untuk Java, OP tampaknya berpikir [input, output, nama, input] adalah def fungsi yang lebih sederhana / lebih jelas daripada hanya [output, nama, input]? Seperti yang dinyatakan @ m3th0dman, dengan tanda tangan yang lebih panjang, pendekatan 'bersih' OP menjadi sangat bertele-tele.
Michael

0

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.


2
Tetapi beberapa sintaks untuk fungsi lambda juga memecahkan ini, jadi mengapa tidak menggunakannya?
svick

0

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.

Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.