プログラミング言語で直面した最大の設計上の欠陥は何ですか?[閉まっている]


29

すべてのプログラミング言語に設計上の欠陥があるのは、他のほとんどの(すべて?)それはさておき、プログラミング言語のどの設計上の欠陥がプログラマとしてのあなたの歴史を通してあなたを最も悩ませましたか?

特定の目的のために設計されていないという理由だけで言語が「悪い」場合は、設計上の欠陥ではなく、設計の機能なので、そのような煩わしさを挙げないでください。言語がその設計目的に適していない場合、それはもちろん設計の欠陥です。実装固有のことや内部的なことも考慮されません。


6
誰かがこの質問がどれだけ建設的であるかを質問したい場合、これは言語設計者にとって建設的であることに注意してください(避けるべき間違い)。
アント


1
@greyfade:実際には、これは実際の言語の欠陥に関するものではありません。これは、言語の採用を低下させるものに関するものと思われます。いくつかの回答には悪い構文などが記載されていますが、それは特定の設計上の欠陥ではありません
アント

8
プログラミング言語で最大の欠陥はありますか?人間。
ジョエルイーサートン

これらの答えが最大の欠点であれば、プログラミング言語に感銘を受けます。
トムホーティン-タックライン

回答:


42

私の大きな悩みの1つは、switchを使用するのを忘れた場合、C派生言語のケースがデフォルトで次のケースにフォールスルーする方法breakです。これは非常に低レベルのコード(例:Duff's Device)では有用ですが、通常はアプリケーションレベルのコードには不適切であり、コーディングエラーの一般的な原因であると理解しています。

1995年頃、私が初めてJavaの詳細について読んでいたときのことを思い出します。switch声明に関する部分に到達したとき、デフォルトのフォールスルー動作を保持していたことに非常に失望しました。これは単に別の名前でswitch栄光gotoをもたらします。


3
@Christopher Mahan:switchそのように働く必要はありません。たとえば、Adaのcase / whenステートメント(switch / caseに相当)にはフォールスルー動作がありません。
グレッグヒューギル

2
@Greg:switchCに関係のない言語の-like ステートメントは、そのように動作する必要はありません。あなたはCスタイルの制御フロー(使用している場合でも{... }for (i = 0; i < N; ++i)return、など)、言語の推論は、人々が期待するようになりますswitchCのような仕事に、そしてエイダ/パスカル/ BASIC-のようなセマンティクス混乱していた人たちにそれを与えます。C#は同じ理由breakswitchステートメントを必要としますが、サイレントフォールスルーを禁止することでエラーを起こしにくくします。(しかし、Iいのfall;代わりに書くことができることを望みますgoto case。)
dan04

4
その後、スイッチを記述せず、if()を記述します。あなたが不平を言っている理由は、スイッチを改善する理由です:それは真/偽の条件ではなく、数値の条件であり、それが違います。ただし、独自のスイッチ関数を作成することもできます。
jokoon

9
-1フォールスルーを許可することは有益であると考えています。愚かさ以外に危険はありませんが、追加機能を付与します。
11

11
問題はswitch フォールスルーを可能にすることではありません。フォールスルーのほとんどの使用は意図的ではないということです。
dan04

41

私は、C派生言語での=割り当てと==同等性のテストの使用が本当に好きではありませんでした。混乱とエラーの可能性が高すぎます。また===、Javascriptで始めてはいけません。

:=割り当てと=平等性テストの方が良かったでしょう。セマンティクスは、割り当ても値を生成する式である現在とまったく同じである可能性があります。


3
@Nemanja Trifunovic:私は元々その提案を考えていましたが、Cにはあいまいなあいまいさがあり、負の数(つまりx<-5)と比較します。Cプログラマーは、そのような必須の空白を許容しません:)
グレッグヒューギル

7
@Greg:私が好むのは:===それを忘れて:しまい、=今日を忘れた場合に既に戻されているように通知されないためです。これに関するコンパイラの警告に感謝します...
Matthieu M.

13
私が今まで使用したほとんどすべてのキーボードでは、「:=」を入力するときにShiftキーを変更する必要があります。私が今使用しているものでは、「:」は大文字で、「=」は小文字で、逆になっています。私はたくさんの課題を入力しますが、そのような入力の面倒は必要ありません。
デヴィッドソーンリー

14
@David Thornley:コードは書かれているよりも何度も読まれます。「面倒なタイピング」についての議論は一切買わない。
グレッグヒューギル

8
@Greg Hewgill:書かれているよりも頻繁に読まれていることを確認してください。しかし、間の問題=とは==、彼らは明確なシンボルだから、読書ではありません。それはあなたが正しいものを手に入れたことを確認することです。
デビッドソーンリー

29

+Javascriptでの追加文字列連結の両方の選択は、ひどい間違いでした。値は型付けされていないため+、各オペランドの正確な内容に応じて、追加するか連結するかを決定するビザンチン規則になります。

$文字列の連結など、まったく新しい演算子を導入することは最初から簡単だったでしょう。


13
@バリー:そうでもない。 +強く型付けされた言語の文字列連結演算子として非常に理にかなっています。問題は、Javascriptがそれを使用しますが、強く型付けされていないことです。
メイソンウィーラー

