44

簡単な質問: 私は C++/Java のようなコンパイル済み言語や Python/Javascript のようなインタープリター型言語で (専門的にも個人的にも) 多くのプログラミングを行っています。個人的には、静的に型付けされた言語でプログラミングすると、ほとんどの場合、コードがより堅牢になることがわかりました。ただし、私が遭遇するほとんどすべてのインタープリター言語は、動的型付け (PHP、Perl、Python など) を使用しています。コンパイルされた言語が (ほとんどの場合) 静的型付けを使用する理由はわかっていますが、解釈された言語設計で静的型付けを嫌う理由がわかりません。

急激な切断はなぜですか?それはインタープリター言語の性質の一部ですか?おっと?

4

7 に答える 7

54

興味深い質問です。ところで、私はphc (PHP のコンパイラ) の作成者/管理者であり、動的言語のコンパイラで博士号を取得しているので、いくつかの洞察を提供できることを願っています。

ここに間違った仮定があると思います。PHP、Perl、Python、Ruby、Lua などの作成者は、「インタープリター型言語」を設計したのではなく、動的言語を設計し、インタープリターを使用して実装しました。彼らがこれを行ったのは、インタープリターはコンパイラーよりもはるかに書きやすいからです。

Java の最初の実装はインタープリターであり、静的に型付けされた言語です。静的言語にはインタープリターが存在します。Haskell と OCaml の両方にインタープリターがあり、以前は C の一般的なインタープリターがありましたが、それはずっと前のことです。これらは、開発を容易にするREPLを許可するため、人気があります。

とはいえ、ご想像のとおり、動的言語コミュニティでは静的型付けに対する嫌悪感があります。彼らは、C、C++、および Java によって提供される静的型システムは冗長であり、努力する価値がないと考えています。これにはある程度同意できると思います。Python でのプログラミングは、C++ よりもはるかに楽しいものです。

他の人のポイントに対処するには:

  • dlamblin は次のように述べています。ええと、あなたはそこで非常に間違っています。動的言語のコンパイルは非常に困難です。evalJavascript と Ruby で広く使用されている、考慮すべきステートメントがほとんどです。phc は事前に PHP をコンパイルしますが、s を処理する実行時インタープリターが必要evalです。evalまた、最適化コンパイラで静的に分析することもできませんが、健全性が必要ない場合はクールな手法があります。

  • Andrew Hareに対する damblin の応答: もちろん、インタプリタで静的解析を実行し、実行前にエラーを見つけることができます。これはまさに Haskell がghci行うことです。関数型言語で使用されるインタープリターのスタイルでは、これが必要になると思います。もちろん、dlamblin が分析は解釈の一部ではないと言うのは正しいことです。

  • アンドリュー・ヘアの答えは、質問者の間違った仮定に基づいており、同様に物事が間違っています。しかし、彼は興味深い質問を提起します:「動的言語の静的解析はどれくらい難しいですか?」. 非常に難しい。基本的に、それがどのように機能するかを説明することで博士号を取得できます。これはまさに私が行っていることです。前のポイントも参照してください。

  • これまでのところ、最も正しい答えはIvo Wetzelのものです。ただし、彼が説明するポイントはコンパイラで実行時に処理でき、このタイプの動的バインディングを持つ Lisp および Scheme 用の多くのコンパイラが存在します。しかし、はい、トリッキーです。

于 2009-09-11T22:27:47.287 に答える
5

それはインタープリター言語の性質によるものだと思います。それらは動的になりたいので、実行時に変更することができます。このため、コンパイラは次のコード行が実行された後のプログラムの状態を正確に知ることはできません。

次のシナリオを想像してください (Python で):

import random
foo = 1

def doSomeStuffWithFoo():
    global foo
    foo = random.randint(0, 1)

def asign():
    global foo
    if foo == 1:
        return 20
    else:
        return "Test"


def toBeStaticallyAnalyzed():
    myValue = asign()

    # A "Compiler" may throw an error here because foo == 0, but at runtime foo maybe 1, so the compiler would be wrong with its assumption
    myValue += 20


doSomeStuffWithFoo() # Foo could be 1 or 0 now... or 4 ;)
toBeStaticallyAnalyzed()

ご覧のとおり、コンパイラはこの状況では意味がありません。実際には、「myValue」が数値以外の何かである可能性について警告する可能性があります。しかし、JavaScript では、"myValue" が文字列の場合、20 も暗黙のうちに文字列に変換されるため、エラーは発生しません。そのため、あちこちで何千もの役に立たない警告が表示される可能性がありますが、それがコンパイラの意図であるとは思いません。

柔軟性には常に代償が伴います。プログラムをより深く調べるか、より慎重にプログラムする必要があります。つまり、上記のような状況では、あなたがコンパイラーになります。

それで、コンパイラとしてのあなたの解決策は?- 「try: except」で修正してください:)

于 2009-09-08T14:34:27.240 に答える
4

