Mengapa operator yang ditentukan pengguna lebih umum?

94

Salah satu fitur yang saya lewatkan dari bahasa fungsional adalah gagasan bahwa operator hanyalah fungsi, jadi menambahkan operator khusus seringkali sesederhana menambahkan fungsi. Banyak bahasa prosedural memungkinkan operator kelebihan, jadi dalam beberapa hal operator masih berfungsi (ini sangat benar di D di mana operator dilewatkan sebagai string dalam parameter templat).

Tampaknya di mana operator kelebihan muatan diperbolehkan, seringkali sepele untuk menambahkan operator kustom tambahan. Saya menemukan posting blog ini , yang berpendapat bahwa operator kustom tidak bekerja dengan baik dengan notasi infiks karena aturan diutamakan, tetapi penulis memberikan beberapa solusi untuk masalah ini.

Saya melihat sekeliling dan tidak dapat menemukan bahasa prosedural yang mendukung operator khusus dalam bahasa tersebut. Ada peretasan (seperti makro di C ++), tapi itu hampir tidak sama dengan dukungan bahasa.

Karena fitur ini cukup sepele untuk diterapkan, mengapa tidak lebih umum?

Saya mengerti bahwa ini dapat menyebabkan beberapa kode jelek, tetapi itu tidak menghentikan desainer bahasa di masa lalu dari menambahkan fitur berguna yang dapat dengan mudah disalahgunakan (makro, operator ternary, pointer tidak aman).

Kasus penggunaan aktual:

  • Terapkan operator yang hilang (mis. Lua tidak memiliki operator bitwise)
  • Mimic D's ~(array concatenation)
  • DSL
  • Gunakan |sebagai gula sintaks gaya pipa Unix (menggunakan coroutine / generator)

Saya juga tertarik pada bahasa yang melakukan memungkinkan operator kustom, tapi aku lebih tertarik pada mengapa hal itu telah dikecualikan. Saya berpikir untuk membuat bahasa scripting untuk menambahkan operator yang ditentukan pengguna, tetapi berhenti sendiri ketika saya menyadari bahwa saya belum melihatnya di mana pun, jadi mungkin ada alasan bagus mengapa perancang bahasa lebih pintar daripada saya tidak mengizinkannya.

hajar
sumber
4
Ada diskusi Reddit tentang pertanyaan ini.
Dinamis
2
@dimatura: Saya tidak tahu banyak tentang R, tapi itu operator kustom masih tidak tampak sangat fleksibel (misalnya tidak ada cara yang tepat untuk menentukan fixity, ruang lingkup, kelebihan beban dll), yang menjelaskan mengapa mereka tidak banyak digunakan. Itu berbeda dalam bahasa lain, tentu saja dalam Haskell yang menggunakan operator infix kustom banyak sekali . Contoh lain dari bahasa prosedural dengan dukungan custom-infix yang masuk akal adalah Nimrod , dan tentu saja Perl juga mengizinkannya .
leftaroundabout
4
Ada pilihan lain, Lisp sebenarnya tidak memiliki perbedaan antara operator dan fungsi.
BeardedO
4
Tidak disebutkan tentang Prolog, bahasa lain di mana operator hanyalah gula sintaksis untuk fungsi (ya bahkan yang matematika) dan yang memungkinkan Anda untuk mendefinisikan operator kustom dengan prioritas khusus.
David Cowden
2
@BeddedO, tidak ada operator infiks di Lisp sama sekali. Setelah Anda memperkenalkannya, Anda harus berurusan dengan semua masalah dengan diutamakan dan semacamnya.
SK-logic

Jawaban:

134

Ada dua aliran pemikiran yang bertentangan secara diametris dalam desain bahasa pemrograman. Salah satunya adalah bahwa programmer menulis kode yang lebih baik dengan batasan lebih sedikit, dan yang lainnya adalah mereka menulis kode yang lebih baik dengan lebih banyak batasan. Menurut pendapat saya, kenyataannya adalah bahwa programmer berpengalaman yang baik berkembang dengan pembatasan lebih sedikit, tetapi pembatasan itu dapat menguntungkan kualitas kode pemula.

Operator yang ditentukan pengguna dapat membuat kode yang sangat elegan di tangan yang berpengalaman, dan kode yang benar-benar buruk oleh pemula. Jadi apakah bahasa Anda termasuk atau tidak tergantung pada sekolah pemikiran perancang bahasa Anda.

Karl Bielefeldt
sumber
23
Pada dua mazhab pemikiran, plus.google.com/110981030061712822816/posts/KaSKeg4vQtz juga memberi +1 karena memiliki jawaban paling langsung (dan mungkin paling benar) untuk pertanyaan mengapa perancang bahasa memilih satu vs yang lain. Saya juga akan mengatakan berdasarkan tesis Anda, Anda dapat memperkirakan bahwa bahasa yang lebih sedikit memungkinkan karena sebagian kecil pengembang sangat terampil daripada yang lain (persentase teratas dari apa pun akan selalu menjadi minoritas menurut definisi)
Jimmy Hoffa
12
Saya pikir jawaban ini tidak jelas dan hanya sedikit terkait dengan pertanyaan itu. (Saya juga kebetulan berpikir bahwa karakterisasi Anda salah karena bertentangan dengan pengalaman saya, tetapi itu tidak penting.)
Konrad Rudolph
1
Seperti @KonradRudolph katakan, ini tidak benar-benar menjawab pertanyaan. Ambil bahasa seperti Prolog yang memungkinkan Anda melakukan apa pun yang Anda inginkan dengan operator termasuk menentukan prioritasnya ketika menempatkan infiks atau awalan. Saya tidak berpikir fakta bahwa Anda dapat menulis operator kustom ada hubungannya dengan tingkat keterampilan audiens target, tetapi lebih karena tujuan Prolog adalah untuk membaca secara logis mungkin. Dimasukkannya operator kustom memungkinkan Anda untuk menulis program yang membaca sangat logis (setelah semua program prolog hanya sekelompok pernyataan logis).
David Cowden
Bagaimana saya merindukan kata-kata kasar itu? Oh man, bookmarked
George Mauer
Ke dalam filosofi mana saya akan jatuh jika saya berpikir bahwa programmer paling mungkin untuk menulis kode terbaik jika bahasa memberi mereka alat untuk mengatakan kapan kompiler harus membuat berbagai kesimpulan (misalnya argumen auto-tinju untuk suatu Formatmetode) dan kapan harus menolak ( misalnya argumen tinju otomatis ke ReferenceEquals). Semakin banyak kemampuan yang diberikan oleh pemrogram untuk mengatakan kapan kesimpulan tertentu tidak tepat, semakin aman dapat menawarkan kesimpulan yang nyaman bila perlu.
supercat
83