13
@Mason:+連結は間違いなく可換ではないため、連結には意味がありません。私が懸念する限り、それは虐待です。
マチューM.

12
@Matthieu:うーん...なぜそれが重要なのですか?連結は(加算と同様に)可換ではありませんが、2つの文字列の加算は無意味なので、だれもそのように考えません。あなたは何も存在しない問題を発明しています。
メイソンウィーラー

4
C ++に切り替えて、任意の種類の難解なオーバーロードに任意の種類を選択した演算子に追加する
ニュートピア

8
@Matthieu:可換性は問題ではありません。JSにMatrixクラスがある場合、*演算子を持つことはJSに悪用と見なされますか?
dan04

24

Javascriptはデフォルトで主要な問題に対してグローバルになり、JSLintなどを使用しないとバグの原因になることがよくあります


2
デフォルトはグローバルですか?私はJSを使用しないことをうれしく思います
アント

5
実際、非常に優れた言語であり、いくつかの悪い設計上の選択肢があります。これは主要なものです。変数のスコープを設定しない場合、グローバルとしてスコープします。良いことは、Doug CrockfordのJslintプログラムを使用することで、このエラーをキャッチでき、さらに多くのことができるということです。
ザカリーK

1
var変数を宣言するときはいつでも使用してください。また、Javaがすべての型を2回宣言するように強制するので、入力が多すぎると言わないでください。
davidk01

3
@ davidk01:人々はそれについて文句を言います。同様に、std::map<KEY, VALUE>::const_iteratorC ++でautoその言語に追加される変数を十分に宣言しなければならないという不満がありました。
dan04

1
"use strict"es5で追加されたディレクティブは、宣言されていない参照をエラーに変更します。
ショーンマクミラン

22

CおよびC ++のプリプロセッサは大がかりで、ふるいのように漏れる抽象化を作成し、ラットの#ifdefステートメントのネストを介してスパゲッティコードを奨励ALL_CAPSし、その制限を回避するために恐ろしく読めない名前を必要とします。これらの問題の原因は、構文レベルまたは意味レベルではなくテキストレベルで動作することです。さまざまなユースケースのために、実際の言語機能に置き換える必要がありました。以下に例を示しますが、これらのいくつかはC ++、C99、または非公式ではあるが事実上の標準の拡張機能で解決されています。

  • #include 実際のモジュールシステムに置き換える必要がありました。

  • インライン関数とテンプレート/ジェネリックは、ほとんどの関数呼び出しのユースケースを置き換えることができます。

  • ある種のマニフェスト/コンパイル時定数機能を使用して、このような定数を宣言できます。 enumのDの拡張は、ここで素晴らしい働きをします。

  • 実際の構文ツリーレベルマクロは、さまざまなユースケースを解決できます。

  • 文字列ミックスインは、コードインジェクションのユースケースに使用できます。

  • static ifまたはversion条件付きコンパイルにステートメントを使用できます。


2
@dsimcha:私はこの#include問題に同意しますが、モジュールシステムが発明されました...その後!そして、CおよびCは、下位互換性の最大を目指し++:/
マシューM.

@Matthieu:はい、モジュールシステムは後から考案されましたが、ここではとにかく後知恵について話しています。
-dsimcha

3
私はこれがさまざまな身体部分の大きな痛みであることに同意しますが、マルチパス設計には単純さの利点があります:Cコンパイラは、Cコードのチャンクを正常にコンパイルする前に操作のコンテキストについて多くを知る必要はありません。これは、C言語自体(C ++に似たクラスなど)内で仮想モジュールシステムを使用するコストが常に現在のcppベースの#includeハッキングよりも低いか同等であることを示すことができる場合にのみ、設計エラーとして認定できます。
reinierpost

同意する。ただし、一部の人々は、前処理は良いことだと考えており、(たとえば)Javaでサポートされていないことを訴えています。
スティーブンC

5
@Stephen:プリプロセッサのあるJavaは、ない場合よりも優れている可能性があることに同意しますが、それは、 Javaにプリプロセッサを置き換えるために必要な「実際の」機能がいくつかないためです。このような機能を含むDのような言語や、動的であることにより他の方法で柔軟性を得るPythonでは、私はそれを少しも見逃していません。
dsimcha

21

数百の言語で数百の間違いをリストすることもできますが、IMOは言語設計の観点からは有用な演習ではありません。

どうして?

ある言語の間違いとなるものは、別の言語の間違いではないからです。例えば:

  • Cをマネージ(つまり、ガベージコレクション)言語にするか、プリミティブ型を拘束すると、セミポータブルな低レベル言語としての有用性が制限されます。
  • Cスタイルのメモリ管理をJavaに追加すると(たとえば、パフォーマンスの問題に対処するために)壊れてしまいます。

学ぶべき教訓ありますが、教訓はめったに明確ではなく、それらを理解するには技術的なトレードオフと歴史的背景を理解する必要があります。(たとえば、ジェネリックの扱いにくいJava実装は、下位互換性を維持するためのビジネス要件オーバーライドした結果です。)

