Semua bahasa pemrograman memiliki kekurangan desainnya hanya karena tidak ada satu bahasa pun yang sempurna, seperti halnya dengan sebagian besar (semua?) Hal lain. Selain itu, kesalahan desain mana dalam bahasa pemrograman yang paling membuat Anda jengkel sepanjang sejarah Anda sebagai seorang programmer?
Perhatikan bahwa jika suatu bahasa "buruk" hanya karena bahasa itu tidak dirancang untuk hal tertentu bukan cacat desain, tetapi fitur desain, jadi jangan daftar gangguan bahasa seperti itu. Jika suatu bahasa ditentukan untuk apa ia dirancang untuk, itu tentu saja merupakan cacat dalam desain. Implementasi hal-hal spesifik dan di bawah tenda hal-hal tidak masuk hitungan.
Jawaban:
Salah satu gangguan besar saya adalah cara
switch
kasus dalam bahasa yang diturunkan dari C default untuk jatuh ke kasus berikutnya jika Anda lupa untuk menggunakanbreak
. Saya mengerti bahwa ini berguna dalam kode level sangat rendah (mis. Perangkat Duff ), tetapi biasanya tidak sesuai untuk kode level aplikasi, dan merupakan sumber umum kesalahan pengkodean.Saya ingat sekitar tahun 1995 ketika saya membaca tentang rincian Jawa untuk pertama kalinya, ketika saya sampai pada bagian tentang
switch
pernyataan itu, saya sangat kecewa karena mereka mempertahankan perilaku gagal bayar yang baku. Ini hanya membuatswitch
dimuliakangoto
dengan nama lain.sumber
switch
tidak harus bekerja seperti itu. Misalnya, ada kasus / ketika pernyataan (setara dengan beralih / kasus) tidak memiliki perilaku jatuh.switch
-seperti pernyataan dalam bahasa yang tidak terkait dengan C tidak harus bekerja seperti itu. Tetapi jika Anda menggunakan kontrol aliran C-gaya ({
...}
,for (i = 0; i < N; ++i)
,return
, dll), bahasa inferensi akan membuat orang berharapswitch
untuk bekerja seperti C, dan memberikan Ada / Pascal / orang DASAR-seperti semantik akan bingung. C # membutuhkanbreak
diswitch
pernyataan untuk alasan yang sama, meskipun merek kurang rawan kesalahan dengan melarang fallthrough diam. (Tapi saya harap Anda bisa menulisfall;
alih-alih yang jelekgoto case
.)switch
memungkinkan fallthrough. Sebagian besar penggunaan fallthrough tidak disengaja.Saya tidak pernah benar-benar menyukai penggunaan
=
untuk penugasan dan==
untuk pengujian kesetaraan dalam bahasa yang diturunkan dari C. Potensi kebingungan dan kesalahan terlalu tinggi. Dan bahkan jangan mulai saya menggunakan===
Javascript.Lebih baik
:=
untuk tugas dan=
untuk pengujian kesetaraan. Semantik bisa persis sama dengan yang ada sekarang, di mana penugasan adalah ekspresi yang juga menghasilkan nilai.sumber
x<-5
). Pemrogram C tidak akan mentoleransi ruang kosong seperti itu yang diperlukan :):=
dan==
karena itu akan terlalu mudah untuk melupakan:
dan tidak diberitahu seperti itu sudah terjadi (meskipun dikembalikan) ketika Anda lupa=
hari ini. Saya berterima kasih atas peringatan kompiler tentang ini ...=
dan==
tidak dalam membaca, karena mereka adalah simbol yang berbeda. Ada dalam bentuk tertulis dan memastikan Anda mendapatkan yang benar.Pilihan
+
dalam Javascript untuk penambahan dan penggabungan string adalah kesalahan yang mengerikan. Karena nilai tidak diketik, ini mengarah ke aturan Bizantium yang menentukan apakah+
akan menambah atau menyatukan, tergantung pada konten yang tepat dari setiap operan.Akan mudah pada awalnya untuk memperkenalkan operator yang sama sekali baru seperti
$
untuk penggabungan string.sumber
+
membuat banyak akal sebagai operator rangkaian string dalam bahasa yang sangat diketik. Masalahnya adalah bahwa Javascript menggunakannya tetapi tidak diketik dengan kuat.+
tidak masuk akal untuk penggabungan, karena penggabungan pasti tidak komutatif. Ini merupakan penyalahgunaan sejauh yang saya ketahui.*
operator?Saya menemukan Javascript default untuk global ke masalah besar, dan sering sumber bug jika Anda tidak menggunakan JSLint atau sejenisnya
sumber
var
setiap kali Anda mendeklarasikan variabel dan Anda siap melakukannya. Dan jangan bilang terlalu banyak mengetik karena Java memaksa Anda untuk mendeklarasikan semua tipe dua kali dan tidak ada yang mengeluh bahwa itu adalah pilihan desain yang buruk.std::map<KEY, VALUE>::const_iterator
variabel dalam C ++ cukup yangauto
ditambahkan ke bahasa itu."use strict"
direktif, ditambahkan dalam ES5, perubahan referensi dideklarasikan dalam kesalahan.Preprosesor dalam C dan C ++ adalah lumpur yang sangat besar, menciptakan abstraksi yang bocor seperti saringan, mendorong kode spageti melalui sarang
#ifdef
pernyataan tikus , dan membutuhkanALL_CAPS
nama-nama yang sangat sulit dibaca untuk mengatasi keterbatasannya. Akar dari masalah ini adalah bahwa ia beroperasi pada level tekstual daripada level sintaksis atau semantik. Seharusnya diganti dengan fitur bahasa nyata untuk berbagai kasus penggunaannya. Berikut adalah beberapa contoh, meskipun diakui beberapa di antaranya diselesaikan dalam C ++, C99 atau ekstensi standar tidak resmi tapi de facto :#include
seharusnya diganti dengan sistem modul nyata.Fungsi dan template / generik sebaris dapat menggantikan sebagian besar kasus penggunaan panggilan fungsi.
Beberapa jenis fitur konstan manifes / kompilasi waktu dapat digunakan untuk mendeklarasikan konstanta tersebut. Ekstensi D enum sangat bagus di sini.
Makro sintaks tingkat pohon nyata dapat memecahkan banyak kasus penggunaan lain-lain.
String mixin dapat digunakan untuk kasus penggunaan kode injeksi
static if
atauversion
pernyataan dapat digunakan untuk kompilasi bersyarat.sumber
#include
masalah ini, tetapi sistem modul ditemukan ... sesudahnya! Dan C dan C ++ bertujuan untuk kompatibilitas maksimal: /#include
peretasan berbasis cpp saat ini .Orang bisa membuat daftar ratusan kesalahan dalam ratusan bahasa, tetapi IMO itu bukan latihan yang berguna dari perspektif desain bahasa.
Mengapa?
Karena sesuatu yang akan menjadi kesalahan dalam satu bahasa tidak akan menjadi kesalahan dalam bahasa lain. Contohnya:
Ada yang pelajaran yang bisa dipelajari, tetapi pelajaran jarang dipotong jelas, dan untuk memahami mereka Anda harus memahami teknis trade-off ... dan konteks historis. (Misalnya, implementasi generik Java yang rumit adalah konsekuensi dari persyaratan bisnis utama untuk mempertahankan kompatibilitas ke belakang.)
IMO, jika Anda serius mendesain bahasa baru, Anda harus benar-benar menggunakan berbagai bahasa yang ada (dan mempelajari bahasa historis) ... dan putuskan sendiri apa kesalahannya. Dan Anda harus ingat bahwa masing-masing bahasa ini dirancang dalam konteks historis tertentu, untuk memenuhi kebutuhan tertentu.
Jika ada pelajaran umum yang dapat dipetik, mereka berada di tingkat "meta":
sumber
C dan C ++ : Semua tipe integer yang tidak berarti apa - apa.
Terutama
char
. Apakah itu teks atau bilangan bulat kecil? Jika teks, apakah itu karakter "ANSI" atau unit kode UTF-8? Jika bilangan bulat, apakah ditandatangani atau tidak ditandatangani?int
dimaksudkan untuk menjadi integer berukuran "asli", tetapi pada sistem 64-bit, tidak.long
mungkin atau mungkin tidak lebih besar dariint
. Mungkin ukuran pointer atau tidak. Ini cukup banyak keputusan sewenang-wenang pada bagian penulis kompiler apakah itu 32-bit atau 64-bit.Jelas merupakan bahasa tahun 1970-an. Sebelum Unicode. Sebelum komputer 64-bit.
sumber
null
.Penemunya, Tony Hoare, menyebutnya "kesalahan miliar dolar" .
Itu diperkenalkan di ALGOL di tahun 60-an, dan ada di sebagian besar bahasa pemrograman yang umum digunakan saat ini.
Alternatif yang lebih baik, yang digunakan dalam bahasa seperti OCaml dan Haskell, adalah mungkin . Gagasan umum adalah bahwa referensi objek tidak boleh nol / kosong / tidak ada kecuali ada indikasi eksplisit bahwa mereka mungkin begitu.
(Meskipun Tony luar biasa dalam kesederhanaannya, saya pikir hampir semua orang akan melakukan kesalahan yang sama, dan kebetulan dia yang pertama.)
sumber
maybe
sebagai null opt-in, sedangkan pengecualian nol adalah opt-out. Tentu saja ada beberapa hal yang bisa dikatakan tentang perbedaan antara kesalahan runtime dan kesalahan waktu kompilasi juga, tetapi fakta bahwa null pada dasarnya menyuntikkan perilaku adalah hal yang patut diperhatikan.Saya merasa bahwa orang-orang yang merancang PHP tidak menggunakan keyboard normal, mereka bahkan tidak menggunakan keyboard colemak, karena mereka seharusnya menyadari apa yang mereka lakukan.
Saya adalah pengembang PHP. PHP tidak menyenangkan untuk diketik.
Who::in::their::right::mind::would::do::this()
? The::
Operator membutuhkan holding shift dan kemudian dua tombol yang ditekan. Buang-buang energi.Meskipun-> ini-> tidak-> jauh-> lebih baik. Itu juga membutuhkan tiga penekanan tombol dengan pergeseran berada di antara dua simbol.
$last = $we.$have.$the.$dumb.'$'.$character
. Tanda dolar digunakan dalam jumlah yang luar biasa besar dan membutuhkan peregangan penghargaan hingga ke bagian paling atas keyboard plus tombol shift.Mengapa mereka tidak bisa mendesain PHP untuk menggunakan kunci yang lebih cepat diketik? Mengapa
we.do.this()
vars tidak dapat atau mulai dengan kunci yang hanya memerlukan penekanan tombol tunggal - atau tidak sama sekali (JavaScript) dan tentukan sebelumnya semua vars (seperti yang harus saya lakukan untuk E_STRICT)!Saya bukan pengetik yang lambat - tapi ini hanya pilihan desain yang lemah.
sumber
I::have::nothing::against::this->at.all()
Penggunaan formulir yang terinspirasi desktop dalam asp.net .
Itu selalu terasa fudge dan menghalangi atau bagaimana web sebenarnya bekerja. Syukurlah asp.net-mvc tidak menderita dengan cara yang sama, meskipun dengan kredit untuk Ruby dll untuk inspirasi itu.
sumber
Bagi saya itu adalah kurangnya konvensi penamaan dan argumen pemesanan PHP di perpustakaan standarnya.
Meskipun kebutuhan JASS untuk membatalkan referensi setelah objek yang direferensikan dirilis / dihapus (atau referensi akan bocor dan beberapa byte memori akan hilang) lebih serius, tetapi karena JASS adalah bahasa tujuan tunggal, itu tidak terlalu kritis.
sumber
Cacat desain terbesar yang saya hadapi adalah bahwa python tidak dirancang seperti python 3.x untuk memulai.
sumber
Array pembusukan dalam C dan akibatnya C ++.
sumber
delete
dan terpisahdelete[]
.Jenis primitif di Jawa.
Mereka melanggar prinsip bahwa segala sesuatu adalah turunan dari
java.lang.Object
, yang dari sudut pandang teoretis mengarah ke kompleksitas tambahan dari spesifikasi bahasa, dan dari perspektif praktis mereka membuat penggunaan koleksi sangat membosankan.Autoboxing membantu meringankan kekurangan praktis tetapi dengan biaya membuat spesifikasi lebih rumit dan memperkenalkan kulit pisang besar lemak: sekarang Anda bisa mendapatkan pengecualian pointer nol dari apa yang tampak seperti operasi aritmatika sederhana.
sumber
Saya tahu Perl terbaik, jadi saya akan memilihnya.
Perl mencoba banyak ide. Beberapa bagus. Ada yang buruk. Beberapa asli dan tidak disalin secara luas karena alasan yang baik.
Salah satunya adalah gagasan tentang konteks - setiap pemanggilan fungsi dilakukan dalam daftar atau konteks skalar, dan dapat melakukan hal-hal yang sepenuhnya berbeda dalam setiap konteks. Seperti yang saya tunjukkan di http://use.perl.org/~btilly/journal/36756 hal ini menyulitkan setiap API, dan sering menyebabkan masalah desain yang halus dalam kode Perl.
Yang berikutnya adalah ide untuk mengikat sintaks dan tipe data secara lengkap. Ini mengarah pada penemuan dasi untuk memungkinkan objek untuk menyamar sebagai tipe data lainnya. (Anda juga dapat mencapai efek yang sama menggunakan overload, tetapi dasi adalah pendekatan yang lebih umum di Perl.)
Kesalahan umum lainnya, yang dibuat oleh banyak bahasa, adalah memulai dengan menawarkan pelingkupan dinamis daripada leksikal. Sulit untuk mengembalikan keputusan desain ini nanti, dan mengarah pada kutil yang tahan lama. Deskripsi klasik dari kutil tersebut di Perl adalah http://perl.plover.com/FAQs/Namespaces.html . Perhatikan bahwa ini ditulis sebelum Perl menambahkan
our
variabel danstatic
variabel.Orang-orang secara sah tidak setuju pada pengetikan statis versus dinamis. Saya pribadi suka mengetik dinamis. Namun penting untuk memiliki struktur yang cukup untuk membiarkan kesalahan ketik ditangkap. Perl 5 melakukan pekerjaan dengan baik dengan ketat. Tapi Perl 1-4 salah. Beberapa bahasa lain memiliki checker yang melakukan hal yang sama dengan ketat. Selama Anda pandai menegakkan pemeriksaan serat, itu bisa diterima.
Jika Anda mencari lebih banyak ide buruk (banyak di antaranya), pelajari PHP dan pelajari sejarahnya. Kesalahan masa lalu favorit saya (sudah lama diperbaiki karena menyebabkan banyak lubang keamanan) adalah default untuk mengizinkan siapa saja untuk mengatur variabel apa pun dengan mengirimkan parameter formulir. Tapi itu jauh dari satu-satunya kesalahan.
sumber
Ambiguitas JavaScripts untuk blok kode dan objek literal.
bisa berupa blok kode, di mana
a
label danb
ekspresi; atau bisa mendefinisikan objek, dengan atributa
yang memiliki nilaib
sumber
a
.Aku akan kembali ke FORTRAN dan ketidakpekaan whitespace.
Itu meliputi spesifikasi. The
END
kartu harus didefinisikan sebagai kartu dengan 'E', sebuah 'N', dan 'D' agar di kolom 7-72, dan tidak ada Nonblanks lain, daripada kartu dengan "END" di tepat kolom dan tidak ada yang lain.Itu menyebabkan kebingungan sintaksis yang mudah.
DO 100 I = 1, 10
adalah pernyataan kontrol loop, sedangkanDO 100 I = 1. 10
pernyataan yang menetapkan nilai 1.1 ke variabel yang dipanggilDO10I
. (Fakta bahwa variabel dapat dibuat tanpa deklarasi, tipenya tergantung pada huruf pertama mereka, berkontribusi terhadap hal ini.) Tidak seperti bahasa lain, tidak ada cara untuk menggunakan spasi untuk memisahkan token untuk memungkinkan disambiguasi.Itu juga memungkinkan orang lain untuk menulis kode yang benar-benar membingungkan. Ada alasan mengapa fitur FORTRAN ini tidak pernah digandakan lagi.
sumber
(test ? a : b)
, e) bersikeras saat menggunakan**
, f) tidak dapat menangani sensitivitas huruf besar-kecil. Sebagian besar ini adalah karena tombol pada tahun 50-an.Salah satu masalah terbesar dengan BASIC adalah kurangnya metode yang terdefinisi dengan baik untuk memperluas bahasa di luar lingkungan awalnya, yang mengarah ke sekelompok implementasi yang sama sekali tidak kompatibel (dan upaya pasca-facto yang hampir tidak relevan pada standardisasi apa pun).
Hampir semua bahasa akan digunakan untuk tujuan umum oleh beberapa programmer gila. Lebih baik untuk merencanakan penggunaan tujuan umum di awal jika ide gila lepas landas.
sumber
Saya percaya pada DSL (domain-spesifik-bahasa) dan satu hal yang saya hargai dalam suatu bahasa adalah jika memungkinkan saya untuk mendefinisikan DSL di atasnya.
Di Lisp ada makro - kebanyakan orang menganggap ini hal yang baik, seperti halnya saya.
Di C dan C ++ ada makro - orang mengeluh tentang mereka, tapi saya bisa menggunakannya untuk mendefinisikan DSL.
Di Jawa, mereka ditinggalkan (dan karena itu dalam C #), dan kekurangan mereka dinyatakan sebagai kebajikan. Tentu itu memungkinkan Anda memiliki kecerdasan, tetapi bagi saya itu hanya sebuah oeuvre . Untuk melakukan DSL saya, saya harus melakukan ekspansi dengan tangan. Ini menyebalkan, dan itu membuat saya terlihat seperti seorang programmer yang buruk, walaupun itu membuat saya melakukan lebih banyak hal dengan lebih sedikit kode.
sumber
cdr
daftar dan membentuk penutupan lambda dari itu (yaitu kelanjutan) dan meneruskannya sebagai argumen kecar
daftar. Itu dilakukan secara rekursif, tentu saja, dan akan "melakukan hal yang benar" untuk kondisional, loop, dan pemanggilan fungsi. Kemudian fungsi "pilihan" baru saja berubah menjadi loop normal. Tidak cantik, tapi kuat. Masalahnya, itu membuatnya sangat mudah untuk membuat loop yang terlalu bersarang.Pernyataan , dalam setiap bahasa yang memilikinya. Mereka tidak melakukan apa pun yang tidak dapat Anda lakukan dengan ekspresi dan mencegah Anda melakukan banyak hal. Keberadaan
?:
operator ternary hanyalah salah satu contoh harus mencoba untuk berkeliling mereka. Dalam JavaScript, mereka sangat menjengkelkan:sumber
unit
tipe (alias()
) alih-alih pernyataan memiliki pertimbangan khusus untuk memastikan bahwa mereka tidak memberikan peringatan atau berperilaku aneh.Bagi saya, itu adalah masalah desain yang mengganggu semua bahasa yang berasal dari C; yaitu, " menggantung yang lain ." Masalah tata bahasa ini seharusnya telah diselesaikan dalam C ++, tetapi dibawa ke Java dan C #.
sumber
Saya pikir semua jawaban sejauh ini menunjuk pada satu kegagalan banyak bahasa utama:
Tidak ada cara untuk mengubah bahasa inti tanpa mempengaruhi kompatibilitas ke belakang.
Jika ini diselesaikan maka hampir semua keluhan lain dapat diselesaikan.
EDIT.
ini dapat diselesaikan di perpustakaan dengan memiliki ruang nama yang berbeda, dan Anda dapat membayangkan melakukan sesuatu yang serupa untuk sebagian besar inti bahasa, meskipun ini mungkin berarti Anda perlu mendukung banyak kompiler / juru bahasa.
Pada akhirnya saya tidak berpikir saya tahu bagaimana menyelesaikannya dengan cara yang benar-benar memuaskan, tetapi itu tidak berarti solusi tidak ada, atau lebih banyak yang tidak dapat dilakukan
sumber
Java integer diam meluap aritmatika
sumber
Baik Java dan C # memiliki masalah yang mengganggu dengan sistem tipe mereka karena keinginan untuk mempertahankan kompatibilitas sambil menambahkan obat generik. Java tidak suka mencampur generik dan array; C # tidak akan mengizinkan beberapa tanda tangan yang berguna karena Anda tidak dapat menggunakan tipe nilai sebagai batas.
Sebagai contoh dari yang terakhir, pertimbangkan itu
bersama atau mengganti diEnum
kelas akan memungkinkan daripada tautologistl; dr: pikirkan polimorfisme parametrik ketika Anda mulai merancang sistem tipe Anda, bukan setelah Anda menerbitkan versi 1.
sumber
MyMethod<T>(T value) where T : struct, IComparable, IFormattable, IConvertible
Tapi Anda masih harus menguji untuk enum dan itu adalah peretasan. Saya pikir kekurangan yang lebih besar dalam generik C # tidak ada dukungan untuk jenis yang lebih tinggi, yang benar-benar akan membuka bahasa untuk beberapa konsep keren.Saya merasa seperti membuka diri untuk dinyalakan, tetapi saya benar-benar benci kemampuan untuk melewatkan tipe data lama dengan merujuk pada C ++. Saya hanya sedikit benci bisa melewati tipe kompleks dengan referensi. Jika saya melihat suatu fungsi:
Dari titik panggilan, tidak ada cara untuk mengatakan bahwa
bar
, yang dapat didefinisikan dalam file yang sama sekali berbeda, adalah:Beberapa orang mungkin berpendapat bahwa melakukan sesuatu seperti ini mungkin hanya desain perangkat lunak yang buruk, dan tidak bisa menyalahkan bahasa, tetapi saya tidak suka bahasanya memungkinkan Anda melakukan ini sejak awal. Menggunakan pointer dan panggilan
jauh lebih mudah dibaca.
sumber
MENGUBAH
Ketika saya belajar COBOL, pernyataan ALTER masih menjadi bagian dari standar. Singkatnya, pernyataan ini akan memungkinkan Anda untuk memodifikasi panggilan prosedur saat runtime.
Bahayanya adalah Anda bisa meletakkan pernyataan ini di bagian kode yang tidak jelas yang jarang diakses dan berpotensi mengubah aliran program Anda secara keseluruhan. Dengan beberapa pernyataan ALTER, Anda dapat membuatnya hampir mustahil untuk mengetahui apa yang sedang dilakukan program Anda kapan saja.
Instruktur universitas saya, dengan sangat tegas, menyatakan bahwa jika ia pernah melihat pernyataan itu dalam program kami, ia akan secara otomatis membuat kami gagal.
sumber
v() { if (not alreadyCalculatedResult) { result = long(operation); alreadyCalculatedResult = true; } result; }
Anda katakanv() { result = long(operation); v = () => result; result; }
Dosa terburuk dari bahasa pemrograman tidak didefinisikan dengan baik. Sebuah kasus yang saya ingat adalah C ++, yang, asalnya:
Seingat saya, butuh sekitar satu dekade untuk mendapatkan C ++ yang didefinisikan dengan cukup baik untuk menjadikannya dapat diandalkan secara profesional seperti C. Ini adalah sesuatu yang seharusnya tidak pernah terjadi lagi.
Hal lain yang saya anggap dosa (haruskah ia pergi dalam jawaban yang berbeda?) Adalah memiliki lebih dari satu cara "terbaik" untuk melakukan beberapa tugas bersama. Ini adalah kasus dari (lagi) C ++, Perl, dan Ruby.
sumber
Kelas-kelas di C ++ adalah semacam pola desain paksa dalam bahasa.
Praktis tidak ada perbedaan pada saat runtime antara struct dan kelas, dan itu sangat membingungkan untuk memahami apa sebenarnya keunggulan pemrograman "penyembunyian informasi" yang ingin saya taruh di sana.
Saya akan downvoted untuk itu, tapi bagaimanapun, kompiler C ++ sangat sulit untuk menulis bahasa ini terasa seperti monster.
sumber
Meskipun setiap bahasa memiliki kesalahan, tidak ada gangguan setelah Anda mengetahuinya. Kecuali untuk pasangan ini:
Sintaks yang rumit ditambah dengan API bertele-tele
Ini terutama berlaku untuk bahasa seperti Objective-C. Sintaksnya tidak hanya sangat kompleks, tetapi API menggunakan nama fungsi seperti:
Saya semua untuk menjadi eksplisit dan tidak ambigu, tetapi ini konyol. Setiap kali saya duduk dengan xcode, saya merasa seperti n00b, dan itu benar-benar membuat frustrasi.
sumber
tableView:cellForRowAtIndexPath:
, yang sangat deskriptif menurut saya.sumber
(u)int_least8_t
). Signedness masuk akal untuk bilangan bulat kecil, tetapi sama sekali tidak masuk akal untuk karakter.char*
... seperti C-String, mereka menjadi sangat bingung.sbyte
,byte
danchar
jenis.