Wszystkie języki programowania mają swoje wady projektowe po prostu dlatego, że żaden język nie może być idealny, tak jak większość (wszystkich?) Innych rzeczy. Poza tym, który błąd projektowy w języku programowania najbardziej cię irytuje w twojej historii jako programisty?
Zauważ, że jeśli język jest „zły” tylko dlatego, że nie jest przeznaczony do konkretnej rzeczy, nie jest to wada projektowa, ale cecha projektu, więc nie wymieniaj takich irytacji języków. Jeśli język jest nieodpowiedni do tego, do czego jest przeznaczony, jest to oczywiście wada w projekcie. Rzeczy specyficzne dla realizacji i pod maską też się nie liczą.
Odpowiedzi:
Jedną z moich dużych irytacji jest sposób, w jaki
switch
przypadki w językach pochodnych C domyślnie przechodzą do następnej sprawy, jeśli zapomnisz użyćbreak
. Rozumiem, że jest to przydatne w kodzie bardzo niskiego poziomu (np. Duff's Device ), ale zwykle jest nieodpowiednie dla kodu poziomu aplikacji i jest częstym źródłem błędów kodowania.Pamiętam około 1995 r., Kiedy po raz pierwszy czytałem o szczegółach dotyczących Javy, kiedy doszedłem do tej części
switch
oświadczenia, byłem bardzo rozczarowany, że zachowali domyślne zachowanie awaryjne. To po prostu stajeswitch
się uwielbionegoto
pod innym imieniem.źródło
switch
nie musi tak działać. Na przykład instrukcja case / when Ady (odpowiednik przełącznika / case) nie ma zachowania awaryjnego.switch
podobne instrukcje w językach niezwiązanych z C nie muszą tak działać. Ale jeśli używasz przepływ sterowania C-Style ({
...}
,for (i = 0; i < N; ++i)
,return
, etc.), język wnioskowanie oczekiwać, że ludzie będąswitch
pracować jak C, nadając mu Ada / Pascal / BASIC-like semantyki byłby mylić ludzi. C # wymagabreak
wswitch
instrukcjach z tego samego powodu, chociaż czyni go mniej podatnym na błędy, zabraniając cichego przewracania się. (Ale chciałbym, żebyś pisałfall;
zamiast brzydkiegogoto case
.)switch
pozwala na upadek. Chodzi o to, że większość zastosowań upadku nie jest zamierzona.Nigdy tak naprawdę nie podobało mi się używanie
=
do zadań i==
testów równości w językach pochodnych C. Potencjalne zamieszanie i błędy są zbyt duże. I nawet nie zaczynaj mnie od===
Javascript.Lepiej byłoby
:=
dla zadania i=
dla testów równości. Semantyka mogłaby być dokładnie taka sama jak dzisiaj, gdzie przypisanie jest wyrażeniem, które również wytwarza wartość.źródło
x<-5
). Programiści C nie tolerowaliby tego rodzaju wymaganych białych znaków :):=
a==
ponieważ zbyt łatwo byłoby zapomnieć:
i nie zostać powiadomionym, jak to już jest (choć cofnięte), gdy zapomnisz=
dzisiejszego dnia. Jestem wdzięczny za ostrzeżenia kompilatora dotyczące tego ...=
a jego brakiem==
jest to, że są to odrębne symbole. Jest na piśmie i upewnia się, że masz właściwy.Wybór
+
w JavaScript zarówno dodawania, jak i łączenia ciągów był strasznym błędem. Ponieważ wartości są bez typu, prowadzi to do bizantyjskich reguł, które określają, czy+
dodają, czy konkatenują, w zależności od dokładnej zawartości każdego argumentu.Na początku łatwo byłoby wprowadzić zupełnie nowy operator, na przykład
$
do łączenia łańcuchów.źródło
+
ma sens jako operator konkatenacji łańcuchów w silnie typowanym języku. Problem polega na tym, że JavaScript go używa, ale nie jest mocno wpisany.+
nie ma sensu konkatenacja, ponieważ konkatenacja zdecydowanie nie jest przemienna. Według mnie jest to nadużycie.*
operatora jest nadużyciem ?Uważam, że JavaScript jest domyślnie globalny jako poważny problem i często jest źródłem błędów, jeśli nie używasz JSLint lub podobnego
źródło
var
gdy deklarujesz zmienną i jesteś gotowy. I nie mów mi, że to zbyt dużo pisania, ponieważ Java zmusza cię do dwukrotnego zadeklarowania wszystkich typów i nikt nie narzeka, że to kiepski wybór.std::map<KEY, VALUE>::const_iterator
zmiennych w C ++ na tyle, żeauto
są dodawane do tego języka."use strict"
dyrektywy, dodał w ES5 zmienia referencje nielegalnej w błąd.Preprocesor w C i C ++ jest ogromną kludge, tworzy abstrakcje, które przeciekają jak sita, zachęca do kodowania spaghetti poprzez gniazda
#ifdef
instrukcji szczura i wymaga strasznie nieczytelnychALL_CAPS
nazw, aby obejść jego ograniczenia. Źródłem tych problemów jest to, że działa na poziomie tekstowym, a nie na poziomie składniowym lub semantycznym. Powinien zostać zastąpiony funkcjami języka rzeczywistego w różnych przypadkach użycia. Oto kilka przykładów, choć wprawdzie niektóre z nich rozwiązano w C ++, C99 lub nieoficjalnych, ale de facto standardowych rozszerzeniach:#include
powinien zostać zastąpiony prawdziwym systemem modułowym.Funkcje wbudowane i szablony / ogólne mogą zastąpić większość przypadków użycia wywołania funkcji.
Do deklarowania takich stałych można użyć pewnego rodzaju funkcji stałej czasowej manifestu / kompilacji. Przedłużenia enumu D świetnie sprawdzają się tutaj.
Makra na poziomie drzewa składni mogą rozwiązać wiele różnych przypadków użycia.
W przypadku użycia wstrzyknięcia kodu można zastosować miksy łańcuchowe .
static if
lubversion
do kompilacji warunkowej można użyć instrukcji.źródło
#include
problemem, ale system modułowy został wynaleziony ... później! A C i C ++ dążą do maksymalnej kompatybilności wstecznej: /#include
hakowaniem opartym na procesorze CPP.Można wymienić setki błędów w setkach języków, ale IMO nie jest to przydatne ćwiczenie z perspektywy projektowania języka.
Czemu?
Ponieważ coś, co byłoby błędem w jednym języku, nie byłoby błędem w innym języku. Na przykład:
Jest to należy wyciągnąć wnioski, ale lekcje rzadko są jednoznaczne, a ich zrozumieć trzeba zrozumieć technicznych kompromisów ... i historyczny kontekst. (Na przykład nieporęczna implementacja Java generics jest konsekwencją nadrzędnych wymagań biznesowych aby zachować kompatybilność wsteczną).
IMO, jeśli poważnie myślisz o zaprojektowaniu nowego języka, musisz faktycznie korzystać z szerokiej gamy istniejących języków (i uczyć się języków historycznych) ... i zdecydować, jakie są błędy. I należy pamiętać, że każdy z tych języków został zaprojektowany w określonym kontekście historycznym, aby zaspokoić określoną potrzebę.
Jeśli trzeba wyciągnąć ogólne wnioski, są one na poziomie „meta”:
źródło
C i C ++ : Wszystkie typy liczb całkowitych, które nic nie znaczą .
Zwłaszcza
char
. Czy to tekst czy mała liczba całkowita? Jeśli jest to tekst, czy jest to znak „ANSI” czy jednostka kodu UTF-8? Jeśli jest to liczba całkowita, czy jest podpisana czy niepodpisana?int
miał być liczbą całkowitą wielkości „natywnej”, ale w systemach 64-bitowych tak nie jest.long
może, ale nie musi być większy niżint
. Może, ale nie musi być wielkości wskaźnika. Jest to dość arbitralna decyzja autorów kompilatora, czy jest to wersja 32-bitowa czy 64-bitowa.Zdecydowanie język lat siedemdziesiątych. Przed Unicode. Przed komputerami 64-bitowymi.
źródło
null
.Jego wynalazca, Tony Hoare, nazywa to „błędem miliarda dolarów” .
Został wprowadzony w ALGOL w latach 60. i istnieje obecnie w większości powszechnie używanych języków programowania.
Lepszą alternatywą, używaną w takich językach jak OCaml i Haskell, jest może . Ogólna idea jest taka, że odwołania do obiektów nie mogą być zerowe / puste / nie istnieją, chyba że istnieje wyraźne wskazanie, że mogą być.
(Chociaż Tony jest niesamowity w swojej skromności, myślę, że prawie każdy popełniłby ten sam błąd, a on po prostu był pierwszy).
źródło
maybe
opt-in zerowej, podczas gdy wyjątek null to opt-out. Oczywiście jest kilka rzeczy do powiedzenia na temat różnicy między błędami środowiska wykonawczego a błędami czasu kompilacji, ale sam fakt, że null jest zasadniczo wstrzykiwaniem zachowania, jest sam w sobie warty odnotowania.Mam wrażenie, że ludzie, którzy zaprojektowali PHP, nie używali normalnej klawiatury, nawet nie używają klawiatury colemak, ponieważ powinni byli zrozumieć, co robią.
Jestem programistą PHP. PHP nie jest fajne do pisania.
Who::in::their::right::mind::would::do::this()
?::
Operator wymaga zmiany trzymającego i potem dwa naciśnięcia przycisków. Co za strata energii.Chociaż-> to-> jest-> nie-> dużo-> lepiej. Wymaga to również trzech naciśnięć klawiszy z przesunięciem pomiędzy dwoma symbolami.
$last = $we.$have.$the.$dumb.'$'.$character
. Znak dolara jest używany wiele razy i wymaga rozciągnięcia nagrody aż do samej góry klawiatury oraz naciśnięcia klawisza Shift.Dlaczego nie mogli zaprojektować PHP do używania kluczy, które są znacznie szybsze do pisania? Dlaczego
we.do.this()
vars nie mogą zacząć mieć klucza, który wymaga tylko jednego naciśnięcia klawisza - lub wcale (JavaScript) i po prostu wstępnie zdefiniować wszystkie vary (tak jak i tak muszę zrobić dla E_STRICT)!Nie jestem powolną maszynistką - ale to tylko kiepski wybór.
źródło
I::have::nothing::against::this->at.all()
Korzystanie z formularzy inspirowanych pulpitem w asp.net .
Zawsze czułem krówki i przeszkadzało w działaniu sieci. Na szczęście asp.net-mvc nie cierpi w ten sam sposób, choć dzięki tej inspiracji Ruby itp.
źródło
Dla mnie to jest PHP absolutny brak „s nazewnictwa i zamawiania argumentem konwencji w standardowej bibliotece.
Chociaż konieczność JASS unieważnienia referencji po zwolnieniu / usunięciu referencyjnego obiektu (lub referencja wyciekłaby i utracono by kilka bajtów pamięci) jest poważniejsza, ale ponieważ JASS jest językiem jednofunkcyjnym, nie jest to aż tak istotne.
źródło
Największą wadą projektową, z jaką się spotykam, jest to, że Python nie został zaprojektowany jak Python 3.x na początek.
źródło
Rozpad tablicy w C i w konsekwencji C ++.
źródło
delete
idelete[]
operatory.Typy pierwotne w Javie.
Łamają zasadę, że wszystko jest potomkiem
java.lang.Object
, co z teoretycznego punktu widzenia prowadzi do dodatkowej złożoności specyfikacji językowej, a z praktycznego punktu widzenia sprawiają, że korzystanie z kolekcji jest niezwykle nużące.Autoboxing pomógł złagodzić praktyczne wady, ale kosztem uczynienia specyfikacji jeszcze bardziej skomplikowaną i wprowadzenia grubej skórki banana: teraz możesz uzyskać wyjątek wskaźnika zerowego od czegoś, co wygląda jak prosta operacja arytmetyczna.
źródło
Znam Perla najlepiej, więc wybiorę to.
Perl próbował wielu pomysłów. Niektóre były dobre. Niektóre były złe. Niektóre były oryginalne i nie zostały powszechnie skopiowane z dobrego powodu.
Jednym z nich jest idea kontekstu - każde wywołanie funkcji odbywa się w kontekście listowym lub skalarnym i może robić zupełnie różne rzeczy w każdym kontekście. Jak wskazałem na http://use.perl.org/~btilly/journal/36756, komplikuje to każdy interfejs API i często prowadzi do subtelnych problemów projektowych w kodzie Perla.
Kolejnym jest pomysł tak kompletnego wiązania składni i typów danych. Doprowadziło to do wynalezienia wiązania, które pozwala maskować obiekty jako inne typy danych. (Możesz również osiągnąć ten sam efekt za pomocą przeciążenia, ale remis jest bardziej powszechnym podejściem w Perlu).
Innym częstym błędem popełnianym przez wiele języków jest rozpoczęcie od dynamicznego określania zakresu, a nie leksykalnego. Trudno później cofnąć tę decyzję projektową i prowadzi to do długotrwałych brodawek. Klasyczny opis tych brodawek w Perlu to http://perl.plover.com/FAQs/Namespaces.html . Zauważ, że zostało to napisane zanim Perl dodał
our
zmienne istatic
zmienne.Ludzie słusznie nie zgadzają się na temat pisania statycznego i dynamicznego. Osobiście lubię dynamiczne pisanie. Jednak ważne jest, aby mieć wystarczającą strukturę, aby pozwolić na złapanie literówek. Perl 5 dobrze sobie z tym radzi ze ścisłością. Ale Perl 1-4 źle to zrozumiał. Kilka innych języków ma kontrolery kłaczków, które robią to samo co surowe. Dopóki jesteś dobry w egzekwowaniu sprawdzania kłaczków, jest to dopuszczalne.
Jeśli szukasz więcej złych pomysłów (wiele z nich), naucz się PHP i przestudiuj jego historię. Moim ulubionym błędem z przeszłości (dawno temu naprawionym, ponieważ prowadził do tylu dziur w zabezpieczeniach) było domyślne, pozwalające każdemu ustawić dowolną zmienną poprzez przekazanie parametrów formularza. Ale to nie jest jedyny błąd.
źródło
Niejednoznaczność JavaScript w blokach kodu i literałach obiektów.
może być blokiem kodu, gdzie
a
jest etykietą ib
wyrażeniem; lub może zdefiniować obiekt z atrybutem,a
który ma wartośćb
źródło
a
.Wrócę do FORTRAN i niewrażliwości na białe znaki.
Przenikało specyfikację.
END
Miał karty, które zostaną zdefiniowane jako karta z „E”, „N”, i „D” w tej kolejności w kolumnach 7-72, a nie innego nonblanks, zamiast karty z „End” we właściwym kolumny i nic więcej.Doprowadziło to do łatwego zamieszania składniowego.
DO 100 I = 1, 10
była instrukcją sterującą pętlą, podczas gdyDO 100 I = 1. 10
była instrukcją, która przypisała wartość 1.1 do zmiennej o nazwieDO10I
. (Przyczynił się do tego fakt, że zmienne można tworzyć bez deklaracji, a ich typ zależy od ich pierwszej litery.) W przeciwieństwie do innych języków, nie było sposobu na użycie spacji do oddzielenia tokenów, aby umożliwić ujednoznacznienie.Umożliwiło to także innym osobom pisanie naprawdę mylącego kodu. Istnieją powody, dla których ta funkcja FORTRAN nigdy nie została powtórzona.
źródło
(test ? a : b)
, e) nalegają przy użyciu**
, f) nie radzi sobie z rozróżnianiem wielkości liter. Większość z nich była spowodowana uderzeniami klawiszy w latach 50.Jednym z największych problemów z BASIC-em był brak dobrze zdefiniowanej metody rozszerzenia języka poza jego wczesne środowiska, co doprowadziło do szeregu całkowicie niekompatybilnych implementacji (i prawie nieistotnej postfaktycznej próby jakiejkolwiek standaryzacji).
Niemal każdy język zostanie upowszechniony przez jakiegoś szalonego programistę. Lepiej na początku zaplanować użycie tego ogólnego celu na wypadek, gdyby szalony pomysł się pojawił.
źródło
Wierzę w DSL (języki specyficzne dla domeny), a jedną rzeczą, którą cenię w języku, jest to, że pozwala mi to zdefiniować DSL na nim.
W Lisp są makra - większość ludzi uważa to za dobrą rzecz, podobnie jak ja.
W C i C ++ są makra - ludzie narzekają na nie, ale mogłem ich użyć do zdefiniowania DSL.
W Javie zostały pominięte (a zatem w języku C #), a ich brak został uznany za cnotę. Jasne, że to pozwala na inteligencję, ale dla mnie to tylko dzieło . Aby wykonać DSL, muszę rozwinąć ręcznie. Jest to ból i sprawia, że wyglądam jak zły programista, mimo że pozwala mi zrobić o wiele więcej przy tonie mniejszej ilości kodu.
źródło
cdr
listę i utworzyłby z niej zamknięcie lambda (tj. Kontynuację) i przekazałby ją jako argument docar
listy. Zrobiono to oczywiście rekurencyjnie i „zrobiłoby to dobrze” w przypadku warunkowych, pętli i wywołań funkcji. Wtedy funkcja „wyboru” właśnie zmieniła się w normalną pętlę. Nie ładna, ale solidna. Problem polega na tym, że bardzo łatwo jest tworzyć zbyt zagnieżdżone pętle.Oświadczenia , w każdym języku, który je ma. Nie robią nic, czego nie można zrobić z wyrażeniami i uniemożliwiają robienie wielu rzeczy. Istnienie
?:
operatora trójskładnikowego jest tylko jednym z przykładów próby obejścia go. W JavaScript są szczególnie denerwujące:źródło
unit
wyrażeń mają pojęcie typu (aka()
), zwracają szczególną uwagę na to, aby nie rzucały ostrzeżeń ani nie zachowywały się dziwnie.Dla mnie jest to problem projektowy, który nęka wszystkie języki wywodzące się z C; mianowicie „ zwisające inne ”. Ten problem gramatyczny powinien zostać rozwiązany w C ++, ale został przeniesiony do Java i C #.
źródło
Myślę, że wszystkie dotychczasowe odpowiedzi wskazują na jedno niepowodzenie wielu języków głównego nurtu:
Nie ma możliwości zmiany języka podstawowego bez wpływu na kompatybilność wsteczną.
Jeśli to zostanie rozwiązane, prawie wszystkie inne uchwyty mogą zostać rozwiązane.
EDYTOWAĆ.
można to rozwiązać w bibliotekach, stosując różne przestrzenie nazw, i można sobie wyobrazić zrobienie czegoś podobnego dla większości rdzeni języka, chociaż może to wtedy oznaczać, że trzeba obsługiwać wiele kompilatorów / tłumaczy.
Ostatecznie nie sądzę, że wiem, jak to rozwiązać w sposób całkowicie zadowalający, ale to nie znaczy, że rozwiązanie nie istnieje lub że nie można zrobić więcej
źródło
Cichy, całkowity arytmetyczny przelew Java
źródło
Zarówno Java, jak i C # mają irytujące problemy ze swoimi systemami typów ze względu na chęć zachowania kompatybilności wstecznej podczas dodawania generycznych. Java nie lubi mieszać ogólnych i tablic; C # nie zezwala na niektóre przydatne podpisy, ponieważ nie można używać typów wartości jako granic.
Rozważ to jako przykład tego ostatniego
obok lub zastępując wEnum
klasie pozwoli zamiast tautologicznegotl; dr: pomyśl o polimorfizmie parametrycznym, kiedy zaczniesz projektować swój system typów, a nie po opublikowaniu wersji 1.
źródło
MyMethod<T>(T value) where T : struct, IComparable, IFormattable, IConvertible
Ale wciąż musisz przetestować wyliczenie i jest to hack. Myślę, że większy brak generycznych C # to brak obsługi wyższych rodzajów, co naprawdę otworzyłoby język na kilka fajnych koncepcji.Wydaje mi się, że otwieram się na płomienie, ale naprawdę nie znoszę możliwości przekazywania zwykłych starych typów danych przez odwołanie w C ++. Tylko trochę nienawidzę być w stanie przekazywać złożone typy przez odniesienie. Jeśli patrzę na funkcję:
Z punktu wywoławczego nie ma sposobu, aby stwierdzić, że
bar
w zupełnie innym pliku można zdefiniować:Niektórzy mogą twierdzić, że robienie czegoś takiego może być po prostu złym projektem oprogramowania i nie winić za to języka, ale nie podoba mi się, że język pozwala ci to zrobić w pierwszej kolejności. Używanie wskaźnika i wywoływanie
jest o wiele bardziej czytelny.
źródło
ZMIENIAĆ
Kiedy nauczyłem się języka COBOL, instrukcja ALTER wciąż była częścią standardu. Krótko mówiąc, to oświadczenie pozwoli ci modyfikować wywołania procedur w czasie wykonywania.
Niebezpieczeństwo polegało na tym, że można było umieścić tę instrukcję w jakiejś niejasnej części kodu, do której rzadko uzyskiwano dostęp i która potencjalnie mogła całkowicie zmienić przebieg reszty programu. Dzięki wielu instrukcjom ALTER możesz praktycznie uniemożliwić sprawdzenie, co robił Twój program w dowolnym momencie.
Mój instruktor uniwersytecki bardzo stanowczo stwierdził, że gdyby kiedykolwiek zobaczył to oświadczenie w którymkolwiek z naszych programów, automatycznie nas wyrzuci.
źródło
v() { if (not alreadyCalculatedResult) { result = long(operation); alreadyCalculatedResult = true; } result; }
mówiszv() { result = long(operation); v = () => result; result; }
Najgorszy grzech języka programowania nie jest dobrze zdefiniowany. Przypadek, który pamiętam, to C ++, który początkowo:
Jak pamiętam, zajęło około dekady zdefiniowanie C ++ na tyle dobrze, aby uczynić go tak niezawodnym zawodowo jak C. Jest to coś, co nigdy nie powinno się powtórzyć.
Coś innego, co uważam za grzech (czy powinienem udzielić innej odpowiedzi?), Ma więcej niż jeden „najlepszy” sposób na wykonanie jakiegoś wspólnego zadania. Jest tak w przypadku (ponownie) C ++, Perla i Ruby.
źródło
Klasy w C ++ są pewnego rodzaju wzorcem wymuszonego projektowania w języku.
W czasie wykonywania praktycznie nie ma różnicy między strukturą a klasą, a zrozumienie, jaka jest prawdziwa prawdziwa przewaga programistyczna „ukrywania informacji”, jest tak mylące, że chcę ją tam umieścić.
Będę wdzięczny za to, ale w każdym razie kompilatory C ++ są tak trudne do napisania, że ten język jest jak potwór.
źródło
Chociaż każdy język ma swoje wady, żaden nie jest uciążliwy, gdy się o nich dowiesz. Z wyjątkiem tej pary:
Złożona składnia w połączeniu z trudnymi interfejsami API
Dotyczy to szczególnie języka takiego jak Objective-C. Składnia jest nie tylko złożona, ale interfejs API używa nazw funkcji takich jak:
Jestem za byciem jawnym i niejednoznacznym, ale to jest śmieszne. Za każdym razem, gdy siadam przy xcode, czuję się jak n00b, co jest naprawdę frustrujące.
źródło
tableView:cellForRowAtIndexPath:
, która moim zdaniem jest bardzo opisowa.źródło
(u)int_least8_t
.). Sygnalizacja ma idealny sens dla małych liczb całkowitych, ale nie ma żadnego sensu dla postaci.char*
... jak C-String, są naprawdę zdezorientowani.sbyte
,byte
orazchar
typów.