あなたは新しい言語の設計を真剣に考えている場合はIMO、あなたが実際にする必要が使用して既存の言語の広い範囲を(および歴史的な言語を学ぶ)...と、ミスが何であるか、あなた自身の心を占めています。また、これらの各言語は、特定のニーズを満たすために、特定の歴史的背景で設計されたものであることに留意する必要があります。

学ぶべき一般的なレッスンがある場合、それらは「メタ」レベルにあります。

  • すべての目的に理想的なプログラミング言語を設計することはできません。
  • 間違いを避けることはできません...特に後から見た場合。
  • 多くの間違いは修正するのが苦痛です...あなたの言語のユーザーにとって。
  • ターゲットオーディエンスの背景とスキルを考慮する必要があります。すなわち、既存のプログラマー。
  • みんなを喜ばせることはできません。

9
-1-プログラミング言語は設計目標に従います。これらの目標に対して機能する言語の機能は、他の目標の1つの妥協点でない限り、設計上の欠陥です。誰もが満足することを意図して作られた言語は多くありませんが、すべての言語は、そもそも満足することを目指している人々を満足させることを試みるべきです。この種のポストモダニズムの政治的正しさは、プログラミング言語の研究開発にとって非常に息苦しいものです。
宮坂

私のポイントは、設計目標を考慮せずにレッスンを学ぶことは難しいということです。
スティーブンC

1
私はOPが質問の2番目の段落であなたの答えをすでに説明したように思えます。
エイダンカリー

20

CおよびC ++意味のないすべての整数型。

特にchar。テキストですか、それとも小さな整数ですか?テキストの場合、それは「ANSI」文字ですか、UTF-8コード単位ですか。整数の場合、符号付きですか、符号なしですか?

int 「ネイティブ」サイズの整数にすることを目的としていましたが、64ビットシステムではそうではありません。

longは、より大きくても大きくなくてもかまいませんint。ポインタのサイズである場合とそうでない場合があります。コンパイラの作成者は、32ビットか64ビットかにかかわらず、ほとんど任意の決定を下します。

間違いなく1970年代の言語。Unicodeの前。64ビットコンピューターの前。


10
Cは標準の万能言語として設計されていないため、設計エラーにはなりません。CPU固有のアセンブラコードを回避するポータブルアセンブラとしてCPUをモデル化するように設計されました。ただし、Javaでは修正されました。

7
大きな問題は、それがひどい考えだという歴史を示しているにもかかわらず、同じ意味のない用語を使い続ける新しい言語だと思います。少なくともCの人たちは間違いに気付き、標準のint型を作成しました。
マークH

5
charはutf-8ユニットではありません。utf-8文字は、保存するのに8ビット以上かかる場合があります。Cは1970年代の言語ではなく、現在(自発的に)プロジェクトに使用しています。
dan_waterworth

4
Cは、PDP-11プロセッサの高レベルの抽象化にすぎません。たとえば、事前および事後増分はPDP-11によって直接サポートされていました。
ビットツイダー

5
これはひどく見当違いの答えです。まず、CとC ++は互換性がありません。2番目に、言語仕様はcharが何であるかを明確に定義します-char 型として宣言されたオブジェクトは、基本実行文字セットのメンバーを格納するのに十分な大きさです。。第三に、Cは「70年代の言語」ではなく、ハードウェアに近い言語であり、おそらく最終的にすべての高レベルの抽象化を実際にCPUに理解させる言語です。あなたは、高レベルの言語のみを知っていて、物事が実際にどのように機能するのかを理解していない人として出てきます。-1
エドS.

18

null

その発明者であるトニー・ホアは、それを「10億ドルの間違い」と呼んでます。

60年代にALGOLで導入され、現在一般的に使用されているほとんどのプログラミング言語に存在しています。

OCamlやHaskellのような言語で使用されるより良い代替手段は、多分です。一般的な考え方は、オブジェクト参照がnull / empty / nonexistentになる可能性があることを明示的に示さない限り、オブジェクト参照をnull / empty / non-existentにすることはできないということです。

(トニーの謙虚さは素晴らしいものの、ほとんどの人が同じ間違いを犯し、たまたま彼が最初だったと思います。)


発明者とさえ意見が違う!!! nullは、ポインター/参照データ型の空の値です。文字列には空の文字列、セットには空のセット(Pascal empty set = [])、整数には0があります。null/ nil /何でも使用するほとんどのプログラミング言語は、変数が適切に割り当てられた場合、nullエラーを防ぐことができます。
umlcat

4
@ user14579:あらゆる種類のセットまたは文字列または配列をサポートするすべての言語には{}がありますが、これらは意味論的に適切であり、配列境界エラーを引き起こす可能性のあるものが既にない限りクラッシュしませんが、それは別の問題です。空の文字列を処理してすべての文字を大文字にすると、空の文字列になります。null文字列に対して同じことをしようとすると、適切な考慮なしにクラッシュします。問題は、この適切な考慮が面倒であり、しばしば忘れられ、単一式関数(ラムダ)を書くことが難しくなることです。
宮坂

1
nullを入力するたびにお金を稼ぐ... nullを入力するたびに誰かがお金を失います。今回を除いて。
ケブピー

