Desain Sintaks - Mengapa menggunakan tanda kurung ketika tidak ada argumen yang disampaikan?

66

Dalam banyak bahasa, sintaks function_name(arg1, arg2, ...)digunakan untuk memanggil suatu fungsi. Ketika kita ingin memanggil fungsi tanpa argumen, kita harus melakukannya function_name().

Saya merasa aneh bahwa kompiler atau juru bahasa naskah harus ()berhasil mendeteksinya sebagai panggilan fungsi. Jika suatu variabel diketahui bisa dipanggil, mengapa tidak function_name;cukup?

Di sisi lain, dalam beberapa bahasa kita dapat melakukan: function_name 'test';atau bahkan function_name 'first' 'second';untuk memanggil fungsi atau perintah.

Saya pikir kurung akan lebih baik jika mereka hanya diperlukan untuk menyatakan urutan prioritas, dan di tempat lain adalah opsional. Misalnya, melakukan if expression == true function_name;harus sama validnya dengan if (expression == true) function_name();.

Hal yang paling menyebalkan menurut saya adalah melakukan 'SOME_STRING'.toLowerCase()ketika jelas tidak ada argumen yang diperlukan oleh fungsi prototipe. Mengapa para desainer memutuskan menentang yang lebih sederhana 'SOME_STRING'.lower?

Penafian: Jangan salah, saya suka sintaksis C-like! ;) Saya hanya bertanya apakah itu bisa lebih baik. Apakah memerlukan ()memiliki keunggulan kinerja, atau apakah membuatnya lebih mudah memahami kode? Saya benar-benar ingin tahu apa alasan sebenarnya.

David Refoua
sumber
103
Apa yang akan Anda lakukan jika Anda ingin melewatkan fungsi sebagai argumen ke fungsi lain?
Vincent Savard
30
Selama kode diperlukan untuk dibaca oleh manusia, keterbacaan adalah raja.
Tulains Córdova
75
Itu hal yang mudah dibaca, Anda bertanya (), namun hal yang menonjol dalam posting Anda adalah if (expression == true)pernyataan. Anda khawatir tentang berlebihan (), tetapi kemudian menggunakan == true:) berlebihan
David Arno
15
Visual Basic mengizinkan ini
edc65
14
Itu wajib di Pascal, Anda hanya menggunakan parens untuk fungsi dan prosedur yang mengambil argumen.
RemcoGerlich

Jawaban:

251

Untuk bahasa yang menggunakan fungsi kelas satu , cukup umum bahwa sintaks yang merujuk ke suatu fungsi adalah:

a = object.functionName

sedangkan tindakan memanggil fungsi itu adalah:

b = object.functionName()

adalam contoh di atas akan merujuk ke fungsi di atas (dan Anda bisa menyebutnya dengan melakukan a()), sementara bakan berisi nilai pengembalian fungsi.

Sementara beberapa bahasa dapat melakukan panggilan fungsi tanpa tanda kurung, itu bisa membingungkan apakah mereka memanggil fungsi, atau hanya merujuk ke fungsi.

Nathan Merrill
sumber
9
Anda mungkin ingin menyebutkan bahwa perbedaan ini hanya menarik dengan adanya efek samping - untuk fungsi murni tidak masalah
Bergi
73
@Bergi: Sangat penting untuk fungsi murni! Jika saya memiliki fungsi make_listyang mengemas argumennya ke dalam daftar, ada perbedaan besar antara f(make_list)meneruskan fungsi ke fatau melewati daftar kosong.
user2357112
12
@Bergi: Meskipun begitu, saya masih ingin dapat melewatkan SATInstance.solveatau fungsi murni luar biasa mahal lainnya ke fungsi lain tanpa segera berusaha menjalankannya. Itu juga membuat segalanya benar-benar canggung jika someFunctionmelakukan sesuatu yang berbeda tergantung pada apakah someFunctiondinyatakan murni. Sebagai contoh, jika saya memiliki (pseudo) func f() {return g}, func g() {doSomethingImpure}dan func apply(x) {x()}, kemudian apply(f)panggilan f. Jika saya kemudian menyatakan itu fmurni, maka tiba-tiba apply(f)beralih gke apply, dan applymemanggil gdan melakukan sesuatu yang tidak murni.
user2357112
6
@ user2357112 Strategi evaluasi tidak tergantung pada kemurnian. Menggunakan sintaks panggilan sebagai anotasi kapan untuk mengevaluasi apa yang mungkin (tetapi mungkin canggung), namun itu tidak mengubah hasil atau kebenarannya. Mengenai contoh Anda apply(f), jika gtidak murni (dan applyjuga?) Maka semuanya tidak murni dan rusak tentu saja.
Bergi
14
Contohnya adalah Python dan ECMAScript, keduanya tidak benar-benar memiliki konsep inti "metode", sebaliknya Anda memiliki properti bernama yang mengembalikan objek fungsi kelas satu yang anonim dan kemudian Anda menyebutnya fungsi kelas satu yang anonim. Secara khusus, dalam kedua Python dan ECMAScript, mengatakan foo.bar()bukan satu operasi "memanggil metode barobjek foo" tetapi dua operasi, "bidang dereferensi barobjek foo dan kemudian memanggil objek yang dikembalikan dari bidang itu". Jadi, itu .adalah operator pemilih bidang dan ()merupakan operator panggilan dan mereka independen.
Jörg W Mittag
63