Diberi pilihan antara array array dengan ~ atau dengan "myArray.Concat (secondArray)", saya mungkin lebih suka yang terakhir. Mengapa? Karena ~ adalah karakter yang sama sekali tidak berarti yang hanya memiliki maknanya - yaitu array concatenation - yang diberikan dalam proyek spesifik di mana ia ditulis.

Pada dasarnya, seperti yang Anda katakan, operator tidak berbeda dengan metode. Tetapi sementara metode dapat diberikan nama-nama yang dapat dibaca dan dipahami yang menambah pemahaman tentang aliran kode, operator tidak jelas dan situasional.

Inilah sebabnya saya juga tidak suka .operator PHP (penggabungan string) atau sebagian besar operator di Haskell atau OCaml, meskipun dalam kasus ini, beberapa standar yang diterima secara universal muncul untuk bahasa fungsional.

Avner Shahar-Kashtan
sumber
27
Hal yang sama dapat dikatakan untuk semua operator. Apa yang membuat mereka bagus, dan juga dapat membuat operator yang ditetapkan pengguna menjadi baik (jika mereka didefinisikan dengan bijaksana), adalah ini: Meskipun karakter yang digunakan untuk mereka hanya karakter, penggunaan luas dan mungkin properti mnemonik membuat artinya dalam kode sumber segera jelas bagi mereka yang akrab dengannya. Jadi jawaban Anda sebagaimana adanya bukan IMHO yang meyakinkan.
33
Seperti yang saya sebutkan di bagian akhir, beberapa operator memiliki pengenalan universal (seperti simbol arithmentic standar, pergeseran bitwise, DAN / ATAU, dan sebagainya), dan keseragaman mereka melebihi kekeruhannya. Tetapi bisa mendefinisikan operator sewenang-wenang tampaknya menyimpan yang terburuk dari kedua dunia.
Avner Shahar-Kashtan
11
Jadi Anda juga mendukung pelarangan, misalnya, nama satu huruf di tingkat bahasa? Lebih umum, tidak membiarkan apa pun dalam bahasa yang tampaknya lebih berbahaya daripada kebaikan? Itu pendekatan yang valid untuk desain bahasa, tetapi bukan satu-satunya, jadi saya tidak berpikir Anda tidak dapat mengandaikannya.
9
Saya tidak setuju bahwa operator kustom "menambahkan" fitur, itu menghapus batasan. Operator umumnya hanya fungsi, jadi alih-alih tabel simbol statis untuk operator, yang dinamis, tergantung pada konteks dapat digunakan sebagai gantinya. Saya membayangkan ini adalah bagaimana kelebihan operator ditangani, karena +dan <<tentu saja tidak didefinisikan pada Object(saya mendapatkan "tidak cocok untuk operator + di ..." ketika melakukan itu pada kelas telanjang di C ++).
beatgammit
10
Yang perlu waktu lama untuk mencari tahu adalah bahwa pengkodean biasanya bukan tentang membuat komputer melakukan sesuatu untuk Anda, ini tentang membuat dokumen yang dapat dibaca oleh orang dan komputer, dengan orang yang sedikit lebih penting daripada komputer. Jawaban ini benar, Jika orang berikutnya (atau Anda dalam 2 tahun) harus menghabiskan waktu 10 detik untuk mencari tahu apa ~ ~ artinya, Anda mungkin juga menghabiskan 10 detik mengetikkan panggilan metode sebagai gantinya.
Bill K
71

Karena fitur ini cukup sepele untuk diterapkan, mengapa tidak lebih umum?

Premis Anda salah. Ini bukan "cukup sepele untuk diterapkan". Bahkan, ia membawa sekantong masalah.

Mari kita lihat "solusi" yang disarankan di pos:

  • Tidak ada prioritas . Penulis sendiri mengatakan, "Tidak menggunakan aturan prioritas bukanlah pilihan."
  • Parsing sadar semantik . Seperti artikel itu katakan, ini akan membutuhkan kompiler untuk memiliki banyak pengetahuan semantik. Artikel itu sebenarnya tidak menawarkan solusi untuk ini dan biarkan saya memberitahu Anda, ini tidak sepele. Kompiler dirancang sebagai pertukaran antara kekuatan dan kompleksitas. Secara khusus, penulis menyebutkan langkah pra-penguraian untuk mengumpulkan informasi yang relevan, tetapi pra-penguraian tidak efisien dan penyusun berusaha sangat keras untuk meminimalkan penguraian penguraian.
  • Tidak ada operator infix khusus . Ya, itu bukan solusi.
  • Solusi hibrida . Solusi ini membawa banyak (tetapi tidak semua) kelemahan parsing semantik sadar. Secara khusus, karena kompilator harus memperlakukan token yang tidak dikenal sebagai berpotensi mewakili operator kustom, sering tidak dapat menghasilkan pesan kesalahan yang bermakna. Ini juga mungkin memerlukan definisi dari operator tersebut untuk melanjutkan dengan penguraian (untuk mengumpulkan informasi jenis, dll.), Sekali lagi memerlukan penguraian tambahan penguraian.

