弱く型付けされた言語に関する明らかな矛盾についての明確化を求める


178

強い型付けは理解できると思いますが、弱い型付けの例を探すたびに、型を自動的に強制/変換するプログラミング言語の例を見つけることになります。

たとえば、「タイピング:強いvs.弱い」というこの記事では、静的vs.動的は、Pythonは強く型付けされていると述べています。

パイソン

1 + "1"
Traceback (most recent call last):
File "", line 1, in ? 
TypeError: unsupported operand type(s) for +: 'int' and 'str'

ただし、そのようなことはJavaとC#で可能であり、そのために弱く型付けされたとは見なしません。

ジャワ

  int a = 10;
  String b = "b";
  String result = a + b;
  System.out.println(result);

C#

int a = 10;
string b = "b";
string c = a + b;
Console.WriteLine(c);

Weakly Type Languagesという名前のこの別の記事で、著者は、明示的な変換を行わずに文字列を数値に連結したり、その逆を行ったりできるので、Perlは弱い型付けであると述べています。

Perl

$a=10;
$b="a";
$c=$a.$b;
print $c; #10a

したがって、同じ例では、Perlは弱く型付けされていますが、JavaやC#はそうではありません。

ああ、これは紛らわしい ここに画像の説明を入力してください

著者は、異なる型の値に対する特定の操作の適用を妨げる言語は強く型付けされ、逆は弱い型付けを意味することを示唆しているようです。

したがって、ある時点で、言語が(perlのように)自動変換または型間の強制を大量に提供する場合、最終的には弱い型付けと見なされる可能性がある一方、変換が少ない他の言語は最終的に強く型付けされていると見なされます。

しかし、私はこの相互解釈で間違っているに違いないと信じる傾向があります。理由や説明方法がわからないだけです。

だから、私の質問は:

  • 言語が本当に弱く型付けされているとはどういう意味ですか?
  • 言語による自動変換/自動強制に関連しない弱い型付けの良い例を挙げていただけますか?
  • 言語を弱く型付けすると同時に強く型付けすることはできますか?

8
強い型付けと弱い型付けは、すべて型変換に関するものです(それ以外に何ができるのでしょうか?)「非常に」弱い言語の例が必要な場合は、destrotallsoftware.com / talks / watをご覧ください
Wilduck

2
@Wildduckすべての言語は型変換を提供しますが、すべてが弱い型付けと見なされるわけではありません。以下に示す私の例は、強く型付けされたと見なされる他の言語で可能な同じ例に基づいて、プログラマーが弱く型付けされた言語をどのように考えるかを示しています。そのため、私の質問はまだ優勢です。違いはなんですか?
エドウィンDalorzo

1
簡単に言えば、「型付け」はバイナリ状態ではないということです。JavaとC#はより強く型付けされていますが、絶対型ではありません。
Jodrell 2012年

3
これはソフトウェアエンジニアリングに適していると思います。
zzzzBov 2012年

4
@Brendan floatとintegerの合計はどうですか?Pythonでは整数が浮動小数点数に強制変換されていませんか?Pythonが絶対に強く型付けされているわけではない、ということでしょうか。
Edwin Dalorzo、2012年

回答:


210

更新:この質問は、2012年10月15日に私のブログの主題でした。素晴らしい質問をありがとう!


言語が「弱く型付けされている」とはどういう意味ですか?

それは「この言語は私が不愉快だと思う型システムを使用している」という意味です。それとは対照的に、「強く型付けされた」言語は、私が心地よいと思う型システムを持つ言語です。

これらの用語は本質的に意味がなく、避ける必要があります。ウィキペディアには、「強く型付けされた」の11の異なる意味がリストされていますが、そのいくつかは矛盾しています。これは、「強く型付けされた」または「弱く型付けされた」という用語を含む会話では、混乱が生じる確率が高いことを示しています。

確かに言えることは、議論中の「強く型付けされた」言語には、実行時またはコンパイル時に型システムに追加の制限があり、議論中の「弱く型付けされた」言語にはないということです。その制限が何であるかは、さらなるコンテキストなしでは決定できません。