静的解析を行うコンパイル手順がないため、解釈された言語は動的型付けを使用します。コンパイルされた言語は、コンパイル時に静的解析を行います。つまり、動作中に型エラーが開発者に報告されます。

静的に型付けされた言語には、実行のコンテキスト外で型規則を強制するコンパイラがあると考えると、理解しやすくなります。解釈された言語は静的に分析されることはないため、型規則は実行のコンテキスト内でインタープリターによって強制される必要があります。

于 2009-09-08T13:04:56.317 に答える
2

コンパイラ + 静的型 = 効率的なマシン コード
コンパイラ + 動的型 = 非効率的なマシン コード

次の擬似コードを検討してください。

function foo(a, b) {
    return a+b
}

静的言語は、a と b が整数であることを (宣言または推論によって) 知ることができ、以下にコンパイルされます。

%reg = addi a,b

とにかく、似たようなもの。

動的言語のコンパイラはコードを出力する必要があり
ます 1. a と b の型をチェックします
2. 各ケースまたはケースの組み合わせを処理します

%reg1 = typeof a
beq %reg1, int, a_int_case
beq %reg1, float, a_float_case
beq %reg1, string, a_string_case

label a_int_case
%reg1 = typeof b
beq %reg1, int, a_int_b_int_case
beq %reg1, float, a_int_b_float_case
beq %reg1, string, a_int_b_string_case

label a_int_b_int_case
%out = addi a,b
goto done

label a_int_b_float_case
%tmp = mkfloat a
%out = addf %tmp,b
goto done

... Etc. I can't finish

それよりもスマートなマシン コードを生成することはできますが、大量のコードを生成せずにはいられないでしょう。つまり、コンパイルは動的言語にとって大きなメリットではありません。

インタプリタは書くのがはるかに簡単で、コンパイルはあまり役に立たないので、インタプリタを書いてみませんか?

(ジャストインタイム コンパイラは実際には型情報を持っており、単一のステートメントに至るまでコンパイルできます。実際には、静的型システムよりも多くの情報を持ち、理論的にはさらに優れたパフォーマンスを発揮します。すべてのアセンブラーはシミュレートされます。実際のマシンで実行できる可能性があるのは、まったくの偶然です。)

于 2009-10-27T13:19:01.843 に答える
1

私の主なインタープリター言語の 1 つが Perl であり、私のコンパイル済み言語の 1 つが Objective-C であるためかもしれませんが、静的型付けよりも動的型付けを提案するコンパイルと解釈につ​​いて特別なことがあると強く感じたことはありません。

双方が相手を見て、「それにはいくつかの利点がある」と考えていることは明らかだと思います。一部のアプリケーションでは、動的な型の柔軟性を得る方が簡単ですが、静的に型付けされ強制されるものを維持する方が簡単な場合があります。

ただし、 Andrew Hare の説明には同意しません。純粋に解釈された言語は前処理ステップを追加する必要があるため、静的型付けエラーの実行前にプログラマーに警告するために純粋に解釈されるわけではありませんが、実行時に型エラーが発生したときにスローされることを排除しません。したがって、コンパイルが行われないからといって、静的な型チェックが行われないわけではありません。しかし、実行時に型エラーを取得することは、コンパイル時またはプリフライト チェック中に取得するほど有用ではないため、そのような状況での静的型付けの「利点」がいかに面倒なように見えるかがわかります。したがって、動的型付けがもたらす利点を支持して捨てられます。

個人的により保守しやすいコードを作成するため、型を静的に保つことを好むことが最初からわかっていて、インタープリター言語を設計していた場合は、言語を静的に型付けされたものとして設計することを止めるものは何もありません。

解釈された言語の wiki 記事を引用すると、 「理論的には、どの言語もコンパイルまたは解釈される可能性があるため、この指定は純粋に一般的な実装慣行のために適用され、言語の基本的なプロパティではありません。」タイピングだけに関する
まともなwiki記事があります。

于 2009-09-08T15:26:56.857 に答える
-2

静的型付けはコンパイラーにとってより簡単になると思います。それが、コンパイル済み言語に静的型付けが存在する主な (それだけではないにしても) 理由です。

インタープリター型言語の場合、変数は型を持たない (値のみを持つ) と仮定する方が簡単です。変数は、内部に収まる必要があるデータの配置ではなく、ヒープのどこかに浮動するデータのラベルと見なされるためです。

プログラマーが望む場合は、変数が特定の型の値を保持することをいつでもアサートできます (たとえば、割り当て時)。それを言語に組み込む理由はありません。もちろん、コンパイルされた言語に対して持っているのと同じ種類の制御ではありません。

おそらく、各変数の型を明示的に宣言する必要がある言語を使用できますが、そうでない場合は、静的型付けでプログラマーが非常に慎重に作成した複雑なジェネリック型を必要とする興味深いことを行う方がはるかに簡単です。

一方で。動的に型付けされた (JIT ではなく静的に) コンパイルされた言語を知っていますか?

于 2009-09-08T13:18:08.973 に答える