すべてのプログラミング言語に設計上の欠陥があるのは、他のほとんどの(すべて?)それはさておき、プログラミング言語のどの設計上の欠陥がプログラマとしてのあなたの歴史を通してあなたを最も悩ませましたか?
特定の目的のために設計されていないという理由だけで言語が「悪い」場合は、設計上の欠陥ではなく、設計の機能なので、そのような煩わしさを挙げないでください。言語がその設計目的に適していない場合、それはもちろん設計の欠陥です。実装固有のことや内部的なことも考慮されません。
すべてのプログラミング言語に設計上の欠陥があるのは、他のほとんどの(すべて?)それはさておき、プログラミング言語のどの設計上の欠陥がプログラマとしてのあなたの歴史を通してあなたを最も悩ませましたか?
特定の目的のために設計されていないという理由だけで言語が「悪い」場合は、設計上の欠陥ではなく、設計の機能なので、そのような煩わしさを挙げないでください。言語がその設計目的に適していない場合、それはもちろん設計の欠陥です。実装固有のことや内部的なことも考慮されません。
回答:
私の大きな悩みの1つは、switch
を使用するのを忘れた場合、C派生言語のケースがデフォルトで次のケースにフォールスルーする方法break
です。これは非常に低レベルのコード(例:Duff's Device)では有用ですが、通常はアプリケーションレベルのコードには不適切であり、コーディングエラーの一般的な原因であると理解しています。
1995年頃、私が初めてJavaの詳細について読んでいたときのことを思い出します。switch
声明に関する部分に到達したとき、デフォルトのフォールスルー動作を保持していたことに非常に失望しました。これは単に別の名前でswitch
栄光goto
をもたらします。
switch
そのように働く必要はありません。たとえば、Adaのcase / whenステートメント(switch / caseに相当)にはフォールスルー動作がありません。
switch
Cに関係のない言語の-like ステートメントは、そのように動作する必要はありません。あなたはCスタイルの制御フロー(使用している場合でも{
... }
、for (i = 0; i < N; ++i)
、return
、など)、言語の推論は、人々が期待するようになりますswitch
Cのような仕事に、そしてエイダ/パスカル/ BASIC-のようなセマンティクス混乱していた人たちにそれを与えます。C#は同じ理由break
でswitch
ステートメントを必要としますが、サイレントフォールスルーを禁止することでエラーを起こしにくくします。(しかし、Iいのfall;
代わりに書くことができることを望みますgoto case
。)
switch
、フォールスルーを可能にすることではありません。フォールスルーのほとんどの使用は意図的ではないということです。
私は、C派生言語での=
割り当てと==
同等性のテストの使用が本当に好きではありませんでした。混乱とエラーの可能性が高すぎます。また===
、Javascriptで始めてはいけません。
:=
割り当てと=
平等性テストの方が良かったでしょう。セマンティクスは、割り当ても値を生成する式である現在とまったく同じである可能性があります。
x<-5
)と比較します。Cプログラマーは、そのような必須の空白を許容しません:)
:=
、==
それを忘れて:
しまい、=
今日を忘れた場合に既に戻されているように通知されないためです。これに関するコンパイラの警告に感謝します...
=
とは==
、彼らは明確なシンボルだから、読書ではありません。それはあなたが正しいものを手に入れたことを確認することです。
+
Javascriptでの追加と文字列連結の両方の選択は、ひどい間違いでした。値は型付けされていないため+
、各オペランドの正確な内容に応じて、追加するか連結するかを決定するビザンチン規則になります。
$
文字列の連結など、まったく新しい演算子を導入することは最初から簡単だったでしょう。
+
強く型付けされた言語の文字列連結演算子として非常に理にかなっています。問題は、Javascriptがそれを使用しますが、強く型付けされていないことです。
+
連結は間違いなく可換ではないため、連結には意味がありません。私が懸念する限り、それは虐待です。
*
演算子を持つことはJSに悪用と見なされますか?
Javascriptはデフォルトで主要な問題に対してグローバルになり、JSLintなどを使用しないとバグの原因になることがよくあります
var
変数を宣言するときはいつでも使用してください。また、Javaがすべての型を2回宣言するように強制するので、入力が多すぎると言わないでください。
std::map<KEY, VALUE>::const_iterator
C ++でauto
その言語に追加される変数を十分に宣言しなければならないという不満がありました。
"use strict"
es5で追加されたディレクティブは、宣言されていない参照をエラーに変更します。
CおよびC ++のプリプロセッサは大がかりで、ふるいのように漏れる抽象化を作成し、ラットの#ifdef
ステートメントのネストを介してスパゲッティコードを奨励ALL_CAPS
し、その制限を回避するために恐ろしく読めない名前を必要とします。これらの問題の原因は、構文レベルまたは意味レベルではなくテキストレベルで動作することです。さまざまなユースケースのために、実際の言語機能に置き換える必要がありました。以下に例を示しますが、これらのいくつかはC ++、C99、または非公式ではあるが事実上の標準の拡張機能で解決されています。
#include
実際のモジュールシステムに置き換える必要がありました。
インライン関数とテンプレート/ジェネリックは、ほとんどの関数呼び出しのユースケースを置き換えることができます。
ある種のマニフェスト/コンパイル時定数機能を使用して、このような定数を宣言できます。 enumのDの拡張は、ここで素晴らしい働きをします。
実際の構文ツリーレベルマクロは、さまざまなユースケースを解決できます。
文字列ミックスインは、コードインジェクションのユースケースに使用できます。
static if
またはversion
条件付きコンパイルにステートメントを使用できます。
#include
問題に同意しますが、モジュールシステムが発明されました...その後!そして、CおよびCは、下位互換性の最大を目指し++:/
#include
ハッキングよりも低いか同等であることを示すことができる場合にのみ、設計エラーとして認定できます。
数百の言語で数百の間違いをリストすることもできますが、IMOは言語設計の観点からは有用な演習ではありません。
どうして?
ある言語の間違いとなるものは、別の言語の間違いではないからです。例えば:
学ぶべき教訓はありますが、教訓はめったに明確ではなく、それらを理解するには技術的なトレードオフと歴史的背景を理解する必要があります。(たとえば、ジェネリックの扱いにくいJava実装は、下位互換性を維持するためのビジネス要件をオーバーライドした結果です。)
あなたは新しい言語の設計を真剣に考えている場合はIMO、あなたが実際にする必要が使用して既存の言語の広い範囲を(および歴史的な言語を学ぶ)...と、ミスが何であるか、あなた自身の心を占めています。また、これらの各言語は、特定のニーズを満たすために、特定の歴史的背景で設計されたものであることに留意する必要があります。
学ぶべき一般的なレッスンがある場合、それらは「メタ」レベルにあります。
CおよびC ++:意味のないすべての整数型。
特にchar
。テキストですか、それとも小さな整数ですか?テキストの場合、それは「ANSI」文字ですか、UTF-8コード単位ですか。整数の場合、符号付きですか、符号なしですか?
int
「ネイティブ」サイズの整数にすることを目的としていましたが、64ビットシステムではそうではありません。
long
は、より大きくても大きくなくてもかまいませんint
。ポインタのサイズである場合とそうでない場合があります。コンパイラの作成者は、32ビットか64ビットかにかかわらず、ほとんど任意の決定を下します。
間違いなく1970年代の言語。Unicodeの前。64ビットコンピューターの前。
null
。
その発明者であるトニー・ホアは、それを「10億ドルの間違い」と呼んでいます。
60年代にALGOLで導入され、現在一般的に使用されているほとんどのプログラミング言語に存在しています。
OCamlやHaskellのような言語で使用されるより良い代替手段は、多分です。一般的な考え方は、オブジェクト参照がnull / empty / nonexistentになる可能性があることを明示的に示さない限り、オブジェクト参照をnull / empty / non-existentにすることはできないということです。
(トニーの謙虚さは素晴らしいものの、ほとんどの人が同じ間違いを犯し、たまたま彼が最初だったと思います。)
maybe
nullの例外はオプトアウトですが、nullのオプトインと考えたいです。もちろん、実行時エラーとコンパイル時エラーの違いについても言うべきことがいくつかありますが、nullが本質的に動作を注入しているという事実だけでも注目に値します。
PHPを設計した人たちは通常のキーボードを使用していなかったし、colemakキーボードさえ使用していないと感じています。
Who::in::their::right::mind::would::do::this()
?::
オペレータは、保持シフトしてから2キーの押下が必要です。なんてエネルギーの無駄。
ただし、-> this-> is-> not-> much-> better。また、2つの記号の間にシフトがある3つのキーを押す必要があります。
$last = $we.$have.$the.$dumb.'$'.$character
。ドル記号は非常に多くの回数使用され、キーボードの一番上までの賞のストレッチに加えて、シフトキーを押す必要があります。
入力するのがずっと速いキーを使用するようにPHPを設計できないのはなぜですか?なぜwe.do.this()
単一のキーを押すだけで必要なキーで変数を開始できないのか、またはまったくしない(JavaScript)で、すべての変数を事前に定義するだけです(とにかくE_STRICTで行う必要があります)!
私は遅いタイピストではありません-しかし、これは単なるデザインの選択に欠けています。
I::have::nothing::against::this->at.all()
デスクトップの使用は、asp.net内のフォームに影響を与えました。
常にファッジを感じ、ウェブが実際にどのように機能するかを邪魔しました。ありがたいことにasp.net-mvcは同じインスピレーションを受けていますが、そのインスピレーションはRubyなどに与えられています。
私にとっては、PHPの標準ライブラリには命名規則と引数の順序付け規則が絶対に欠けています。
しかしJASS NULLIFY参照への必要性参照されたオブジェクトが削除/リリースされた(または参照が漏れなり、メモリの数バイトが失われてしまう)の後には、より深刻ですが、JASSは、単一の目的言語であることから、その重要ではありません。
私が直面する最大の設計上の欠陥は、pythonがpython 3.xのように設計されていなかったことです。
Cでの配列の崩壊、したがってC ++。
delete
and delete[]
演算子を持たなければならない理由であることに注意してください。
Javaのプリミティブ型。
それらはすべてがの子孫であるという原則を破り、java.lang.Object
理論的な観点から言語仕様の追加の複雑さをもたらし、実用的な観点からはコレクションの使用を非常に退屈にします。
オートボクシングは実用的な欠点を軽減するのに役立ちましたが、仕様をさらに複雑にし、大きな太ったバナナスキンを導入することを犠牲にして、単純な算術演算のように見えるものからnullポインター例外を取得できます。
私はPerlを最もよく知っているので、それを選びます。
Perlは多くのアイデアを試しました。いくつかは良かった。いくつかは悪かった。一部はオリジナルであり、正当な理由で広くコピーされていません。
1つはコンテキストの概念です。すべての関数呼び出しはリストまたはスカラーコンテキストで行われ、各コンテキストでまったく異なることを実行できます。http://use.perl.org/~btilly/journal/36756で指摘したように、これはすべてのAPIを複雑にし、Perlコードでの微妙なデザインの問題を頻繁に引き起こします。
次は、構文とデータ型を完全に結び付けるという考え方です。これは、オブジェクトが他のデータ型になりすますことを可能にするタイの発明につながりました。(オーバーロードを使用して同じ効果を達成することもできますが、Perlではタイがより一般的なアプローチです。)
多くの言語が犯すもう1つのよくある間違いは、語彙的ではなく動的スコープを提供することから始めることです。後でこの設計上の決定を元に戻すことは難しく、長期にわたるいぼにつながります。Perlでのこれらのいぼの古典的な説明はhttp://perl.plover.com/FAQs/Namespaces.htmlです。これは、Perlがour
変数とstatic
変数を追加する前に書かれていることに注意してください。
人々は、静的タイピングと動的タイピングについて合法的に同意しません。私は個人的に動的型付けが好きです。しかし、タイプミスを捕らえるのに十分な構造を持つことが重要です。Perl 5はstrictでこれをうまくやっています。しかし、Perl 1-4はこれを間違えました。他のいくつかの言語には、strictと同じことを行うリントチェッカーがあります。あなたがリントチェックを実施することに長けていれば、それは受け入れられます。
より多くの悪いアイデア(それらの多く)を探しているなら、PHPを学び、その歴史を調べてください。私のお気に入りの過去のミス(非常に多くのセキュリティホールにつながるため、ずっと前に修正されました)は、デフォルトで、フォームパラメータを渡すことで誰でも任意の変数を設定できるようにしました。しかし、それは唯一の間違いではありません。
コードブロックとオブジェクトリテラルのJavaScriptのあいまいさ。
{a:b}
a
ラベルおよびb
式であるコードブロックです。またはa
、値を持つ属性を持つオブジェクトを定義できますb
a
。
FORTRANとホワイトスペースの非感受性に戻ります。
仕様に浸透しました。END
カードが適切に「END」と「E」、「N」、列7-72にこの順に「D」、および他のデータ有り無しではなく、カードとカードのように定義されなければなりませんでした列と他の何もない。
これにより、構文上の混乱が生じやすくなりました。 DO 100 I = 1, 10
はループ制御ステートメントDO 100 I = 1. 10
でしたが、値1.1をという変数に割り当てたステートメントでしたDO10I
。(変数は、最初の文字に応じて型を宣言せずに作成できるという事実がこれに寄与しました。)他の言語とは異なり、トークンを区別するためにスペースを使用して曖昧さをなくす方法はありませんでした。
また、他の人が本当に紛らわしいコードを書くことができました。FORTRANのこの機能が二度と繰り返されない理由があります。
(test ? a : b)
、e)主張する人々に対処しなければならないを使用**
する場合、f)は大文字と小文字を区別できません。これのほとんどは、50年代のキーパンチによるものでした。
私はDSL(ドメイン固有言語)を信じており、言語で私が大切にしていることの1つは、その上でDSLを定義できるかどうかです。
Lispにはマクロがあります-私と同じように、ほとんどの人はこれを良いことだと考えています。
CおよびC ++にはマクロがあります-人々はそれらについて文句を言いますが、私はそれらを使用してDSLを定義することができました。
Javaでは、それらは除外され(したがってC#では)、それらの欠如は美徳であると宣言されました。確かにそれはあなたがインテリセンスを持っていることを可能にしますが、私にとってはそれは単なる作品です。DSLを実行するには、手動で拡張する必要があります。それは苦痛であり、それは私がたくさんのより少ないコードでもっと多くのことをすることができるにもかかわらず、私を悪いプログラマーのように見せます。
cdr
リストのを取り、そこからラムダ閉包を形成し(つまり、継続)、それをcar
リストの引数として渡します。もちろんそれは再帰的に行われ、条件、ループ、関数呼び出しに対して「正しいことをする」でしょう。次に、「選択」機能が通常のループに変わりました。きれいではありませんが、堅牢でした。問題は、過度にネストされたループを非常に簡単に作成できることです。
ステートメントを含むすべての言語のステートメント。式ではできないことは何もせず、多くのことを実行できません。?:
三項演算子の存在は、それらを回避しようとすることの1つの例にすぎません。JavaScriptでは、特に面倒です。
// With statements:
node.listen(function(arg) {
var result;
if (arg) {
result = 'yes';
} else {
result = 'no';
}
return result;
})
// Without:
node.listen(function(arg) if (arg) 'yes' else 'no')
unit
タイプ(別名()
)の概念を持つ言語でさえ、警告を投げたり、奇妙な振る舞いをしないように特別な配慮が必要です。
私にとっては、Cから派生したすべての言語を悩ませているのは設計上の問題です。つまり、「宙ぶらりんのその他」。この文法上の問題はC ++で解決されるはずでしたが、JavaとC#にも引き継がれました。
これまでのすべての答えは、多くの主流言語の単一の失敗を指していると思います。
下位互換性に影響を与えずにコア言語を変更する方法はありません。
これが解決すれば、他のほとんどすべてのグリップを解決できます。
編集。
これはライブラリで異なる名前空間を使用することで解決でき、言語のほとんどのコアで同様のことを行うことができますが、複数のコンパイラ/インタープリターをサポートする必要があるかもしれません。
最終的に、完全に満足できる方法でそれを解決する方法を知っているとは思わないが、それは解決策が存在しない、またはそれ以上はできないことを意味しない
Javaのサイレント整数算術オーバーフロー
JavaとC#の両方には、ジェネリックを追加する際に後方互換性を維持したいため、型システムに迷惑な問題があります。Javaはジェネリックと配列を混在させることを好みません。C#では、値型を境界として使用できないため、いくつかの有用な署名は許可されません。
後者の例として、
public static T Parse <T>(Type <T> type、string str)ここで、T:Enum一緒にまたは交換
public static object Parse(Type type、string str)で
Enum
クラスが可能になりますMyEnum e = Enum.Parse(typeof(MyEnum)、str);トートロジーではなく
MyEnum e =(MyEnum)Enum.Parse(typeof(MyEnum)、str);
tl; dr:バージョン1を公開した後ではなく、型システムの設計を開始するときに、パラメトリック多態性について考えてください。
MyMethod<T>(T value) where T : struct, IComparable, IFormattable, IConvertible
、列挙型をテストする必要があり、それはハックです。C#ジェネリックの大きな欠如は、より高い種類をサポートしていないことだと思います。
私は炎上するために自分自身を開放しているように感じますが、C ++で参照によってプレーンな古いデータ型を渡す機能を本当に嫌います。参照によって複雑な型を渡すことができるのは少し嫌いです。関数を見ている場合:
void foo()
{
int a = 8;
bar(a);
}
呼び出しポイントから、bar
完全に異なるファイルで定義される可能性のあるを示す方法はありません。
void bar(int& a)
{
a++;
}
このようなことをすることは単に悪いソフトウェア設計であり、言語を責めることではないと主張する人もいるかもしれません。ポインターを使用して呼び出す
bar(&a);
より読みやすくなりました。
COBOLを学んだとき、ALTERステートメントはまだ標準の一部でした。一言で言えば、このステートメントを使用すると、実行時にプロシージャ呼び出しを変更できます。
危険なのは、めったにアクセスされず、プログラムの残りの部分のフローを完全に変更する可能性のあるコードの不明瞭なセクションにこのステートメントを配置できることです。複数のALTERステートメントを使用すると、いつでもプログラムが何をしていたかを知ることがほぼ不可能になります。
私の大学の教官は、非常に強調して、私たちのプログラムのいずれかでその声明を見たなら、彼は自動的に私たちをflしたと述べました。
v() { if (not alreadyCalculatedResult) { result = long(operation); alreadyCalculatedResult = true; } result; }
あなたが言うv() { result = long(operation); v = () => result; result; }
プログラミング言語の最悪の罪は、明確に定義されていません。私が覚えているケースはC ++で、その起源は次のとおりです。
私が覚えているように、C ++をCと同じくらい専門的に信頼できるものにするために十分に定義されたC ++を得るには、約10年かかりました。
私が罪と考える他の何か(それは別の答えになるでしょうか?)は、いくつかの一般的なタスクを行うための複数の「最良の」方法を持っています。(これも)C ++、Perl、Rubyの場合です。
C ++のクラスは、言語のある種の強制設計パターンです。
実行時に構造体とクラスに違いは実際上ありません。「情報隠蔽」の本当のプログラミングの利点が何であるかを理解するのは非常にわかりにくいので、そこに配置します。
私はそのことに賛成するつもりですが、とにかく、C ++コンパイラはこの言語を書くのがとても難しいので、モンスターのように感じます。
すべての言語には欠点がありますが、一度知ってしまえば迷惑なものはありません。このペアを除く:
複雑な構文と冗長なAPI
これは、Objective-Cのような言語に特に当てはまります。構文が非常に複雑であるだけでなく、APIは次のような関数名を使用します。
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
私は明確で曖昧さを感じないようにしていますが、これはばかげています。Xcodeを使用するたびに、n00bのように感じますが、それは本当にイライラします。
tableView:cellForRowAtIndexPath:
、これは私の意見では非常に記述的です。
(u)int_least8_t
)の概念を混同していることです。符号付きは小さな整数には完全に意味がありますが、文字にはまったく意味がありません。
char*
、Cストリングのようにキャストする必要があると初心者に説明すると、本当に混乱します。
sbyte
、byte
とchar
種類。