Secara keseluruhan, ini adalah fitur yang mahal untuk diimplementasikan, baik dalam hal kompleksitas parser dan dalam hal kinerja, dan tidak jelas bahwa itu akan membawa banyak manfaat. Tentu, ada beberapa manfaat untuk kemampuan mendefinisikan operator baru tetapi bahkan mereka yang suka bertengkar (lihat saja jawaban lain dengan alasan bahwa memiliki operator baru bukanlah hal yang baik).

Konrad Rudolph
sumber
2
Terima kasih untuk suara kewarasannya. Pengalaman saya menunjukkan bahwa orang-orang yang menganggap hal-hal sepele adalah ahli atau bodoh, dan lebih sering dalam kategori yang terakhir: x
Matthieu M.
14
setiap satu dari masalah ini telah diselesaikan dalam bahasa yang telah ada selama 20 tahun ...
Philip JF
11
Namun, ini sangat sepele untuk diterapkan. Lupakan semua algoritma penguraian buku naga itu, ini adalah abad ke-21 dan ini saatnya untuk melanjutkan. Bahkan memperluas sintaksis bahasa itu sendiri itu mudah, dan menambahkan operator dari prioritas yang diberikan adalah sepele. Lihatlah, katakanlah, parser Haskell. Ini jauh, jauh lebih sederhana daripada parser untuk bahasa "mainstream" kembung.
SK-logic
3
@ SK-logic Pernyataan selimut Anda tidak meyakinkan saya. Ya, parsing telah pindah. Tidak, menerapkan prioritas operator sewenang-wenang tergantung pada jenisnya bukan “sepele”. Menghasilkan pesan kesalahan yang baik dengan konteks terbatas bukan "sepele". Memproduksi parser yang efisien dengan bahasa yang membutuhkan banyak lintasan untuk mentransformasikannya tidak layak
Konrad Rudolph
6
Di Haskell, parsing itu "mudah" (dibandingkan dengan kebanyakan bahasa). Diutamakan adalah konteks independen. Bagian yang memberi Anda pesan galat terkait dengan fitur sistem ketik dan tipe lanjutan, bukan ke operator yang ditentukan pengguna. Ini kelebihan beban, bukan definisi pengguna, itulah masalah yang sulit.
Philip JF
25

Mari kita abaikan seluruh argumen "operator disalahgunakan untuk merusak keterbacaan", dan fokus pada implikasi desain bahasa.

Operator infix memiliki lebih banyak masalah daripada aturan presedensi sederhana (meskipun untuk menjadi tumpul, tautan yang Anda referensi meremehkan dampak dari keputusan desain itu). Salah satunya adalah resolusi konflik: apa yang terjadi ketika Anda mendefinisikan a.operator+(b)dan b.operator+(a)? Memilih satu di atas yang lain mengarah pada melanggar properti komutatif yang diharapkan dari operator itu. Melempar kesalahan dapat menyebabkan modul yang bisa berfungsi menjadi rusak sekali bersama-sama. Apa yang terjadi ketika Anda mulai melemparkan jenis turunan ke dalam campuran?

Faktanya adalah bahwa operator tidak hanya berfungsi. Fungsi berdiri sendiri atau dimiliki oleh kelasnya, yang memberikan preferensi yang jelas pada parameter mana (jika ada) yang memiliki pengiriman polimorfik.

Dan itu mengabaikan berbagai masalah pengemasan dan resolusi yang muncul dari operator. Alasan perancang bahasa (pada umumnya) membatasi definisi operator infiks adalah karena ia menciptakan setumpuk masalah untuk bahasa sambil memberikan manfaat yang dapat diperdebatkan.

Dan terus terang, karena mereka tidak sepele untuk diterapkan.

Telastyn
sumber
1
Saya percaya ini adalah alasan mengapa Java tidak memasukkannya, tetapi bahasa yang memilikinya memiliki aturan yang jelas untuk diutamakan. Saya pikir ini mirip dengan perkalian matriks. Itu tidak komutatif, jadi Anda harus waspada ketika Anda menggunakan matriks. Saya pikir hal yang sama berlaku ketika berhadapan dengan kelas, tapi ya, saya setuju bahwa memiliki non-komutatif +adalah kejahatan. Tetapi apakah ini benar-benar argumen terhadap operator yang ditentukan pengguna? Sepertinya argumen terhadap operator overloading secara umum.
beatgammit
1
@tjameson - Ya, bahasa memang memiliki aturan yang jelas untuk diutamakan, untuk matematika . Aturan yang diutamakan untuk operasi matematika kemungkinan tidak akan benar-benar berlaku jika operator kelebihan beban untuk hal-hal seperti boost::spirit. Segera setelah Anda mengizinkan operator yang ditetapkan pengguna yang menjadi lebih buruk karena tidak ada cara yang baik untuk bahkan menentukan prioritas matematika. Saya telah menulis sedikit tentang hal itu sendiri dalam konteks bahasa yang secara khusus terlihat untuk mengatasi masalah dengan operator yang ditentukan secara sewenang-wenang.
Telastyn
22
Saya sebut omong kosong, Pak. Ada kehidupan di luar OO-ghetto, dan fungsi di sana tidak harus dimiliki oleh objek apa pun , oleh karena itu menemukan fungsi yang tepat persis sama dengan menemukan operator yang tepat.
Matthieu M.
1
@matthieuM. - tentu saja Aturan kepemilikan dan pengiriman lebih seragam dalam bahasa yang tidak mendukung fungsi anggota. Namun, pada titik awal, pertanyaan awal menjadi 'mengapa bahasa non-OO lebih umum?' yang merupakan ballgame lainnya.
Telastyn
11
Pernahkah Anda melihat bagaimana Haskell melakukan operator kustom? Mereka bekerja persis seperti fungsi normal, kecuali mereka juga memiliki prioritas yang terkait. (Faktanya, begitu juga fungsi-fungsi normal, bahkan meskipun tidak ada perbedaan). Pada dasarnya, operator digabungkan secara default dan nama-nama adalah awalan, tetapi itulah satu-satunya perbedaan.
Tikhon Jelvis
19