「強く型付けされた」と「弱く型付けされた」を使用する代わりに、どのようなタイプセーフを意味するかを詳細に説明する必要があります。たとえば、C#は静的に型付けされた言語であり、ほとんどの場合、タイプセーフな言語とメモリセーフな言語です。。C#では、これらの「強力な」タイピングの3つの形式すべてに違反することができます。キャスト演算子は静的型付けに違反しています。それはコンパイラに「あなたよりもこの式の実行時の型について知っています」と言っています。開発者が間違っている場合、ランタイムはタイプセーフを保護するために例外をスローします。開発者がタイプセーフティまたはメモリセーフティを解除したい場合は、「安全でない」ブロックを作成してタイプセーフティシステムをオフにすることで解除できます。安全でないブロックでは、ポインターマジックを使用して、intをfloatとして扱う(型の安全性に違反する)か、所有していないメモリに書き込むことができます。(メモリの安全性に違反しています。)

C#は、コンパイル時と実行時の両方でチェックされる型制限を課すため、コンパイル時間のチェックや実行時のチェックが少ない言語と比較して、「強く型付けされた」言語になります。また、C#を使用すると、特別な状況でこれらの制限を回避してエンドランを実行できるため、そのようなエンドランを実行できない言語と比較して、「弱く型付けされた」言語になります。

それは本当ですか?言うことは不可能です。それは話者の視点とさまざまな言語機能に対する彼らの態度に依存します。


14
@edalorzo:これは、(1)型理論のどの側面が関連していて、無関係であるか、(2)言語が型の制限を強制するか、または単に奨励するために必要かどうかに関する好みと個人的な意見に基づいています。私が指摘したように、C#は静的型付けを許可および奨励するため、強く型付けされていると合理的に言えます。また、型の安全に違反する可能性があるため、型付けが弱いと合理的に言えます。
Eric Lippert

4
@edalorzo:組み立てに関しても、やはり意見の問題です。アセンブリ言語コンパイラでは、64ビットdoubleをスタックから32ビットレジスタに移動できません。スタックから32ビットレジスタに32ビットポインタを64ビットdoubleに移動できます。その意味で、言語は「タイプセーフ」です。データのタイプ分類に基づいて、プログラムの合法性に制限を課します。その制限が「強い」か「弱い」かは意見の問題ですが、明らかに制限です。
Eric Lippert、2012年

2
私は今あなたの意見を理解していると思いますが、本当に弱く型付けされた言語は、完全に型付けされていないか、またはモノタイプ化されている必要があります。そのため、どの言語にも型の特定の定義があり、安全であり、言語がそのデータまたはデータ型に違反または操作するために提供するホールの数に応じて、多かれ少なかれ弱く型付けされていると見なす可能性があります特定のコンテキストのみ。
Edwin Dalorzo、2012年

7
@edalorzo:正解。たとえば、型付けされていないラムダ計算は、あなたが得ることができるのと同じくらい弱く型付けされています。すべての関数は、関数から関数への関数です。すべてが「同じタイプ」であるため、任意のデータを制限なしに任意の関数に渡すことができます。型なしラムダ計算における式の妥当性は、その構文形式にのみ依存し、特定の式を特定の型を持つものとして分類するセマンティック分析には依存しません。
Eric Lippert、2012年

3
@マーク私は彼に別の+1を与え、誰もがその主題について異なる解釈を提供するだろうと予測しました。この「弱い型付け」は「神秘的な概念」または「都市伝説」のようです。誰もがそれを目にしていますが、誰もそれが存在することを証明することはできません:-)
Edwin Dalorzo 2012年

64

他の人が指摘したように、「強く型付けされた」と「弱く型付けされた」という用語は非常に多くの異なる意味を持っているため、質問に対する単一の答えはありません。ただし、質問でPerlについて具体的に言及したので、Perlがどのように弱い型付けであるかを説明します。

ポイントは、Perlでは、「整数変数」、「浮動変数」、「文字列変数」、「ブール変数」などは存在しないということです。実際、ユーザーが(通常)知ることができる限り、整数値、浮動小数点、文字列、ブールはありません。「スカラー」だけがあり、これらすべてが同時に存在します。したがって、たとえば、次のように書くことができます。