Memang, Scala memungkinkan ini, meskipun ada konvensi yang diikuti: jika metode ini memiliki efek samping, tanda kurung harus tetap digunakan.

Sebagai penulis kompiler, saya akan menemukan kehadiran kurung dijamin cukup nyaman; Saya akan selalu tahu bahwa itu adalah pemanggilan metode, dan saya tidak perlu membangun bifurkasi untuk kasus aneh.

Sebagai seorang programmer dan pembaca kode, keberadaan tanda kurung tidak diragukan lagi bahwa itu adalah pemanggilan metode, meskipun tidak ada parameter yang dilewati.

Melewati parameter bukan satu-satunya karakteristik pendefinisian pemanggilan metode. Mengapa saya memperlakukan metode tanpa parameter yang berbeda dari metode yang memiliki parameter?

Robert Harvey
sumber
Terima kasih, dan Anda memberikan alasan yang valid mengapa ini penting. Bisakah Anda juga memberikan beberapa contoh mengapa perancang bahasa harus menggunakan fungsi dalam prototipe?
David Refoua
kehadiran tanda kurung tidak diragukan lagi bahwa itu adalah panggilan metode yang saya setujui sepenuhnya. Saya lebih suka bahasa pemrograman saya lebih eksplisit daripada implisit.
Corey Ogburn
20
Scala sebenarnya sedikit lebih halus dari itu: sementara bahasa lain hanya mengizinkan satu daftar parameter dengan nol atau lebih parameter, Scala memungkinkan nol atau lebih daftar parameter dengan nol atau lebih parameter. Jadi, def foo()adalah metode dengan satu daftar parameter kosong, dan def foomerupakan metode tanpa daftar parameter dan keduanya adalah hal yang berbeda ! Secara umum, metode tanpa daftar parameter perlu dipanggil tanpa daftar argumen dan metode dengan daftar parameter kosong perlu dipanggil dengan daftar argumen kosong. Memanggil metode tanpa daftar parameter dengan argumen kosong sebenarnya ...
Jörg W Mittag
19
... diartikan sebagai memanggil applymetode objek yang dikembalikan oleh pemanggilan metode dengan daftar argumen kosong, sedangkan memanggil metode dengan daftar parameter kosong tanpa daftar argumen dapat bergantung pada konteks yang diartikan sebagai memanggil metode dengan daftar argumen kosong, η-ekspansi ke metode yang diterapkan sebagian (yaitu "referensi metode"), atau mungkin tidak dapat dikompilasi sama sekali. Juga, Scala mengikuti Prinsip Akses Seragam, dengan tidak (secara sintaksis) membedakan antara memanggil metode tanpa daftar argumen dan referensi bidang.
Jörg W Mittag
3
Bahkan ketika saya mulai menghargai Ruby karena tidak adanya "upacara" yang disyaratkan - orangtua, kurung kurawal, tipe eksplisit - saya dapat mengatakan bahwa metode-tanda kurung berbunyi lebih cepat ; tapi Ruby yang dulunya akrab akrab cukup mudah dibaca dan jelas lebih cepat untuk menulis.
radarbob
19