Saya pikir Anda akan terkejut seberapa sering operator overloads yang diimplementasikan dalam beberapa bentuk. Tetapi mereka tidak umum digunakan di banyak komunitas.

Mengapa menggunakan ~ untuk menyatukan ke array? Mengapa tidak menggunakan << seperti yang dilakukan Ruby ? Karena programmer yang bekerja dengan Anda mungkin bukan programmer Ruby. Atau programmer D. Jadi apa yang mereka lakukan ketika mereka menemukan kode Anda? Mereka harus pergi dan mencari apa arti simbol itu.

Saya dulu bekerja dengan pengembang C # yang sangat baik yang juga memiliki selera bahasa fungsional. Tiba-tiba, ia mulai memperkenalkan monad ke C # dengan cara metode ekstensi dan menggunakan terminologi monad standar. Tidak ada yang bisa membantah bahwa beberapa kodenya lebih singkat dan bahkan lebih mudah dibaca setelah Anda tahu artinya, tetapi itu berarti bahwa setiap orang harus mempelajari terminologi monad sebelum kodenya masuk akal .

Cukup adil, menurut Anda? Itu hanya tim kecil. Secara pribadi, saya tidak setuju. Setiap pengembang baru ditakdirkan untuk bingung dengan terminologi ini. Apakah kita tidak memiliki cukup masalah dalam mempelajari domain baru?

Di sisi lain, saya dengan senang hati akan menggunakan satu ??operator yang di C # karena saya mengharapkan lain C # pengembang untuk tahu apa itu, tapi aku tidak akan membebani menjadi bahasa yang tidak mendukung secara default.

pdr
sumber
Saya tidak mengerti contoh Double.NaN Anda, perilaku ini adalah bagian dari spesifikasi floating point dan didukung dalam setiap bahasa yang saya gunakan. Apakah Anda mengatakan bahwa operator khusus tidak didukung karena akan membingungkan pengembang yang tidak tahu tentang fitur ini? Itu terdengar seperti argumen yang sama terhadap penggunaan operator ternary atau ??contoh Anda .
beatgammit
@jameson: Anda benar, sebenarnya, itu agak singgung sampai saya coba buat, dan tidak terlalu ditulis dengan baik. Saya memikirkan ?? contoh ketika saya sedang menulis itu dan lebih suka contoh itu. Menghapus paragraf double.NaN.
pdr
6
Saya pikir Anda "setiap pengembang baru ditakdirkan untuk menjadi bingung dengan terminologi ini" titik agak loyo, khawatir tentang kurva pembelajaran pengembang baru ke basis kode Anda bagi saya sama seperti khawatir tentang kurva belajar pengembang baru ke versi baru .NET. Saya pikir yang lebih penting adalah ketika devs mempelajarinya (.NET baru atau basis kode Anda), apakah masuk akal dan menunjukkan nilai? Jika demikian, kurva belajar tidak sia-sia, karena setiap pengembang hanya perlu mempelajarinya sekali saja, meskipun kodenya perlu ditangani berulang kali, jadi jika itu meningkatkan kodenya itu adalah biaya yang kecil.
Jimmy Hoffa
7
@JimmyHoffa Ini adalah biaya berulang. Dibayar dimuka untuk setiap pengembang baru dan juga mempengaruhi pengembang yang ada karena mereka harus mendukungnya. Ada biaya dokumentasi juga dan elemen risiko - rasanya cukup aman hari ini, tetapi 30 tahun kemudian, semua orang akan pindah, bahasa dan aplikasi sekarang "warisan", dokumentasi adalah tumpukan besar yang mengepul dan beberapa pengisap miskin akan dibiarkan merobek rambutnya di kecemerlangan programmer yang terlalu malas untuk mengetik ".concat ()". Apakah nilai yang diharapkan cukup untuk mengimbangi biaya?
Sylverdrag
3
Juga, argumen "Setiap pengembang baru ditakdirkan untuk bingung dengan terminologi ini. Apakah kita tidak memiliki cukup masalah dalam mempelajari domain baru?" bisa diterapkan ke OOP kembali di tahun 80-an, pemrograman terstruktur sebelum itu, atau IOC 10 tahun yang lalu. Sudah waktunya untuk berhenti menggunakan argumen keliru ini.
Mauricio Scheffer
11