$foo = "123" + "456";           # $foo = 579
$bar = substr($foo, 2, 1);      # $bar = 9
$bar .= " lives";               # $bar = "9 lives"
$foo -= $bar;                   # $foo = 579 - 9 = 570

もちろん、あなたが正しく指摘しているように、これはすべて型強制と見なすことができます。しかし要点は、Perlでは、型は常に強制されているということです。実際、ユーザーが変数の内部「タイプ」が何であるかを知るのは非常に困難です。上記の例の2行目で、の値が$bar文字列である"9"か数値であるかを尋ねると、9ほとんど意味がありません。Perlに関する限り、それらは同じものです。実際、Perlスカラーが内部的に文字列と数値の両方を同時に持つことも可能です。たとえば、$foo上記の2行目がそうです。

これとは逆に、Perl変数は型指定されていない(または、ユーザーに内部型を公開しない)ため、演算子をオーバーロードして、引数の型ごとに異なることを行うことはできません。演算子が引数の値の種類を判別できない(わからない)ため、「この演算子は数値に対してXを実行し、文字列に対してYを実行する」とは言えません。

したがって、たとえば、Perlには数値加算演算子(+)と文字列連結演算子(.)の両方があり、必要です。上記で見たように、文字列("1" + "2" == "3")を追加したり、数値(1 . 2 == 12)を連結したりしても問題ありません。同様に、数値比較演算子==!=<><=>=および<=>その引数の数値を比較し、文字列の比較演算子がいる間eqneltgtlegeおよびcmp文字列として辞書的に比較します。ですから2 < 10、しかし2 gt 10(しかし"02" lt 10、しばらく"02" == 2)。(JavaScriptのような他の特定言語は、Perlのような弱いタイピングに対応するように努めていますが、演算子のオーバーロード行います。これにより、の関連性が失われるなど、見苦しくなることがあり+ます。)

(ここでの軟膏のハエは、歴史的な理由により、Perl 5にはビット単位の論理演算子のようないくつかのコーナーケースがあり、その動作は引数の内部表現に依存していることです。意外な理由で内部表現が変わる可能性があるため、特定の状況でこれらの演算子が何を行うかを予測するのは難しい場合があります。

そうは言っても、Perlに強力な型があると主張できます。それらはあなたが期待するような種類ではありません。具体的には、上で説明した「スカラー」タイプに加えて、Perlには「配列」と「ハッシュ」という2つの構造化タイプもあります。これらは、スカラーとは非常に異なり、Perl変数には、そのタイプを示すさまざまなシギル$スカラー、@配列、%ハッシュ)があります1。そこているあなたはので、これらの型の間で強制規則は、することができます例えば、書き込み%foo = @bar、それらの多くは非常に非可逆です:例えば、$foo = @bar割り当て配列の @barへの$foo、その内容ではありません。(また、タイプグロブやI / Oハンドルなど、他のいくつかの奇妙な型があり、公開されていないことがよくあります。)

また、この優れた設計のわずかな違いは、特別な種類のスカラーである参照型の存在です(これref演算子を使用して、通常のスカラーと区別できます)。参照を通常のスカラーとして使用することは可能ですが、それらの文字列/数値は特に有用ではなく、通常のスカラー演算を使用して変更すると、特別な参照性を失う傾向があります。また、任意のPerlの変数2はすることができbless、そのクラスのオブジェクトにそれを回す、クラスに編。PerlのOOクラスシステムは、上記のプリミティブ型(または型なし)システムと多少直交していますが、アヒルの型付けの意味では「弱い」ものです。パラダイム。一般的な意見としては、Perlでオブジェクトのクラスをチェックしているとしたら、何かがおかしいということです。


1実際、シギルはアクセスされる値のタイプを示すため、たとえば配列の最初のスカラー@fooはと示され$foo[0]ます。詳細についてはperlfaq4を参照してください。

2 Perlのオブジェクトは(通常)それらへの参照を通じてアクセスされますが、実際に取得されるのblessは、参照が指す(おそらく匿名の)変数です。ただし、祝福は実際には変数のプロパティであり、値でありません。たとえば、祝福された実際の変数を別の変数に割り当てると、祝福​​されていない浅いコピーが得られます。詳細については、perlobjを参照してください。