Ini sebenarnya adalah kebetulan pilihan sintaks yang cukup halus. Saya akan berbicara dengan bahasa fungsional, yang didasarkan pada kalkulus lambda yang diketik.

Dalam bahasa tersebut, setiap fungsi memiliki tepat satu argumen . Apa yang sering kita anggap sebagai "argumen berganda" sebenarnya adalah parameter tunggal dari jenis produk. Jadi misalnya, fungsi yang membandingkan dua bilangan bulat:

leq : int * int -> bool
leq (a, b) = a <= b

membutuhkan sepasang bilangan bulat. Tanda kurung tidak menunjukkan parameter fungsi; mereka digunakan untuk mencocokkan pola argumen. Untuk meyakinkan Anda bahwa ini benar-benar satu argumen, kita dapat menggunakan fungsi proyektif alih-alih pencocokan pola untuk mendekonstruksi pasangan:

leq some_pair = some_pair.1 <= some_pair.2

Dengan demikian, tanda kurung benar-benar kenyamanan yang memungkinkan kita untuk mencocokkan pola dan menyimpan beberapa pengetikan. Mereka tidak diharuskan.

Bagaimana dengan fungsi yang seolah-olah tidak memiliki argumen? Fungsi semacam itu sebenarnya memiliki domain Unit. Anggota tunggal Unitbiasanya ditulis sebagai (), jadi itu sebabnya tanda kurung muncul.

say_hi : Unit -> string
say_hi a = "Hi buddy!"

Untuk memanggil fungsi ini, kita harus menerapkannya ke nilai tipe Unit, yang harus (), jadi kita akhirnya menulis say_hi ().

Jadi, sebenarnya tidak ada daftar argumen!