Saya dapat memikirkan beberapa alasan:

  • Mereka tidak sepele untuk diterapkan - memungkinkan operator kustom yang sewenang-wenang dapat membuat kompiler Anda jauh lebih kompleks, terutama jika Anda mengizinkan aturan prioritas, perbaikan, dan pengaturan yang ditentukan pengguna. Jika kesederhanaan adalah suatu kebajikan, maka overloading operator membawa Anda jauh dari desain bahasa yang baik.
  • Mereka disalahgunakan - kebanyakan oleh coders yang menganggap "keren" untuk mendefinisikan ulang operator dan mulai mendefinisikan ulang mereka untuk semua jenis kelas khusus. Tidak lama kemudian, kode Anda dipenuhi dengan simbol yang disesuaikan yang tidak dapat dibaca atau dipahami orang lain karena operator tidak mengikuti aturan konvensional yang dipahami dengan baik. Saya tidak membeli argumen "DSL", kecuali DSL Anda kebetulan merupakan bagian dari matematika :-)
  • Mereka merusak keterbacaan dan pemeliharaan - jika operator diganti secara teratur, bisa menjadi sulit dikenali ketika fasilitas ini digunakan, dan coders dipaksa untuk terus bertanya pada diri sendiri apa yang sedang dilakukan operator. Adalah jauh lebih baik untuk memberikan nama fungsi yang bermakna. Mengetik beberapa karakter tambahan itu murah, masalah perawatan jangka panjangnya mahal.
  • Mereka dapat mematahkan harapan kinerja implisit . Sebagai contoh, saya biasanya mengharapkan pencarian elemen dalam array menjadi O(1). Tetapi dengan overloading operator, someobject[i]bisa dengan mudah menjadi O(n)operasi tergantung pada implementasi operator pengindeksan.

Pada kenyataannya, ada sangat sedikit kasus di mana kelebihan beban operator memiliki penggunaan yang dapat dibenarkan dibandingkan dengan hanya menggunakan fungsi biasa. Contoh yang sah mungkin merancang kelas bilangan kompleks untuk digunakan oleh matematikawan, yang memahami cara-cara yang dipahami dengan baik bahwa operator matematika didefinisikan untuk bilangan kompleks. Tapi ini sebenarnya bukan kasus yang sangat umum.

Beberapa kasus menarik untuk dipertimbangkan:

  • Lisps : secara umum tidak membedakan sama sekali antara operator dan fungsi - +hanya fungsi biasa. Anda dapat menentukan fungsi sesuka Anda (biasanya ada cara mendefinisikannya dalam ruang nama terpisah untuk menghindari konflik dengan bawaan +), termasuk operator. Tetapi ada kecenderungan budaya untuk menggunakan nama fungsi yang bermakna, jadi ini tidak banyak disalahgunakan. Selain itu, dalam notasi awalan Lisp cenderung digunakan secara eksklusif, sehingga nilai "sintaksis gula" yang disediakan operator lebih sedikit.
  • Java - melarang overloading operator. Ini kadang-kadang menjengkelkan (untuk hal-hal seperti kasus nomor Kompleks) tetapi rata-rata itu mungkin keputusan desain yang tepat untuk Jawa yang dimaksudkan sebagai bahasa sederhana, tujuan umum OOP. Kode Java sebenarnya cukup mudah untuk dipelihara oleh pengembang berkemampuan rendah / menengah sebagai akibat dari kesederhanaan ini.
  • C ++ memiliki overloading operator yang sangat canggih. Kadang-kadang ini disalahgunakan ( cout << "Hello World!"siapa pun?) Tetapi pendekatan itu masuk akal mengingat posisi C ++ sebagai bahasa kompleks yang memungkinkan pemrograman tingkat tinggi sambil tetap memungkinkan Anda untuk menjadi sangat dekat dengan logam untuk kinerja, sehingga Anda dapat misalnya menulis kelas bilangan kompleks yang berperilaku persis seperti yang Anda inginkan tanpa mengorbankan kinerja. Dipahami bahwa itu adalah tanggung jawab Anda sendiri jika Anda menembak diri sendiri.
mikera
sumber
8

Karena fitur ini cukup sepele untuk diterapkan, mengapa tidak lebih umum?

Itu tidak sepele untuk diterapkan (kecuali diterapkan secara sepele). Ini juga tidak membuat Anda banyak, bahkan jika diimplementasikan secara ideal: keuntungan keterbacaan dari kesederhanaan diimbangi oleh kerugian keterbacaan dari ketidaktahuan dan opacity. Singkatnya, ini tidak biasa karena biasanya tidak sepadan dengan waktu pengembang atau pengguna.

Yang mengatakan, saya dapat memikirkan tiga bahasa yang melakukannya, dan mereka melakukannya dengan cara yang berbeda:

  • Racket, sebuah skema, ketika itu tidak semua S-ekspresi-y memungkinkan dan mengharapkan Anda untuk menulis berapa jumlah untuk parser untuk setiap sintaks yang ingin Anda perpanjang (dan memberikan kait yang berguna untuk membuat traktable ini).
  • Haskell, bahasa pemrograman yang berfungsi murni, memungkinkan mendefinisikan setiap operator yang hanya terdiri dari tanda baca, dan memungkinkan Anda untuk menyediakan tingkat perbaikan (10 tersedia) dan suatu asosiatif. Ternary dll. Operator dapat dibuat dari operator biner dan fungsi urutan yang lebih tinggi.
  • Agda, bahasa pemrograman yang diketik secara dependen, sangat fleksibel dengan operator (makalah di sini ) yang memungkinkan baik if-then dan if-then-else didefinisikan sebagai operator dalam program yang sama, tetapi lexer, parser, dan evaluator semuanya sangat berpasangan hasil dari.
Alex R
sumber
4
Anda telah mengatakannya "tidak sepele", dan segera mendaftarkan tiga bahasa yang menampilkan beberapa implementasi sepele yang terlampau rumit. Jadi, ini agak sepele, kan?
SK-logic
7

Salah satu alasan utama operator kustom tidak disarankan adalah karena setiap operator dapat berarti / dapat melakukan apa saja.

Misalnya cstreambanyak shift kiri yang terlalu banyak dikritik.