3
@umlcat-Obeml、Haskell、F#のようなパターンマッチングの言語がある場合、Maybe x | Noneパターンは、コンパイル時にヌルケースを忘れないようにします。nullが確立されたイディオムである言語では、コンパイル時のトリックはどれもエラーをキャッチできません。MaybeおよびSomeモナドを持つ言語では、nullケースを処理しないことを明示的に選択する必要があるため、「null」アプローチよりも深刻な利点があります。
ジェイソンTrue

1
@Jason- maybenullの例外はオプトアウトですが、nullのオプトインと考えたいです。もちろん、実行時エラーとコンパイル時エラーの違いについても言うべきことがいくつかありますが、nullが本質的に動作を注入しているという事実だけでも注目に値します。
宮坂

14

PHPを設計した人たちは通常のキーボードを使用していなかったし、colemakキーボードさえ使用していないと感じています。

私はPHP開発者です。PHPは入力するのが楽しくありません。

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で行う必要があります)!

私は遅いタイピストではありません-しかし、これは単なるデザインの選択に欠けています。


Perlはこの痛みを共有しています。
デニス

2
C ++もそうであり、不可解な奇妙な理由でPowerShellもそうです。
宮坂

2
より強力なエディターを使用して、これらのオペレーター用に独自のキーシーケンスを定義します。
ケビンクライン

1
I::have::nothing::against::this->at.all()
Mateen Ulhaq

1
たぶん、彼らはqwertyキーボードを使用していません。$および:キーボードのようなすべてのazertyでShiftキーを押す必要はありません。
アルク

13

デスクトップの使用は、asp.net内のフォームに影響を与えました。

常にファッジを感じ、ウェブが実際にどのように機能するかを邪魔しました。ありがたいことにasp.net-mvcは同じインスピレーションを受けていますが、そのインスピレーションはRubyなどに与えられています。


18
それは図書館のものではありませんか?
ニキエ

@nikieそれは良い点です;)

1
@nikie実際、XMLベースのASPXコードは言語であるため、このコードをひねって動作させることができます。:D
CodexArcanum

2
ASP.NETの本当の問題は、プログラマーからWebの詳細を隠そうとすることです。実際には、ASP.NETには本当に便利で便利なものがいくつかありますが、それを達成するには、一生懸命戦い、深く掘り下げる必要があります。
CodexArcanum

1
一方、「クラシックな」デスクトップアプリを使用して、数千ものシンプルで成功したデータ収集アプリがあります。唯一の悪い点は、MVCまではMicrosoftの唯一の選択肢はWindowsフォームだったことです。
-ElGringoGrande

13

私にとっては、PHPの標準ライブラリには命名規則と引数の順序付け規則が絶対に欠けています。

しかしJASS NULLIFY参照への必要性参照されたオブジェクトが削除/リリースされた(または参照が漏れなり、メモリの数バイトが失われてしまう)の後には、より深刻ですが、JASSは、単一の目的言語であることから、その重要ではありません。


9
PHPのstdlibに規約がないことは、言語設計の欠陥ではなく、議論の余地があります

3
@delnan:規約の欠如は、PHPがどのように設計された結果であり、したがって言語設計に多くの関係があります。また、ライブラリと言語の間に明確な区別があるかどうかも明確ではありません。特にLispには、ある言語を別の言語の上にブートストラップするという誇り高い伝統があります。
-btilly

1
JASSの本当に注目すべき点は、ハンドルを参照カウントしていることですが、手動で破棄しない限りクリーンアップしないことです(そして、グラフィックインターフェイスはメモリをどこでもリークする関数を作成しました)!
クレイグギドニー

13

私が直面する最大の設計上の欠陥は、pythonがpython 3.xのように設計されていなかったことです。


1
さて、グイドでさえすべてを一度に正しくすることはできません

5
@delnan、ああ、私は知っています、Python <3はまだ驚くほど良い言語ですが、Python 3.xの形でより良い言語を持っているのは少し迷惑です私は欲しい。
dan_waterworth

Python 3.xモジュールのロビーに進みます!その間、私は2.5.4で書き続けます。SOのおかげで、3.xが正常に機能していることを思い出しました。
ケブピー

@kevpie最初に、ロビーメンタルコンパイルをPythonに追加して、ライブラリメンテナーの移行を容易にします。2to3は長期的には保守可能なソリューションではありません。
エヴァンプライス

12

Cでの配列の崩壊、したがってC ++。


適切な配列のサポートも希望します。C ++では、abscons構文とテンプレートを使用して減衰を防ぐことができますが、ハックのようなものです:/
Matthieu M.

1
これが、C ++が個別のdeleteand delete[]演算子を持たなければならない理由であることに注意してください。
dan04

配列をいつでも構造体に入れて、必要に応じて値で渡すことができますが、通常は元の問題よりも厄介です。C ++では、通常、配列を使用する必要を回避できます。
デビッドソーンリー

2
少なくともCの場合、適切な配列サポートに対する引数は、特にCポインター演算の動作方法を考えると、「配列境界のチェックは高価です」です。
スティーブンC

@Stephen C:配列の減衰に関係する配列境界チェックは何ですか?
ネマンジャトリフノヴィッチ

11

Javaのプリミティブ型。