kepala kebun
sumber
3
Dan itulah sebabnya kurung kosong ()menyerupai sendok dikurangi pegangannya.
Mindwin
3
Mendefinisikan leqfungsi yang menggunakan <daripada <=cukup membingungkan!
KRyan
5
Jawaban ini mungkin lebih mudah dipahami jika menggunakan kata "tuple" di samping frasa seperti "jenis produk."
Kevin
Kebanyakan formalisme akan memperlakukan intf (int x, int y) sebagai fungsi dari I ^ 2 hingga I. Kalkulus Lambda berbeda, itu tidak menggunakan metode ini untuk mencocokkan apa di formalisme lain adalah arity tinggi. Sebaliknya kalkulus lambda menggunakan kari. Perbandingannya adalah x -> (y -> (x <= y)). (x -> (y -> (x <= y)) 2 akan mengevaluasi ke (y-> 2 <= y) dan (x -> (y -> (x <= y)) 2 3 akan mengevaluasi ke ( y-> 2 <= y) 3 yang kemudian dievaluasi menjadi 2 <= 3.
Taemyr
1
@PeriataBreatta Saya tidak terbiasa dengan sejarah penggunaan ()untuk mewakili unit. Saya tidak akan terkejut jika setidaknya sebagian alasannya menyerupai "fungsi parameterless". Namun, ada penjelasan lain yang mungkin untuk sintaksis. Dalam aljabar jenis, Unitsebenarnya identitas untuk jenis produk. Produk biner ditulis sebagai (a,b), kita dapat menulis produk yang belum (a)jadi, jadi ekstensi logis adalah menulis produk nullary secara sederhana ().
gardenhead
14

Dalam Javascript misalnya menggunakan nama metode tanpa () mengembalikan fungsi itu sendiri tanpa menjalankannya. Dengan cara ini misalnya Anda dapat melewatkan fungsi sebagai argumen ke metode lain.

Di Jawa pilihan dibuat bahwa pengidentifikasi diikuti oleh () atau (...) berarti pemanggilan metode sementara pengidentifikasi tanpa () merujuk ke variabel anggota. Ini dapat meningkatkan keterbacaan, karena Anda tidak ragu apakah Anda berurusan dengan metode atau variabel anggota. Bahkan, nama yang sama dapat digunakan untuk metode dan variabel anggota, keduanya akan dapat diakses dengan sintaks masing-masing.

Florian F
sumber
4
+1 Selama kode diperlukan untuk dibaca oleh manusia, keterbacaan adalah raja.
Tulains Córdova
1
@ TulainsCórdova Saya tidak yakin perl setuju dengan Anda.
Racheet
@Racheet: Perl mungkin tetapi Perl Best Practicestidak
slebetman
1
@Racheet Perl sangat mudah dibaca jika ditulis agar dapat dibaca. Itu sama untuk hampir semua bahasa non-esoterik. Pendek, Perl singkat sangat mudah dibaca oleh programmer Perl, sama seperti kode Scala atau Haskell dapat dibaca oleh orang yang berpengalaman dalam bahasa tersebut. Saya juga bisa berbicara bahasa Jerman kepada Anda dengan cara yang sangat berbelit-belit yang secara tata bahasa sepenuhnya benar, tetapi Anda tetap tidak akan mengerti sepatah kata pun, bahkan jika Anda fasih berbahasa Jerman. Itu juga bukan kesalahan Jerman. ;)
simbabque
di java itu tidak "mengidentifikasi" suatu metode. Itu menyebutnya. Object::toStringmengidentifikasi suatu metode.
njzk2
10

Sintaks mengikuti semantik, jadi mari kita mulai dari semantik:

Apa cara menggunakan fungsi atau metode?

Sebenarnya ada beberapa cara:

  • suatu fungsi dapat dipanggil, dengan atau tanpa argumen
  • suatu fungsi dapat diperlakukan sebagai nilai
  • suatu fungsi dapat diterapkan sebagian (membuat penutupan mengambil setidaknya satu argumen lebih sedikit dan menutup argumen yang diteruskan)

Memiliki sintaksis yang serupa untuk kegunaan yang berbeda adalah cara terbaik untuk membuat bahasa yang ambigu atau setidaknya yang membingungkan (dan kami memiliki cukup banyak dari itu).

Dalam bahasa C, dan bahasa mirip C:

  • memanggil fungsi dilakukan dengan menggunakan tanda kurung untuk melampirkan argumen (mungkin tidak ada) seperti pada func()
  • memperlakukan fungsi sebagai nilai dapat dilakukan dengan hanya menggunakan namanya seperti dalam &func(C membuat pointer fungsi)
  • dalam beberapa bahasa, Anda memiliki sintaksis pendek untuk menerapkan sebagian; Java memungkinkan someVariable::someMethodmisalnya (terbatas pada penerima metode, tetapi masih berguna)

Perhatikan bagaimana setiap penggunaan memiliki sintaks yang berbeda, memungkinkan Anda membedakannya dengan mudah.

Matthieu M.
sumber
2
Ya, "dengan mudah" adalah salah satu dari situasi "jelas hanya jika sudah dipahami". Seorang pemula akan sepenuhnya dibenarkan dalam mencatat bahwa itu sama sekali tidak jelas tanpa penjelasan mengapa beberapa tanda baca memiliki makna yang dimilikinya.
Eric Lippert
2
@EricLippert: Memang, mudah tidak selalu berarti intuitif. Meskipun dalam kasus ini saya akan menunjukkan bahwa tanda kurung juga digunakan untuk fungsi "doa" dalam matematika. Yang sedang berkata, saya telah menemukan pemula dalam banyak bahasa berjuang lebih banyak dengan konsep / semantik daripada sintaksis secara umum.
Matthieu M.
Jawaban ini membingungkan perbedaan antara "currying" dan "aplikasi parsial". The ::Operator di Jawa adalah operator aplikasi parsial, bukan operator currying. Aplikasi parsial adalah operasi yang mengambil fungsi dan satu atau lebih nilai dan mengembalikan fungsi yang menerima nilai lebih sedikit. Currying hanya beroperasi pada fungsi, bukan nilai.
Periata Breatta
@PeriataBreatta: Poin bagus, sudah pasti.
Matthieu M.
8

Dalam bahasa dengan efek samping, IMO sangat membantu untuk membedakan antara pembacaan variabel (dalam efek samping teori)

variable

dan memanggil fungsi, yang mungkin menimbulkan efek samping

launch_nukes()

OTOH, jika tidak ada efek samping (selain yang dikodekan dalam sistem tipe) maka tidak ada titik untuk bahkan membedakan antara membaca variabel dan memanggil fungsi tanpa argumen.

Daniel Jour
sumber
1
Perhatikan bahwa OP menyajikan kasus di mana objek adalah satu-satunya argumen dan disajikan sebelum nama fungsi (yang juga menyediakan ruang lingkup untuk nama fungsi). noun.verbsintaksis bisa sejelas dalam arti noun.verb()dan sedikit kurang berantakan. Demikian pula, sebuah fungsi yang member_mengembalikan referensi kiri mungkin menawarkan sintaksis ramah daripada fungsi setter yang khas: obj.member_ = new_valuevs. obj.set_member(new_value)( _postfix adalah sebuah petunjuk yang mengatakan "terkena" mengingatkan _ awalan untuk fungsi "tersembunyi").
Paul A. Clayton
1
@ PaulA.Clayton Saya tidak melihat bagaimana ini tidak tercakup oleh jawaban saya: Jika ini noun.verbberarti pemanggilan fungsi, bagaimana Anda membedakannya dari akses anggota belaka?
Daniel Jour
1
Sehubungan dengan membaca variabel yang secara teori bebas dari efek samping, saya hanya ingin mengatakan ini: Selalu waspada terhadap teman-teman palsu .
Justin Time
8

Tidak ada jawaban lain yang mencoba menjawab pertanyaan: berapa banyak redundansi dalam desain suatu bahasa? Karena bahkan jika Anda dapat mendesain bahasa sehingga x = sqrt ymenetapkan x ke akar kuadrat dari y, itu tidak berarti Anda harus melakukannya.

Dalam bahasa tanpa redundansi, setiap urutan karakter berarti sesuatu, yang berarti bahwa jika Anda membuat kesalahan tunggal, Anda tidak akan mendapatkan pesan kesalahan, program Anda akan melakukan hal yang salah, yang mungkin sesuatu yang sangat berbeda dari apa yang Anda dimaksudkan dan sangat sulit untuk di-debug. (Seperti yang diketahui oleh siapa pun yang bekerja dengan ekspresi reguler.) Sedikit redundansi adalah hal yang baik karena memungkinkan banyak kesalahan Anda terdeteksi, dan semakin banyak redundansi, semakin besar kemungkinan diagnosis akan akurat. Sekarang tentu saja, Anda bisa mengambil terlalu jauh (tidak ada dari kita yang ingin menulis dalam COBOL hari ini), tetapi ada keseimbangan yang benar.

Redundansi juga membantu keterbacaan, karena ada lebih banyak petunjuk. Bahasa Inggris tanpa redundansi akan sangat sulit dibaca, dan hal yang sama berlaku untuk bahasa pemrograman.

Michael Kay
sumber
Saya setuju: bahasa pemrograman harus memiliki mekanisme "jaring pengaman". Tapi saya tidak setuju bahwa "sedikit redundansi" dalam sintaks adalah cara yang baik untuk melakukannya - itu adalah boilerplate , dan apa yang dilakukan kebanyakan adalah memperkenalkan noise yang membuat kesalahan lebih sulit dikenali, dan membuka kemungkinan kesalahan sintaks yang mengalihkan perhatian dari bug nyata. Jenis verbositas yang membantu keterbacaan tidak ada hubungannya dengan sintaksis, lebih pada konvensi penamaan . Dan jaring pengaman sejauh ini paling efisien diimplementasikan sebagai sistem tipe yang kuat , di mana sebagian besar perubahan acak akan membuat program salah ketik.
leftaroundabout
@leftaroundabout: Saya suka berpikir tentang keputusan desain bahasa dalam hal jarak Hamming. Jika dua konstruk memiliki makna yang berbeda, sering kali membantu untuk membuatnya berbeda setidaknya dalam dua cara, dan memiliki bentuk yang berbeda hanya dalam satu cara menghasilkan kompilator squawk. Perancang bahasa umumnya tidak dapat bertanggung jawab untuk memastikan bahwa pengidentifikasi mudah dibedakan, tetapi jika misalnya penugasan membutuhkan :=dan perbandingan ==, dengan =hanya dapat digunakan untuk inisialisasi waktu kompilasi, maka kemungkinan kesalahan pengubahan perbandingan menjadi penugasan akan sangat berkurang.
supercat
@leftaroundabout: Lagipula, jika saya mendesain bahasa, saya mungkin akan meminta ()untuk pemanggilan fungsi argumen-nol tetapi juga membutuhkan token untuk "mengambil alamat" dari suatu fungsi. Tindakan sebelumnya jauh lebih umum, jadi menyimpan token dalam kasus yang kurang umum tidak terlalu berguna.
supercat
@ supercat: well, sekali lagi saya pikir ini menyelesaikan masalah di tingkat yang salah. Tugas dan perbandingan masing-masing adalah operasi yang berbeda secara konseptual, demikian pula evaluasi fungsi dan pengambilan fungsi-alamat. Oleh karena itu mereka harus memiliki tipe yang jelas berbeda, maka tidak masalah lagi jika operator memiliki jarak Hamming yang rendah, karena kesalahan ketik apa pun akan menjadi kesalahan tipe kompilasi-waktu.
leftaroundabout
@leftaroundabout: Jika tugas sedang dibuat ke variabel tipe boolean, atau jika tipe pengembalian fungsi itu sendiri adalah pointer ke fungsi (atau - dalam beberapa bahasa - fungsi aktual), mengganti perbandingan dengan tugas, atau pemanggilan fungsi dengan referensi fungsi, dapat menghasilkan program yang secara sintaksis valid, tetapi memiliki makna yang sama sekali berbeda dari versi aslinya. Pengecekan tipe akan menemukan beberapa kesalahan seperti itu secara umum, tetapi membuat sintaks berbeda tampaknya seperti pendekatan yang lebih baik.
supercat
5

Dalam kebanyakan kasus, ini adalah pilihan sintaksis tata bahasa. Ini berguna untuk tata bahasa agar berbagai konstruksi individu menjadi (relatif) tidak ambigu ketika disatukan. (Jika ada ambiguitas, seperti dalam beberapa deklarasi C ++, harus ada aturan khusus untuk resolusi.) Kompiler tidak memiliki lintang untuk menebak; diperlukan untuk mengikuti spesifikasi bahasa.


Visual Basic, dalam berbagai bentuk, membedakan antara prosedur yang tidak mengembalikan nilai, dan fungsi.

Prosedur harus disebut sebagai pernyataan, dan tidak memerlukan paren, cukup argumen yang dipisahkan koma, jika ada. Fungsi harus dipanggil sebagai bagian dari ekspresi, dan memerlukan parens.

Ini adalah perbedaan yang relatif tidak perlu yang membuat refactoring manual antara kedua bentuk lebih menyakitkan daripada yang seharusnya.

(Di sisi lain Visual Basic menggunakan parens yang sama ()untuk referensi array seperti untuk panggilan fungsi, sehingga referensi array terlihat seperti panggilan fungsi. Dan ini memudahkan refactoring manual dari array menjadi panggilan fungsi! Jadi, kita bisa merenungkan bahasa lain menggunakan []'s untuk referensi array, tapi saya ngelantur ...)


Dalam bahasa C dan C ++ isi variabel secara otomatis diakses dengan menggunakan namanya, dan jika Anda ingin merujuk ke variabel itu sendiri alih-alih isinya, Anda menerapkan &operator unary .

Mekanisme semacam ini juga bisa diterapkan pada nama-nama fungsi. Nama fungsi mentah dapat menyiratkan panggilan fungsi, sedangkan &operator unary akan digunakan untuk merujuk ke fungsi (itu sendiri) sebagai data. Secara pribadi, saya suka ide mengakses fungsi no-argumen bebas efek samping dengan sintaks yang sama dengan variabel.

Ini sangat masuk akal (seperti pilihan sintaksis lainnya untuk ini).

Erik Eidt
sumber
2
Tentu saja, sementara tidak adanya tanda kurung visual basic pada panggilan prosedur adalah perbedaan yang tidak perlu dibandingkan dengan panggilan fungsinya, menambahkan tanda kurung yang diperlukan untuk mereka akan menjadi perbedaan yang tidak perlu antara pernyataan , banyak yang dalam bahasa yang diturunkan BASIC memiliki efek yang sangat mengingatkan pada prosedur. Ini juga sudah lama sejak saya melakukan pekerjaan dalam versi MS BASIC, tetapi apakah itu masih tidak memungkinkan sintaks call <procedure name> (<arguments>)? Cukup yakin itu adalah cara yang sah untuk menggunakan prosedur saat saya melakukan pemrograman QuickBASIC di masa yang lain ...
Periata Breatta
1
@PeriataBreatta: VB masih mengizinkan sintaks "panggilan", dan kadang-kadang mengharuskannya dalam kasus-kasus di mana hal yang dipanggil adalah ekspresi [misalnya orang dapat mengatakan "Panggil Jika (someCondition, Delegate1, Delegate2) (Arguments)", tetapi langsung doa hasil dari operator "Jika" tidak akan valid sebagai pernyataan mandiri].
supercat
@PeriataBreatta Saya suka VB6 tidak memanggil prosedur braket karena itu membuat kode seperti DSL terlihat bagus.
Mark Hurd
@supercat Di LinqPad, sungguh menakjubkan betapa sering saya perlu menggunakan Callmetode yang hampir tidak pernah saya perlukan dalam kode produksi "normal".
Mark Hurd
5

Konsistensi dan keterbacaan.

Jika saya belajar bahwa Anda memanggil fungsi Xseperti ini: X(arg1, arg2, ...), maka saya mengharapkan untuk bekerja dengan cara yang sama tanpa argumen: X().

Sekarang pada saat yang sama saya belajar bahwa Anda dapat mendefinisikan variabel sebagai beberapa simbol, dan menggunakannya seperti ini:

a = 5
b = a
...

Sekarang apa yang akan saya pikirkan ketika saya menemukan ini?

c = X

Dugaanmu sebaik milikku. Dalam keadaan normal, Xini adalah variabel. Tetapi jika kami mengambil jalan Anda, itu juga bisa menjadi fungsi! Apakah saya harus mengingat apakah simbol mana yang memetakan ke grup mana (variabel / fungsi)?

Kita dapat memaksakan batasan artifisial, misalnya "Fungsi dimulai dengan huruf kapital. Variabel mulai dengan huruf kecil", tetapi itu tidak perlu dan membuat segalanya lebih rumit, meskipun kadang-kadang mungkin membantu beberapa tujuan desain.

Catatan-samping 1: Jawaban lain sama sekali mengabaikan satu hal lagi: bahasa memungkinkan Anda menggunakan nama yang sama untuk fungsi dan variabel, dan membedakan berdasarkan konteks. Lihat Common Lispsebagai contoh. Fungsi xdan variabel xhidup berdampingan dengan sangat baik.

Side-catatan 2: Jawaban yang diterima menunjukkan sintaks: object.functionname. Pertama, itu tidak universal untuk bahasa dengan fungsi kelas satu. Kedua, sebagai seorang programmer saya akan memperlakukan ini sebagai informasi tambahan: functionnamemilik seorang object. Apakah objectobjek, kelas atau namespace tidak terlalu menjadi masalah, tapi itu memberitahu saya bahwa itu milik suatu tempat . Ini berarti Anda harus menambahkan sintaks buatan object.untuk setiap fungsi global atau membuat beberapa objectuntuk menahan semua fungsi global.

Dan bagaimanapun, Anda kehilangan kemampuan untuk memiliki ruang nama terpisah untuk fungsi dan variabel.

MatthewRock
sumber
Ya. Konsistensi adalah alasan yang cukup baik, berhenti penuh (bukan karena poin Anda yang lain tidak valid - itu adalah).
Matius Baca
4

Untuk menambah jawaban lain, ambil contoh C ini:

void *func_factory(void)
{
        return 0;
}

void *(*ff)(void);

void example()
{
        ff = func_factory;
        ff = func_factory();
}

Jika operator pemanggilan adalah opsional, tidak akan ada cara untuk membedakan antara penugasan fungsi dan pemanggilan fungsi.

Ini bahkan lebih bermasalah dalam bahasa yang tidak memiliki sistem tipe, misalnya JavaScript, di mana inferensi tipe tidak dapat digunakan untuk mencari tahu apa yang bisa dan tidak berfungsi.

Mark K Cowan
sumber
3
JavaScript tidak kekurangan sistem tipe. Tidak memiliki deklarasi tipe . Tetapi sepenuhnya menyadari perbedaan antara berbagai jenis nilai.
Periata Breatta
1
Periata Breatta: intinya adalah bahwa variabel dan properti Java mungkin berisi jenis nilai apa pun, jadi Anda tidak selalu dapat mengetahui apakah itu fungsi pada waktu membaca kode.
reinierpost
Misalnya, apa fungsi ini lakukan? function cross(a, b) { return a + b; }
Mark K Cowan
@MarkKCowan Ini sebagai berikut: ecma-international.org/ecma-262/6.0/…
curiousdannii