Ketika suatu bahasa memungkinkan operator kelebihan beban, umumnya ada dorongan untuk menjaga perilaku operator serupa dengan perilaku dasar untuk menghindari kebingungan.

Juga operator yang ditentukan pengguna membuat penguraian jauh lebih sulit, terutama ketika ada juga aturan preferensi khusus.

ratchet freak
sumber
6
Saya tidak melihat bagaimana bagian-bagian tentang overloading operator berlaku untuk mendefinisikan operator yang sama sekali baru.
Saya telah melihat penggunaan operator yang berlebihan (pustaka aljabar linear) yang sangat baik dan sangat buruk seperti yang Anda sebutkan. Saya rasa ini bukan argumen yang bagus untuk menentang operator khusus. Penguraian mungkin merupakan masalah, tetapi cukup jelas ketika operator diharapkan. Setelah parsing, terserah pembuat kode untuk mencari makna operator.
beatgammit
3
Dan bagaimana ini berbeda dari metode overloading?
Jörg W Mittag
@ JörgWMittag dengan metode overload ada (sebagian besar waktu) nama yang berarti terlampir. dengan simbol lebih sulit untuk menjelaskan secara ringkas apa yang akan terjadi
ratchet freak
1
@ scratchetfreak: Baiklah. +tambahkan dua hal, -kurangi, *gandakan. Perasaan saya adalah bahwa tidak ada yang memaksa programmer untuk membuat fungsi / metode addbenar-benar menambahkan apa pun dan doNothingdapat meluncurkan nuklir. Dan a.plus(b.minus(c.times(d)).times(e)itu jauh lebih mudah dibaca a + (b - c * d) * e(bonus tambahan - di mana sengatan pertama adalah kesalahan dalam transkripsi). Saya tidak melihat bagaimana yang pertama lebih bermakna ...
Maciej Piechotka
4

Kami tidak menggunakan operator yang ditentukan pengguna karena alasan yang sama kami tidak menggunakan kata-kata yang ditentukan pengguna. Tidak ada yang akan menyebut fungsi mereka "sworp". Satu-satunya cara untuk menyampaikan pemikiran Anda kepada orang lain adalah menggunakan bahasa bersama. Dan itu berarti kata-kata dan tanda (operator) harus diketahui oleh masyarakat untuk siapa Anda menulis kode Anda.

Oleh karena itu operator yang Anda lihat digunakan dalam bahasa pemrograman adalah yang telah kami ajarkan di sekolah (aritmatika) atau yang telah ditetapkan dalam komunitas pemrograman, seperti operator boolean.

Vagif Verdi
sumber
1
Tambahkan ke bahwa kemampuan untuk menemukan makna di balik suatu fungsi. Dalam kasus "sworp", Anda dapat dengan mudah memasukkannya ke kotak pencarian dan menemukan dokumentasinya. Menempel operator ke mesin pencari adalah ball park yang sangat berbeda. Mesin pencari kami saat ini cukup payah dalam menemukan operator, dan saya telah membuang banyak waktu untuk mencari artinya dalam beberapa kasus.
Mark H
1
Berapa "sekolah" yang Anda hitung? Sebagai contoh, saya pikir ∈ elemadalah ide bagus dan tentu saja semua operator harus mengerti, tetapi yang lain tampaknya tidak setuju.
Tikhon Jelvis
1
Bukan masalah dengan simbol. Ini masalah dengan keyboard. Saya misalnya menggunakan emacs haskell-mode dengan unicode beautifier diaktifkan. Dan secara otomatis mengkonversi operator ascii ke simbol unicode. Tapi saya tidak akan bisa mengetiknya jika tidak akan ada ascii yang setara.
Vagif Verdi
2
Bahasa alami dibangun hanya dari "kata yang ditentukan pengguna" dan tidak ada yang lain. Dan beberapa bahasa sebenarnya mendorong pembentukan kata kustom.
SK-logic
4

Adapun bahasa yang mendukung overloading seperti itu: Scala melakukannya, sebenarnya dengan cara yang jauh lebih bersih dan lebih baik C ++. Sebagian besar karakter dapat digunakan dalam nama fungsi, sehingga Anda dapat mendefinisikan operator seperti! + * = ++, jika Anda mau. Ada dukungan bawaan untuk infix (untuk semua fungsi yang mengambil satu argumen). Saya pikir Anda dapat menentukan asosiatif fungsi tersebut juga. Namun Anda tidak dapat menentukan prioritas (hanya dengan trik jelek, lihat di sini ).

kutschkem
sumber
4

Satu hal yang belum disebutkan adalah kasus Smalltalk, di mana semuanya (termasuk operator) mengirim pesan. "Operator" suka +, |dan sebagainya sebenarnya metode unary.

Semua metode dapat diganti, jadi a + bberarti penambahan bilangan bulat jika adan bkeduanya bilangan bulat, dan berarti penambahan vektor jika keduanya OrderedCollections.

Tidak ada aturan prioritas, karena ini hanya pemanggilan metode. Ini memiliki implikasi penting untuk notasi matematika standar: 3 + 4 * 5berarti (3 + 4) * 5, tidak 3 + (4 * 5).

(Ini adalah batu sandungan utama bagi para pemula Smalltalk. Melanggar aturan matematika menghilangkan kasus khusus, sehingga semua evaluasi kode berlangsung seragam dari kiri ke kanan, membuat bahasa yang jauh lebih sederhana.)

Frank Shearar
sumber
3

Anda berjuang melawan dua hal di sini:

  1. Mengapa operator ada dalam bahasa pada awalnya?
  2. Apa keutamaan operator atas fungsi / metode?

Dalam sebagian besar bahasa, operator tidak benar-benar diimplementasikan sebagai fungsi sederhana. Mereka mungkin memiliki beberapa fungsi scaffolding, tetapi compiler / runtime secara eksplisit menyadari makna semantik mereka dan bagaimana menerjemahkannya secara efisien ke dalam kode mesin. Ini jauh lebih benar bahkan dibandingkan dengan fungsi built-in (itulah sebabnya sebagian besar implementasi juga tidak menyertakan semua overhead panggilan fungsi dalam implementasinya). Sebagian besar operator adalah abstraksi level yang lebih tinggi pada instruksi primitif yang ditemukan dalam CPU (yang sebagian alasan mengapa sebagian besar operator adalah aritmatika, boolean, atau bitwise). Anda dapat memodelkannya sebagai fungsi "khusus" (menyebutnya "primitif" atau "builtin" atau "asli" atau apa pun), tetapi untuk melakukan itu secara umum memerlukan serangkaian semantik yang sangat kuat untuk mendefinisikan fungsi khusus tersebut. Alternatifnya adalah memiliki operator built-in yang secara semantik terlihat seperti operator yang ditentukan pengguna, tetapi sebaliknya memanggil jalur khusus dalam kompiler. Itu bertentangan dengan jawaban untuk pertanyaan kedua ...

Selain masalah terjemahan mesin yang saya sebutkan di atas, pada level sintaksis operator tidak terlalu berbeda dari fungsi. Mereka membedakan karakteristik cenderung bahwa mereka singkat dan simbolis, yang mengisyaratkan karakteristik tambahan signifikan mereka harus berguna: mereka harus memiliki makna yang luas dipahami / semantik untuk pengembang. Simbol-simbol pendek tidak membawa banyak makna kecuali jika itu adalah tangan pendek untuk seperangkat semantik yang sudah dipahami. Itu membuat operator yang ditetapkan pengguna secara inheren tidak membantu, karena sifatnya mereka tidak begitu dipahami secara luas. Mereka masuk akal sebanyak satu atau dua nama fungsi huruf.

Kelebihan operator C ++ menyediakan lahan subur untuk memeriksa hal ini. Sebagian besar "penyalahgunaan" operator kelebihan datang dalam bentuk kelebihan yang mematahkan beberapa kontrak semantik yang dipahami secara luas (contoh klasik adalah kelebihan operator + sehingga a + b! = B + a, atau di mana + memodifikasi salah satu dari nya operan).

Jika Anda melihat Smalltalk, yang memungkinkan operator overloading dan operator yang ditentukan pengguna, Anda dapat melihat bagaimana suatu bahasa bisa melakukannya, dan seberapa bermanfaatnya. Dalam Smalltalk, operator hanyalah metode dengan sifat sintaksis yang berbeda (yaitu, mereka dikodekan sebagai infix binary). Bahasa ini menggunakan "metode primitif" untuk operator dan metode khusus yang dipercepat. Anda menemukan bahwa sedikit jika ada operator yang ditentukan pengguna dibuat, dan ketika mereka, mereka cenderung tidak terbiasa sebanyak penulis mungkin dimaksudkan untuk mereka untuk digunakan. Bahkan ekivalen dari kelebihan operator jarang terjadi, karena sebagian besar merupakan kerugian bersih untuk mendefinisikan fungsi baru sebagai operator alih-alih metode, karena yang terakhir memungkinkan untuk ekspresi semantik fungsi.

Christopher Smith
sumber
1

Saya selalu menemukan kelebihan operator di C ++ menjadi jalan pintas yang nyaman untuk tim pengembang tunggal, tetapi yang menyebabkan semua jenis kebingungan dalam jangka panjang hanya karena panggilan metode sedang "disembunyikan" dengan cara yang tidak mudah. untuk alat seperti doxygen untuk dipisahkan, dan orang-orang perlu memahami idiom untuk memanfaatkannya dengan benar.

Kadang-kadang jauh lebih sulit untuk dimengerti daripada yang Anda harapkan, bahkan. Sekali waktu, dalam proyek C ++ lintas-platform besar saya memutuskan itu akan menjadi ide yang baik untuk menormalkan cara jalur dibangun dengan membuat FilePathobjek (mirip dengan objek Java File), yang akan memiliki operator / digunakan untuk menggabungkan yang lain bagian jalan di atasnya (sehingga Anda dapat melakukan sesuatu seperti File::getHomeDir()/"foo"/"bar"dan itu akan melakukan hal yang benar pada semua platform yang didukung kami). Setiap orang yang melihatnya pada dasarnya akan berkata, "Apa-apaan ini? Pembagian tali? ... Oh, itu lucu, tapi saya tidak percaya untuk melakukan hal yang benar."

Demikian pula, ada banyak kasus dalam pemrograman grafis atau bidang lain di mana matematika vektor / matriks banyak terjadi di mana ia tergoda untuk melakukan hal-hal seperti Matriks * Matriks, Vektor * Vektor (titik), Vektor% Vektor (lintas), Matriks * Vektor ( matrix transform), Matrix ^ Vector (transformasi matriks kasus khusus mengabaikan koordinat homogen - berguna untuk normals permukaan), dan seterusnya, tetapi sementara itu menghemat sedikit waktu penguraian untuk orang yang menulis perpustakaan matematika vektor, itu hanya berakhir Facebook membingungkan masalah lebih untuk orang lain. Itu tidak layak.

halus
sumber
0

Kelebihan operator adalah ide yang buruk karena alasan yang sama bahwa kelebihan metode adalah ide yang buruk: simbol yang sama pada layar akan memiliki arti yang berbeda tergantung pada apa yang ada di sekitarnya. Ini membuatnya lebih sulit untuk membaca santai.

Karena keterbacaan adalah aspek penting dari pemeliharaan, Anda harus selalu menghindari kelebihan muatan (kecuali dalam beberapa kasus yang sangat khusus). Jauh lebih baik bagi setiap simbol (apakah operator atau pengidentifikasi alfanumerik) untuk memiliki makna unik yang berdiri sendiri.

Sebagai ilustrasi: saat membaca kode asing, jika Anda menemukan pengidentifikasi alfanumerik baru yang tidak Anda ketahui, setidaknya Anda memiliki keuntungan yang Anda tahu tidak tahu.. Anda kemudian dapat mencarinya. Namun, jika Anda melihat pengidentifikasi umum atau operator yang Anda tahu artinya, Anda jauh lebih kecil kemungkinannya untuk menyadari bahwa itu sebenarnya telah kelebihan beban untuk memiliki makna yang sama sekali berbeda. Untuk mengetahui operator apa yang kelebihan beban (dalam basis kode yang membuat penggunaan berlebihan kelebihan muatan), Anda akan membutuhkan pengetahuan tentang kode yang lengkap, bahkan jika Anda hanya ingin membaca sebagian kecil darinya. Ini akan menyulitkan pengembang baru untuk mempercepat kode itu, dan tidak mungkin membawa orang untuk pekerjaan kecil. Ini mungkin baik untuk keamanan pekerjaan programmer, tetapi jika Anda bertanggung jawab atas keberhasilan basis kode, Anda harus menghindari praktik ini dengan cara apa pun.

Karena operator berukuran kecil, operator yang kelebihan muatan akan memungkinkan lebih banyak kode padat, tetapi membuat kode padat bukan manfaat nyata. Satu baris dengan logika dua kali lebih lama untuk dibaca. Kompiler tidak peduli. Satu-satunya masalah adalah keterbacaan manusia. Karena membuat kode yang ringkas tidak meningkatkan keterbacaan, tidak ada manfaat nyata untuk kekompakan. Silakan mengambil ruang, dan memberikan operasi unik pengidentifikasi unik, dan kode Anda akan lebih sukses dalam jangka panjang.

AgilePro
sumber
"simbol yang sama pada layar akan memiliki arti yang berbeda tergantung pada apa yang ada di sekitarnya" - itu sudah menjadi kasus bagi banyak operator di sebagian besar bahasa.
rkj
-1

Kesulitan teknis untuk menangani prioritas dan penguraian kompleks dikesampingkan, saya pikir ada beberapa aspek dari apa bahasa pemrograman yang harus dipertimbangkan.

Operator biasanya adalah konstruksi logis pendek yang didefinisikan dengan baik dan didokumentasikan dalam bahasa inti (bandingkan, tetapkan ..). Mereka juga biasanya sulit dimengerti tanpa dokumentasi (bandingkan a^bdengan xor(a,b)misalnya). Ada sejumlah operator yang agak terbatas yang sebenarnya mungkin masuk akal dalam pemrograman normal (>, <, =, + dll.).

Gagasan saya adalah lebih baik tetap menggunakan sekumpulan operator yang terdefinisi dengan baik dalam suatu bahasa - kemudian mengizinkan operator kelebihan dari operator tersebut (diberi rekomendasi lembut bahwa operator harus melakukan hal yang sama, tetapi dengan tipe data khusus).

Kasus penggunaan Anda ~dan |akan benar-benar dimungkinkan dengan overloading operator sederhana (C #, C ++, dll.). DSL adalah area penggunaan yang valid, tetapi mungkin satu-satunya area yang valid (dari sudut pandang saya). Namun, saya berpikir bahwa ada alat yang lebih baik untuk membuat bahasa baru di dalamnya. Menjalankan bahasa DSL sejati dalam bahasa lain tidak sulit dengan menggunakan alat kompiler-kompiler mana pun. Hal yang sama berlaku untuk "memperpanjang argumen LUA". Suatu bahasa kemungkinan besar didefinisikan terutama untuk menyelesaikan masalah dengan cara tertentu, bukan menjadi dasar untuk sub-bahasa (ada pengecualian).

Petter Nordlander
sumber
-1

Faktor lain untuk itu adalah tidak selalu mudah untuk mendefinisikan operasi dengan operator yang tersedia. Maksud saya, ya, untuk jenis angka apa pun, operator '*' dapat masuk akal, dan biasanya diimplementasikan dalam bahasa atau dalam modul yang ada. Tetapi dalam kasus kelas kompleks tipikal yang Anda perlu mendefinisikan (hal-hal seperti ShipingAddress, WindowManager, ObjectDimensions, PlayerCharacter, dll) bahwa perilaku tidak jelas ... Apa artinya menambah atau mengurangi nomor ke Alamat? Lipat gandakan dua Alamat?

Tentu, Anda dapat menentukan bahwa menambahkan string ke kelas ShippingAddress berarti operasi kustom seperti "ganti baris 1 di alamat" (bukan fungsi "setLine1") dan menambahkan nomor adalah "ganti kode pos" (alih-alih "setZipCode") , tapi kemudian kodenya tidak terlalu mudah dibaca dan membingungkan. Kami biasanya berpikir bahwa operator digunakan dalam tipe / kelas dasar, karena perilakunya intuitif, jelas dan konsisten (setidaknya setelah Anda terbiasa dengan bahasa tersebut). Pikirkan tipe-tipe seperti Integer, String, ComplexNumber, dll.

Jadi, bahkan jika mendefinisikan operator bisa sangat berguna dalam beberapa kasus tertentu, implementasi dunia nyata mereka sangat terbatas, karena 99% dari kasus di mana itu akan menjadi kemenangan yang jelas sudah diimplementasikan dalam paket bahasa dasar.

Khelben
sumber