それらはすべてがの子孫であるという原則を破り、java.lang.Object理論的な観点から言語仕様の追加の複雑さをもたらし、実用的な観点からはコレクションの使用を非常に退屈にします。

オートボクシングは実用的な欠点を軽減するのに役立ちましたが、仕様をさらに複雑にし、大きな太ったバナナスキンを導入することを犠牲にして、単純な算術演算のように見えるものからnullポインター例外を取得できます。


プリミティブ型を削除すると、ひどいパフォーマンスの問題が発生します。また、オートボクシングは非常に簡単に台無しになる可能性があるため、何も改善されません。
deadalnix

当時、これはJavaデザイナーによる賢明な決定でした。90年代に利用可能なマシン/ VMのパフォーマンスの向上は、java.lang.Objectの周りのすべてを統合するという概念的な利点を上回りました。
-mikera

10

私は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を学び、その歴史を調べてください。私のお気に入りの過去のミス(非常に多くのセキュリティホールにつながるため、ずっと前に修正されました)は、デフォルトで、フォームパラメータを渡すことで誰でも任意の変数を設定できるようにしました。しかし、それは唯一の間違いではありません。


5
そうです、Perlには多くの間違いがあります。なぜなら、Perlを作成した人々は新しいアイデアを試していたからです。(Perlには非常に優れたものもあり、他の誰もがコピーしたと思われる正規表現の標準です)
ザカリーK

@ zachary-k:絶対に同意しました。そして、問題を詳しく説明する前にそれを明確にしようとしました。
-btilly

4
Lispはもともと動的にスコープされていましたが、時間が経つにつれて字句的にスコープされるようになりました(少なくともSchemeとCommon Lispでは)。変更することは不可能ではありません。
デヴィッド

4
@ david-thornley:どこかで後方互換性を犠牲にしない限り不可能です。スキームは常にレキシカルスコープでした。Common Lispは、標準化されたときから語彙的にスコープされていましたが、さまざまなLispコミュニティがそれを採用するのに苦労していました。また、Emacs Lispは、長い間変更したいという要望があったにもかかわらず、まだ動的スコープを使用しています。
btilly

1
ところで、人々がPerlを嫌うものの多くはPerlで発明されたのではなく、他の言語、主にBourneシェルから取られました。
-reinierpost

10

コードブロックとオブジェクトリテラルのJavaScriptのあいまいさ。

  {a:b}

aラベルおよびb式であるコードブロックです。またはa、値を持つ属性を持つオブジェクトを定義できますb


私は実際にJavaScriptについてこれが好きです。言語構造の単純さは素晴らしく、開発者が何をしているのかを開発者が知っていれば、目的は明白です。
Xeoncross

2
Xeoncross:私は一般的にあいまいさが嫌いです。この場合、開発者には明らかですが、eval()には追加の括弧が必要です。
-user281377

2
@ammoQ:それは明らかですか?それは何ですか?オブジェクトまたはコードブロック?
コンフィギュレー

configurator:明らかにオブジェクト。正気な人は、というラベルを使用しませんa
user281377

10

FORTRANとホワイトスペースの非感受性に戻ります。

仕様に浸透しました。ENDカードが適切に「END」と「E」、「N」、列7-72にこの順に「D」、および他のデータ有り無しではなく、カードとカードのように定義されなければなりませんでした列と他の何もない。

これにより、構文上の混乱が生じやすくなりました。 DO 100 I = 1, 10はループ制御ステートメントDO 100 I = 1. 10でしたが、値1.1をという変数に割り当てたステートメントでしたDO10I。(変数は、最初の文字に応じて型を宣言せずに作成できるという事実がこれに寄与しました。)他の言語とは異なり、トークンを区別するためにスペースを使用して曖昧さをなくす方法はありませんでした。

また、他の人が本当に紛らわしいコードを書くことができました。FORTRANのこの機能が二度と繰り返されない理由があります。


1
私はクラッシュする一つの小さな小さな宇宙船を引き起こしたことを意味する-あなたは最悪の例だリテラルを再定義することができ言語で
マーティンベケット

早い段階で、DIMENSIONの代わりにDAMNATIONを書くことができ、それは機能します。
マイクダンラベイ

CS以外の人々からまだ教えられています。私はまだa)宣言と戦っている、b)空白と戦っている、c)「継続行」のように、d)6文字の名前を使う、または4)見たときに困惑している(test ? a : b)、e)主張する人々に対処しなければならないを使用**する場合、f)は大文字と小文字を区別できません。これのほとんどは、50年代のキーパンチによるものでした。
マイクダンラベイ

1
@Martin Beckett-FORTRANでのリテラルの再定義は、実際には言語機能というよりもコンパイラの欠陥でした。確かに意図的な言語機能ではありませんでした。
スティーブンC

1
@oosterwal:確かにやりました。私は間違っているかもしれませんが、パンチカードに基づいた言語定義を漠然と覚えています。それらは当時FORTRANプログラムを入力する主な方法であり、列73-80が予約されている80列の行のアイデアはパンチカードからのものです。
デビッドソーンリー

9

BASICの最大の問題の1つは、初期環境を超えて言語を拡張するための明確に定義された方法がないことであり、完全に互換性のない実装が多数発生します(そして、標準化における事後的な試みはほとんど無関係です)。