19

エリックが言ったことに加えて、次のCコードを検討してください。

void f(void* x);

f(42);
f("hello");

Python、C#、Javaなどの言語とは対照的に、上記の型は型情報が失われるため弱く型付けされます。エリックは、C#ではキャストすることでコンパイラを回避でき、「この変数の型についてはあなたよりも知っている」と効果的に伝えることで正しく指摘しました。

しかし、それでも、ランタイムはタイプをチェックし続けます!キャストが無効な場合、ランタイムシステムはそれをキャッチして例外をスローします。

タイプの消去では、これは起こりません。タイプ情報は破棄されます。void*Cでのキャストはまさにそれを行います。この点で、上記はのようなC#メソッド宣言とは根本的に異なりvoid f(Object x)ます。

(技術的には、C#では、安全でないコードやマーシャリングによって型を消去することもできます。)

これは、できるだけ弱く型付けされています。他のすべては静的対の動的な型チェック、すなわち、時間のだけの問題であるタイプがチェックされています。


1
+1良い点、あなたは今、型消去を「弱い型付け」をも意味するかもしれない機能と考えさせました。Javaにも型消去があります。実行時に、型システムを使用すると、コンパイラーが決して承認しない制約に違反できます。Cの例は、ポイントを説明するのに優れています。
Edwin Dalorzo

1
同意し、玉ねぎ、または地獄に層があります。これらは型の弱点のより重要な定義のようです。
Jodrell 2012年

1
@edalorzo Javaではコンパイラを回避することができますが、ランタイム型システムが違反をキャッチするため、これはまったく同じだとは思いません。したがって、Javaランタイム型システムはこの点に関して強く型付けされています(たとえば、リフレクションを使用してアクセス制御を回避できる場合には例外があります)。
Konrad Rudolph、

1
@edalorzoこの方法でコンパイラを回避できるのは、ランタイムシステムではありません。JavaやC#(およびある程度C ++も)などの言語には、2回保証される型システムがあることを認識することが重要です。1回はコンパイル時、もう1回は実行時です。void*両方の型チェックを突破します。ジェネリック型の消去は行われません。コンパイル時のチェックを回避するだけです。これは、明示的なキャスト(Ericによる言及)とまったく同じです。
Konrad Rudolph

1
@edalorzoあなたの混乱について:私たちはすべきではありません。区別は流暢です。そして、はい、型消去はJavaをこの点で弱く型付けします。私のポイントは、さらに、一般的なタイプの消去とあなたがいることだった、まだ実行時の型チェックを回避することはできませんあなたはまた、リフレクションを使用しない限り
Konrad Rudolph

14

完璧な例は、強力なタイピングのウィキペディアの記事から来てます:

一般に強い型付けは、プログラミング言語が発生を許可されている混合に厳しい制限を課すことを意味します。

弱いタイピング

a = 2
b = "2"

concatenate(a, b) # returns "22"
add(a, b) # returns 4

強いタイピング

a = 2
b = "2"

concatenate(a, b) # Type Error
add(a, b) # Type Error
concatenate(str(a), b) #Returns "22"
add(a, int(b)) # Returns 4

弱い型付け言語は、エラーなしで異なる型を混在させることができることに注意してください。強力な型言語では、入力型が期待される型である必要があります。強い型言語では、型を変換できます(str(a)整数を文字列に変換)またはキャスト(int(b))できます。

これはすべて、タイピングの解釈に依存します。


3
しかし、これは質問で提供された矛盾する例につながります。厳密に型付けされた言語には、2つの「タイプエラー」の例のいずれか(または両方)が2番目の2つの例の関連するものに自動的に変換されることを意味する暗黙の強制が含まれる場合がありますが、通常、その言語は依然として厳密に型付けされています。
Mark Hurd 2012年

3
そうだね。強いタイピングと弱いタイピングの程度はさまざまだと言えるでしょう。暗黙的な変換とは、言語が暗黙的な変換を行わない言語よりも強く型付けされていないことを意味します。
SaulBack 2012年

4

他の人がコメントし、貢献しているので、私はこのテーマに関する自分の研究との議論に貢献したいと思います。示唆されているように、これは実用的というより理論的であるように思われるため、プログラマーフォーラムでこれのほとんどがよりよく議論される可能性があります。

理論的な観点から見ると、Luca CardelliとPeter Wegnerによる、「型の理解、データの抽象化、および多態性」という名前の記事は、私が読んだ最高の議論の1つだと思います。

タイプは、潜在的な型なし表現を恣意的または意図しない使用から保護する一連の衣服(または鎧)と見なすことができます。基礎となる表現を隠し、オブジェクトが他のオブジェクトと相互作用する方法を制限する保護カバーを提供します。型なしシステムでは、型なしオブジェクトはネイキッド であり、基礎となる表現がすべての人に公開されます。型システムに違反するには、保護服を脱いで、裸の表現を直接操作します。

このステートメントは、弱い型付けによって型の内部構造にアクセスし、それを別の型(別の型)のように操作できることを示唆しているようです。おそらく、安全でないコード(Ericによって言及された)またはKonradによって言及されたc型消去されたポインターを使用して何ができるか。

記事は続きます...

すべての式が型整合性のある言語は、強く型付けされた言語と呼ばれます。言語が強く型付けされている場合、そのコンパイラーは、受け入れたプログラムが型エラーなしで実行されることを保証できます。一般に、強い型付けに努め、可能な限り静的型付けを採用する必要があります。すべての静的型付け言語は強く型付けされていますが、その逆は必ずしも真ではありません。

したがって、強い型付けとは型エラーがないことを意味します。弱い型付けとは逆に、型エラーが存在する可能性が高いことを意味していると思います。実行時またはコンパイル時?ここでは関係ないようです。

おかしなことに、この定義に従って、Perlのような強力な型強制を持つ言語は、システムに障害がないため、強く型付けされていると見なされますが、適切で明確に定義された同等のものに型を強制することで型を処理しています。

一方、(Javaの)and と(C#の)の許容範囲がClassCastException、少なくともコンパイル時に、弱い型付けのレベルを示していると言えるでしょうか。エリックの答えはこれに同意するようです。ArrayStoreExceptionInvalidCastExceptionArrayTypeMismatchException

この質問の回答の1つで提供されているリファレンスの1つで提供されているタイプフルプログラミングという2番目の記事では、Luca Cardelliが型違反の概念について詳しく説明しています。

ほとんどのシステムプログラミング言語では、任意の型違反が許されており、その一部は無差別に、一部はプログラムの制限された部分にのみ存在します。タイプ違反を伴う操作は、不健全と呼ばれます。型違反は、いくつかのクラスに分類されます(そのうちの1つは言及できます)。

基本値の強制変換:これには、整数、ブール値、文字、セットなどの間の変換が含まれます。ここでは、型違反の必要はありません。組み込みインターフェースを提供して、型どおりの方法で強制を実行できます。

そのため、演算子によって提供されるような型強制は、型違反と見なされる可能性がありますが、型システムの一貫性が損なわれない限り、型の弱いシステムにはならないと言えます。

これに基づいて、Python、Perl、Java、C#のいずれも型付けが弱い。

Cardelliは、私が本当に弱いタイピングのケースを非常によく考える2つのタイプのビレーションについて言及しています。

アドレス演算。必要に応じて、組み込み(非サウンド)インターフェースが必要です。これにより、アドレスと型変換に対する適切な操作が提供されます。さまざまな状況には、ヒープへのポインター(コレクターの再配置では非常に危険)、スタックへのポインター、静的領域へのポインター、および他のアドレス空間へのポインターが含まれます。場合によっては、配列のインデックス付けがアドレス計算に取って代わります。 メモリマッピング。これには、構造化データが含まれていますが、メモリの領域を非構造化配列として見ることが含まれます。これは、典型的なメモリアロケータとコレクタです。

C(Konradによって言及された)などの言語で、または.Net(Ericによって言及された)の安全でないコードを介してこの種のことが可能であることは、本当に弱い型付けを意味します。

この概念の定義は非常に理論的であり、特定の言語に関しては、これらのすべての概念の解釈がさまざまな議論の余地のある結論につながる可能性があるため、これまでのところ最良の答えはエリックのものです。


4

弱い型付けは確かに、高い割合の型が暗黙的に強制され、コーダーの意図を推測しようとする可能性があることを意味します。

強い型付けとは、型が強制されない、または少なくとも強制されないことを意味します。

静的型付けとは、変数の型がコンパイル時に決定されることを意味します。

最近、多くの人が「明示的に型付けされた」と「強く型付けされた」を混同しています。「明示的に型付け」とは、変数の型を明示的に宣言することを意味します。

Pythonのほとんどは強く型付けされていますが、ブールコンテキストではほとんどすべてのものを使用でき、ブールコンテキストは整数コンテキストで使用でき、整数は浮動小数点コンテキストで使用できます。型を宣言する必要がないため、明示的に型付けされていません(興味深いことに、完全にpythonではないCythonを除く)。また、静的に型付けされません。

CとC ++は、明示的に型付けされ、静的に型付けされ、やや強く型付けされます。これは、型を宣言するため、型はコンパイル時に決定され、整数とポインター、または整数とdoubleを混合したり、1つの型へのポインターをキャストしたりできるためです。別の型へのポインター。

Haskellは明らかに型付けされていないため興味深い例ですが、静的かつ強力に型付けされています。


+1「マニフェスト型」という造語が好きなので、明示的に型を宣言し、型推論が重要な役割を果たす重要な役割を果たしているHaskellやScalaなどの他の静的型言語と区別する必要があるJavaやC#などの言語を分類します。あなたが言うように人々を混乱させ、これらの言語が動的に型付けされていると信じ込ませます。
Edwin Dalorzo、2012年

3

強い<=>弱い型付けは、あるデータ型から別のデータ型に言語によって自動的に強制される値の量の連続性に関するだけでなく、実際の値の強さまたは弱さ型付けのです。PythonとJava、そしてほとんどの場合C#では、値の型が明確になっています。Perlでは、それほど多くはありません-変数に格納できるのは、ほんの一握りの異なる値型だけです。

ケースを一つずつ開けましょう。


パイソン

Pythonの例では1 + "1"+演算子は__add__for型intを呼び出し、文字列"1"を引数として渡しますが、これによりNotImplementedが生成されます。

>>> (1).__add__('1')
NotImplemented

次に、インタプリタ__radd__はstrを試します:

>>> '1'.__radd__(1)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: 'str' object has no attribute '__radd__'

失敗すると、+オペレーターは結果とともに失敗しTypeError: unsupported operand type(s) for +: 'int' and 'str'ます。そのため、例外は強い型付けについてはあまり述べていませんが、演算子が強制しないという事実+ 引数を自動的に同じ型にという事実は、Pythonが連続体で最も弱い型付けの言語ではないという事実へのポインタです。

一方、Python 'a' * 5 では実装されています。

>>> 'a' * 5
'aaaaa'

あれは、

>>> 'a'.__mul__(5)
'aaaaa'

演算が異なるという事実は、いくつかの強い型付けを必要とします-ただし、*乗算する前に値を数値に強制変換することの反対は、必ずしも値の型付けを弱いとは限りません。


ジャワ

Javaの例は、String result = "1" + 1;便宜上、演算子+が文字列に対して多重定義されているためにのみ機能します。Java +オペレーターは、シーケンスを次のものを作成するものに置き換えますStringBuilderこれを参照):

String result = a + b;
// becomes something like
String result = new StringBuilder().append(a).append(b).toString()

これはむしろ静的な型付けの例であり、実際の強制StringBuilderはありません- append(Object)ここで特に使用されるメソッドがあります。ドキュメントには次のように書かれています:

Object引数の文字列表現を追加します。

全体的な効果は、メソッドによって引数が文字列に変換され、String.valueOf(Object)その文字列の文字がこの文字シーケンスに追加された場合とまったく同じです。

どこString.valueOf

Object引数の文字列表現を返します。[戻り値]引数がの場合null、次に等しい文字列"null"。それ以外の場合は、の値obj.toString()が返されます。

したがって、これは言語による強制が絶対にないケースです-すべての懸念をオブジェクト自体に委任します。


C#

ここでのJon Skeetの回答によると、演算子+stringクラスに対してオーバーロードされていません-Javaのように、これは静的型と強力型の両方のおかげで、コンパイラーによって生成された便利です。


Perl

以下のようperldataで説明し、

Perlには、スカラー、スカラーの配列、および「ハッシュ」と呼ばれるスカラーの連想配列の3つの組み込みデータ型があります。スカラーは、単一の文字列(任意のサイズ、使用可能なメモリによってのみ制限される)、数値、または何かへの参照(perlrefで説明されます)です。通常の配列は、0から始まる、番号でインデックス付けされたスカラーの順序付きリストです。ハッシュは、関連付けられた文字列キーでインデックス付けされたスカラー値の順序付けられていないコレクションです。

ただし、Perlには、数値、ブール値、文字列、null、undefineds、他のオブジェクトへの参照などの個別のデータ型はありません。これらすべてに対して1つの型、スカラー型しかありません。0は "0"と同様にスカラー値です。文字列として設定されたスカラー変数は、実際には数値に変わる可能性があり、それ以降は「単なる文字列」とは異なる動作をします可能性があり、数値コンテキストでアクセスされる場合とはます。。スカラーはPerlで何でも保持できます。これは、システムに存在するオブジェクトと同じだけのオブジェクトです。Pythonでは名前はオブジェクトを参照するだけですが、Perlでは名前のスカラー値は変更可能なオブジェクトです。さらに、オブジェクト指向型システムはこの上に接着されています。perlには、スカラー、リスト、ハッシュの3つのデータ型しかありません。Perlのユーザー定義オブジェクトはbless、パッケージへの参照(前の3つのいずれかへのポインター)です。このような値を取得して、いつでも任意のクラスに祝福できます。

Perlでは、気まぐれに値のクラスを変更することもできます。あるクラスの値を作成する場所では、そのクラスに属している値などを明示的に構築する必要があるPythonではこれは不可能object.__new__です。Pythonでは、作成後にオブジェクトの本質を実際に変更することはできません。Perlでは、何でも行うことができます。

package Foo;
package Bar;

my $val = 42;
# $val is now a scalar value set from double
bless \$val, Foo;
# all references to $val now belong to class Foo
my $obj = \$val;
# now $obj refers to the SV stored in $val
# thus this prints: Foo=SCALAR(0x1c7d8c8)
print \$val, "\n"; 
# all references to $val now belong to class Bar
bless \$val, Bar;
# thus this prints Bar=SCALAR(0x1c7d8c8)
print \$val, "\n";
# we change the value stored in $val from number to a string
$val = 'abc';
# yet still the SV is blessed: Bar=SCALAR(0x1c7d8c8)
print \$val, "\n";
# and on the course, the $obj now refers to a "Bar" even though
# at the time of copying it did refer to a "Foo".
print $obj, "\n";

したがって、タイプアイデンティティは変数に弱くバインドされており、その場で任意の参照を介して変更できます。実際には、

my $another = $val;

\$another\$val祝福された参照を提供しますが、クラスIDはありません。


TL; DR

Perlへの弱い型付けについては、自動強制よりもはるかに多く、動的でありながら非常に強く型付けされた言語であるPythonとは異なり、値自体の型は固定されていません。Pythonが与えるTypeErrorこと1 + "1"は、JavaまたはC#のように有用な何かをするという反対のことは強く型付けされた言語であることを排除しないにもかかわらず、言語が強く型付けされていることを示しています。


これは完全に混乱しています。Perl 5 変数が型を持たないということは、常に型を持つに影響を与えません。
ジムバルター

@JimBalterええ、そうです。値には文字列または数値という型があり、スカラー変数に文字列と数値のどちらが含まれているかによって、コンテキストによって動作が異なる場合があります。しかし、変数に含まれる値は、単に変数にアクセスすることで、タイプを変更することができ、かつ自身が住んでいる価値あるため、変数、数値自体はタイプ間で可変とみなすことができます。
Antti Haapala 2016年

値は型を変更しません-それは矛盾しています。値は常にタイプです。変数に含まれる値は変更される可能性があります。「1」への1からの変更は、1〜2からの変化としての価値で同じくらいの変化である
ジムBalter

Perlなどの弱く型付けされた言語では、前のタイプの値の変更がコンテキストに応じて暗黙的に発生します。ただし、C ++でも、演算子の定義を介してこのような暗黙の変換を行うことができます。弱い型付けは非常に非公式な特性であり、Eric Lippertが指摘したように、実際には言語を記述するための有用な方法ではありません。
ジムバルター2016年

PS Perlでさえ、<桁>と "<桁>"の値が異なるだけでなく、異なることを示すことができます。Perlは、暗黙の変換によって、ほとんどの場合、<数字>と "<数字>" 同じ値であるように見せますが、幻想は完全ではありません。例:「12」| 「34」は36ですが、12 | 34は46です。もう1つの例は、ほとんどのコンテキストで「00」は数値的に00に等しいですが、ブールコンテキストでは「00」はtrueですが、00はfalseです。
ジムバルター2016年

1

他の多くの人が述べているように、「強い」タイプと「弱い」タイプの概念全体には問題があります。

Smalltalkは原型として非常に強く型付けされています-2 つのオブジェクト間の操作に互換性がない場合は常に例外が発生します。ただし、動的に型付けされているため、Smalltalkを厳密に型付けされた言語と呼ぶ人はほとんどいません

「静的」タイプと「動的」タイプの概念の方が、「強い」と「弱い」よりも便利だと思います。静的に型付けされた言語では、コンパイル時にすべての型が計算されます。そうでない場合、プログラマーは明示的に宣言する必要があります。

動的に型付けされる言語とは対照的です。動的に型付けされる言語は、型付けが実行時に実行されます。これは通常、ポリモーフィック言語の要件であるため、2つのオブジェクト間の操作が正当であるかどうかの決定は、事前にプログラマーが決定する必要はありません。

ポリモーフィックで動的に型付けされた言語(SmalltalkやRubyなど)では、「型」を「プロトコルへの適合性」と考える方が便利です。オブジェクトが別のオブジェクトと同じ方法でプロトコルに従う場合(2つのオブジェクトが継承やミックスインやその他のブードゥーを共有していない場合でも)、ランタイムシステムではそれらは同じ「タイプ」と見なされます。より正確には、そのようなシステムのオブジェクトは自律的であり、特定の引数を参照する特定のメッセージに応答することが理にかなっているかどうかを判断できます。

青を表すオブジェクト引数を使用して、メッセージ "+"に意味のある応答を返すことができるオブジェクトが必要ですか?動的に型付けされた言語でそれを行うことができますが、静的に型付けされた言語では苦痛です。


3
動的型付けと静的型付けの概念は議論されていないと思います。とはいえ、静的型言語では多態性がとにかくハンディキャップがあるとは信じていません。最終的に、型システムは、実行時でもコンパイル時でも、特定の演算が特定のオペランドに適用可能かどうかを検証します。また、パラメトリック関数やクラスなど、他の形式のポリモーフィズムでは、動的型付けと比較すると非常に難しいと述べた方法で静的型言語の型を組み合わせることができます。
Edwin Dalorzo 2012

0

私は@Eric Lippertの答えが好きですが、質問に対処するために-強く型付けされた言語は、通常、プログラムの各ポイントで変数の型を明示的に知っています。弱く型付けされた言語はそうではないため、特定の型では不可能な操作を実行しようとする可能性があります。これを確認する最も簡単な方法は関数内にあると考えています。C ++:

void func(string a) {...}

変数aは文字列型であることがわかっており、互換性のない操作はコンパイル時にキャッチされます。

Python:

def func(a)
  ...

変数aは何でもかまいません。実行時にのみキャッチされる無効なメソッドを呼び出すコードを含めることができます。


12
動的な型付けと静的な型付けを、強い型付けと弱い型付けで混同しているかもしれません。コードの両方のバージョンで、ランタイム型システムはaが文字列であることを非常によく知っています。最初のケースではコンパイラがそれを伝えることができるだけですが、2番目のケースではできません。しかし、これによってこれらの言語が弱く型付けされることはありません。
Edwin Dalorzo
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.