ほぼすべての言語は、狂ったプログラマーによって汎用的に使用されます。クレイジーなアイデアが生まれた場合に備えて、最初にその汎用的な使用法を計画することをお勧めします。


2
+1:適切なモジュールとライブラリのない言語は、発生を待っている間違いです。COBOLもこの問題に悩まされ、互換性のない独特なバリアントを生み出しました。
-S.Lott

8

私はDSL(ドメイン固有言語)を信じており、言語で私が大切にしていることの1つは、その上でDSLを定義できるかどうかです。

Lispにはマクロがあります-私と同じように、ほとんどの人はこれを良いことだと考えています。

CおよびC ++にはマクロがあります-人々はそれらについて文句を言いますが、私はそれらを使用してDSLを定義することができました。

Javaでは、それらは除外され(したがってC#では)、それらの欠如は美徳であると宣言されました。確かにそれはあなたがインテリセンスを持っていることを可能にしますが、私にとってはそれは単なる作品です。DSLを実行するには、手動で拡張する必要があります。それは苦痛であり、それは私がたくさんのより少ないコードでもっと多くのことをすることができるにもかかわらず、私を悪いプログラマーのように見せます。


4
まともなマクロのない言語は、修正不可能な巨大な設計上の欠陥であることに同意します。しかし、「彼らは取り残された」とはどういう意味ですか?Cプリプロセッサは、まともなマクロシステムではありませんでした。Javaは、マクロを含む適切な言語から派生したものではありません。
SKロジック

1
外部のマクロ処理言語(たとえば、無数のm4など)でDSLを書くことができます。
ちょうど私の正しい意見

4
@SK:CプリプロセッサがLisp(例えば)と比較してまともなマクロシステムであるとは言いません。しかし、何にも比べて、非常に便利です。
マイクダンラベイ

4
@reinierpost:差分実行やバックトラックなどの制御構造を導入するなど、Lispでできることを考えています。これらは、Lispマクロを使用して実行できます。C / C ++では、Cマクロ(およびプログラマの訓練)を使用して差分実行を行うことができましたが、バックトラックはできませんでした。C#ではどちらもできません。代わりに得られるのは、インテリセンスのようなものです。BFD。
マイクダンラヴィー

1
@David:私がやった方法は、ステートメントのリストなどの通常のコードをラップするマクロがあったことです。cdrリストのを取り、そこからラムダ閉包を形成し(つまり、継続)、それをcarリストの引数として渡します。もちろんそれは再帰的に行われ、条件、ループ、関数呼び出しに対して「正しいことをする」でしょう。次に、「選択」機能が通常のループに変わりました。きれいではありませんが、堅牢でした。問題は、過度にネストされたループを非常に簡単に作成できることです。
マイクダンラベイ

7

ステートメントを含むすべての言語のステートメント。式ではできないことは何もせず、多くのことを実行できません。?:三項演算子の存在は、それらを回避しようとすることの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')

私はここで混乱しています:物事を行うためのより簡単な方法が必要ですか?
-TheLQ

2
正しい。すべての表現。
11年

1
Lispはこれに適しています。
デビッドソーンリー

1
@ SK-logic:文は、FORTRAN、ALGOL、およびCOBOLを介して、機械語から盲目的に継承されたと思われます。
デビッドソーンリー

1
機械語は共通の祖先であると確信しています。これは、フォンノイマンアーキテクチャに基づく現代のコンピューターが命令を順次実行し、状態を変更するという事実を反映しているだけです。最終的にIOが発生すると、意味のあるデータを生成しない式が存在するため、一部のコードに副作用のみがあることを意味的に示すのに文がまったく役に立たないわけではありません。ステートメントではなくunitタイプ(別名())の概念を持つ言語でさえ、警告を投げたり、奇妙な振る舞いをしないように特別な配慮が必要です。
宮坂

6

私にとっては、Cから派生したすべての言語を悩ませているのは設計上の問題です。つまり、「宙ぶらりんのその他」。この文法上の問題はC ++で解決されるはずでしたが、JavaとC#にも引き継がれました。


3
C ++の中核目標の1つは、(一度に考えられていたこと、または少なくとも)彼らは大幅にそれが行ったように引っ掛かっていない可能性がありセマンティック動作を変更した場合C.との完全な下位互換性があることだった
エドS.

2
@Ed S.、しかし、「ぶら下がりelse」問題の解消は、<compound_statement>(別名<block>)文法の生成を削除し、中括弧を条件付きおよび反復制御構造に組み込むことで達成できたかもしれません。 try / catch例外処理制御構造が追加されました。JavaとC#のこの文法的なあいまいさを修正しない理由はありません。現在、この文法的なあいまいさの回避策は、条件付きまたは反復制御ステートメントに続くすべてのステートメントを複合ステートメントにすることです。
ビットツイダー

1
それはどういう意味ですか?これは「文法上の問題」ではありません(完全に明確です)。どのように「解決」しますか?実際、Cのルールは満足できるものです。おそらく、Pythonに似た構文(=意味のあるインデント)のみがこの問題を実際に解決できます。さらに、現代の言語ではブレースが必須ではないことを実際に非常に嬉しく思ってます。私はすべてのCのような構文がひどいことに同意しますが、ぶら下がり-その他はそれらの問題の最小です。
コンラッドルドルフ

1
つづき:Pythonがステートメントリストを線引きする手段としてのインデントの使用は、信じられないほど奇妙だと思います。この手法は、語彙スキャンを構文解析と密接に結合することにより、「関心の分離」の原則に違反します。文脈自由文法は、ソースのレイアウトについて何も知らなくても解析できるはずです。
ビットツイダー

3
@ bit-twiddler:いいえ、ありません。Python lexerは、空白を適切なINDENTおよびDEDENTトークンに変換するだけです。それが完了すると、Pythonにはかなり従来の文法があります(docs.python.org/reference/grammar.html)。
dan04

6

これまでのすべての答えは、多くの主流言語の単一の失敗を指していると思います。

下位互換性に影響を与えずにコア言語を変更する方法はありません。

これが解決すれば、他のほとんどすべてのグリップを解決できます。

編集。

これはライブラリで異なる名前空間を使用することで解決でき、言語のほとんどのコアで同様のことを行うことができますが、複数のコンパイラ/インタープリターをサポートする必要があるかもしれません。

最終的に、完全に満足できる方法でそれを解決する方法を知っているとは思わないが、それは解決策が存在しない、またはそれ以上はできないことを意味しない


1
これをどのように「解決」しますか?
Bjarkeフロイント・ハンセン

私は確かにそれは完全に解決することができないよ-私は限りそれが行くことができるようにこれをプッシュしようと思うので、明らかに役立ちますコア言語のうち、標準ライブラリで物事を保つ
JK。

1
下位互換性とは、新しいコンパイラーが古いコードをコンパイルできることを意味しますか?
宮坂

私はそれの一部になり、新しいコンパイラがコンパイルに失敗し、古いコードの意味を変更するべきではありません意味
JK。

ほとんどの場合、特定の機能が存在しない、または変更したい場合、人々は以前の機能に基づいて新しい言語を作成します。Cのクラス=> C ++
umlcat


4

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を公開した後ではなく、型システムの設計を開始するときに、パラメトリック多態性について考えてください。


2
型を列挙型に制限できないことはC#では厄介ですが、このように回避することはできますがMyMethod<T>(T value) where T : struct, IComparable, IFormattable, IConvertible 、列挙型をテストする必要があり、それはハックです。C#ジェネリックの大きな欠如は、より高い種類をサポートしていないことだと思います。
CodexArcanum

4

私は炎上するために自分自身を開放しているように感じますが、C ++で参照によってプレーンな古いデータ型を渡す機能を本当に嫌います。参照によって複雑な型を渡すことができるのは少し嫌いです。関数を見ている場合:

void foo()
{
    int a = 8;
    bar(a);
}

呼び出しポイントから、bar完全に異なるファイルで定義される可能性のあるを示す方法はありません。

void bar(int& a)
{
    a++;
}

このようなことをすることは単に悪いソフトウェア設計であり、言語を責めることではないと主張する人もいるかもしれません。ポインターを使用して呼び出す

bar(&a);

より読みやすくなりました。


+1私はあなたに同意しませんが、あなたの推論に感謝します。
ジョンパーディ

@Jon私は実際にあなたの意見に非常に興味があるでしょう。「言語を責めるな」という見解を持っていますか?
ジェフ

6
@Jeff:1つには、参照セマンティクスがC ++に取り入れられた主な理由は、演算子のオーバーロードであり、統一された参照動作が理にかなっています。ただし、さらに重要なことは、C ++は汎用性があり、非常にきめ細かい機能を提供するように設計されていることです。たとえそれを行うと、プログラマーエラーの重大なリスクが生じます。そのため、少なくともこの特定のケースでは、言語を非難しないでください。言語に邪魔されるよりも、間違いを犯したいです。
ジョンパーディ

@Jonは同意しました。参照渡しがPOD以外のすべてに適用されるのは非常に奇妙です。この機能が代替としてC ++から完全に欠落している場合はそれを好むが、反対することに同意することができる:)。入力いただきありがとうございます!
ジェフ

Javaは、あなたほどポインターを好まないようです。
Mateen Ulhaq

4

ALTER

COBOLを学んだとき、ALTERステートメントはまだ標準の一部でした。一言で言えば、このステートメントを使用すると、実行時にプロシージャ呼び出しを変更できます。

危険なのは、めったにアクセスされず、プログラムの残りの部分のフローを完全に変更する可能性のあるコードの不明瞭なセクションにこのステートメントを配置できることです。複数のALTERステートメントを使用すると、いつでもプログラムが何をしていたかを知ることがほぼ不可能になります。

私の大学の教官は、非常に強調して、私たちのプログラムのいずれかでその声明を見たなら、彼は自動的に私たちをflしたと述べました。


ただし、スタブ化またはメモ化という優れたユースケースがあります。代わりに書くのv() { if (not alreadyCalculatedResult) { result = long(operation); alreadyCalculatedResult = true; } result; }あなたが言うv() { result = long(operation); v = () => result; result; }
コンフィギュレータ

4

プログラミング言語の最悪の罪は、明確に定義されていません。私が覚えているケースはC ++で、その起源は次のとおりです。

  1. 定義が非常に悪かったため、次の書籍または例ではプログラムをコンパイルして実行できませんでした。
  2. 1つのコンパイラーとOSでコンパイルして実行するようにプログラムを微調整したら、コンパイラーまたはプラットフォームを切り替えた場合、最初からやり直す必要があります。

私が覚えているように、C ++をCと同じくらい専門的に信頼できるものにするために十分に定義されたC ++を得るには、約10年かかりました。

私が罪と考える他の何か(それは別の答えになるでしょうか?)は、いくつかの一般的なタスクを行うための複数の「最良の」方法を持っています。(これも)C ++、Perl、Rubyの場合です。


1
進化する言語で不明確さを回避する方法がわかりません。または、そのことについては、元のデザイナーがいくつかの重要なポイント(Pascalなど)を逃した事前に設計された言語で。
デビッドソーンリー

@David Thornley明確な定義は明確です。バグに耐え、ほとんどのプログラミング言語の設計者は最初からそれを正しく理解しています。ツールは、文法の完成時に明確であることを確認でき(C ++には少なくとも3つの文法が必要です)、標準実装のセマンティクスを指定する必要があります。
アパララ

明確に定義された言語が可能であることに同意しますが、それは標準C ++以前のような進化する言語では起こりません。代替案は、各言語がリリース前に徹底的に設計されていることであり、必ずしもそれが最高の言語を取得する方法ではありません。言語の設計は非常に複雑であるため、ほとんどのプログラミング言語の設計者は最初は問題を抱えていると思います。
デビッドソーンリー

「明確に定義された」という言葉の意味を理解するのに苦労していると思います。異なるC ++コンパイラが実際に同じ言語をコンパイルしなかったという苦情はありますか?
ショーンマクミラン

3

C ++のクラスは、言語のある種の強制設計パターンです。

実行時に構造体とクラスに違いは実際上ありません。「情報隠蔽」の本当のプログラミングの利点が何であるかを理解するのは非常にわかりにくいので、そこに配置します。

私はそのことに賛成するつもりですが、とにかく、C ++コンパイラはこの言語を書くのがとても難しいので、モンスターのように感じます。


2
情報の非表示は、実装の特定の詳細(変更される可能性が高い)をAPIのアクセス可能な部分(APIの「UI」)から隠すことができるため、重要です。
アント

1
APIのUI ...いいえ、真剣に、私はそれを購入しません。
jokoon

3
この違いはC ++の最も不快な部分ではなく、近いものでもありません。唯一の違いは、デフォルトのアクセス修飾子です(構造体の場合はパブリック、クラスの場合はプライベート)。C ++は恐ろしく、怪しい言語ですが、この部分には確かにありません。
SKロジック

sk-logic:そうですね、そこから恐怖が始まると言えます。
-jokoon

2
情報の隠蔽は優れています。あなたはそれに関する議論を至る所で見つけることができます。私がそれについて考えることができる唯一の著名なソフトウェア本は、ブルックスの「神話上の男月」であり、彼は後にそれを本の最大の間違いと考えました。利点を理解していない場合、あなたは本当にあなたが下している判断を下す資格がありません。
デヴィッドソーンリー

3

すべての言語には欠点がありますが、一度知ってしまえば迷惑なものはありません。このペアを除く:

複雑な構文と冗長なAPI

これは、Objective-Cのような言語に特に当てはまります。構文が非常に複雑であるだけでなく、APIは次のような関数名を使用します。

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath

私は明確で曖昧さを感じないようにしていますが、これはばかげています。Xcodeを使用するたびに、n00bのように感じますが、それは本当にイライラします。


Objective-C構文は圧倒的に複雑ですか?C ++を見たことがありませんか?そして、実際のメソッド名はでtableView:cellForRowAtIndexPath:、これは私の意見では非常に記述的です。
右折

タッチタイプを学ぶ。
finnw

2
  1. Cで署名された文字-数学者に小さなアイテムの大きなコレクションを持たせるために考案された憎悪
  2. ケースを使用してセマンティックコンテンツを運ぶ-再び話す必要はなく、数式を入れるのに十分なスペースがない数学者向け
  3. Let / Plainの割り当てと基本的な方言での割り当ての設定-ここに関係する数学者はいないと思います

あなたの署名された文字のコメントについて私は迷っています、説明してもらえますか?
ウィンストンイーバート

1
人間にとって、(符号なし)文字の概念と、符号なし文字をデフォルトとして使用するようコンパイラーに指示する必要性は、数学者にとって2!= 2であるという主張とまったく同じです。 、または斜体。
Ekkehard.Horner

5
問題は、Cが "char"(つまり、テキスト文字列の一部)と "byte"(つまり、(u)int_least8_t)の概念を混同していることです。符号付きは小さな整数には完全に意味がありますが、文字にはまったく意味がありません。
dan04

@ dan04:同意します。小さな整数(符号付きと符号なし)、バイトと文字に異なる型があればいいのにと思います。生メモリを操作するにはchar*、Cストリングのようにキャストする必要があると初心者に説明すると、本当に混乱します。
マチューM.

C#が独立して、この権利を得たsbytebytechar種類。
dan04
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.