Ich suche Klarheit über offensichtliche Widersprüche in Bezug auf schwach typisierte Sprachen

178

Ich glaube, ich verstehe starkes Tippen , aber jedes Mal, wenn ich nach Beispielen für schwaches Tippen suche, finde ich Beispiele für Programmiersprachen, die Typen einfach automatisch erzwingen / konvertieren.

In diesem Artikel mit dem Namen Typing: Strong vs. Weak sagt Static vs. Dynamic beispielsweise , dass Python stark typisiert ist, da Sie eine Ausnahme erhalten, wenn Sie versuchen:

Python

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

Dies ist jedoch in Java und in C # möglich, und wir betrachten sie nicht als schwach typisiert.

Java

  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);

In diesem anderen Artikel mit dem Namen " Schwach typisierte Sprachen" sagt der Autor, dass Perl schwach typisiert ist, nur weil ich eine Zeichenfolge ohne explizite Konvertierung mit einer Zahl und umgekehrt verketten kann.

Perl

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

Das gleiche Beispiel macht Perl schwach typisiert, aber nicht Java und C #?.

Das ist verwirrend Geben Sie hier die Bildbeschreibung ein

Die Autoren scheinen zu implizieren, dass eine Sprache, die die Anwendung bestimmter Operationen auf Werte verschiedener Typen verhindert, stark typisiert ist und das Gegenteil schwach typisiert bedeutet.

Daher habe ich mich irgendwann veranlasst zu glauben, dass eine Sprache, die viele automatische Konvertierungen oder Zwänge zwischen Typen (als Perl) bietet, möglicherweise als schwach typisiert angesehen wird, während andere Sprachen, die nur wenige Konvertierungen bereitstellen, möglicherweise als schwach typisiert gelten als stark getippt angesehen.

Ich neige jedoch dazu zu glauben, dass ich mich in dieser Interpretation irren muss, ich weiß einfach nicht warum oder wie ich es erklären soll.

Meine Fragen sind also:

  • Was bedeutet es wirklich, wenn eine Sprache wirklich schwach geschrieben ist?
  • Können Sie gute Beispiele für schwaches Tippen nennen, die nicht mit der automatischen Konvertierung / dem automatischen Zwang der Sprache zusammenhängen?
  • Kann eine Sprache gleichzeitig schwach und stark typisiert werden?
Edwin Dalorzo
quelle
8
Bei starker und schwacher Eingabe dreht sich alles um die Typkonvertierung (worum könnte es sonst noch gehen?) Wenn Sie ein Beispiel für eine "sehr" schwache Sprache wünschen , sehen Sie sich Folgendes an : destroyallsoftware.com/talks/wat .
Wilduck
2
@Wildduck Alle Sprachen bieten Typkonvertierungen, aber nicht alle gelten als schwach typisiert. Meine unten gezeigten Beispiele zeigen, wie Programmierer eine Sprache als schwach typisiert betrachten, basierend auf denselben Beispielen, die für andere Sprachen möglich sind, die als stark typisiert gelten. Als solches hat meine Frage immer noch Vorrang. Was ist der Unterschied?
Edwin Dalorzo
1
Die kurze Antwort, denke ich, ist, dass "Typisierung" kein binärer Zustand ist. Java und C # sind stärker typisiert, aber nicht absolut.
Jodrell
3
Ich glaube, das ist besser für Software Engineering geeignet .
zzzzBov
4
@Brendan Was ist mit der Summierung eines Floats und einer Ganzzahl? Wird die Ganzzahl in Python nicht zu einem Float gezwungen? Würden Sie jetzt sagen, dass Python nicht absolut stark typisiert ist?
Edwin Dalorzo

Antworten:

210

UPDATE: Diese Frage war das Thema meines Blogs am 15. Oktober 2012. Danke für die tolle Frage!


Was bedeutet es wirklich, wenn eine Sprache "schwach getippt" ist?

Es bedeutet "diese Sprache verwendet ein Typensystem, das ich als unangenehm empfinde". Eine "stark typisierte" Sprache ist dagegen eine Sprache mit einem Typensystem, das ich angenehm finde.

Die Begriffe sind im Wesentlichen bedeutungslos und sollten vermieden werden. Wikipedia listet elf verschiedene Bedeutungen für "stark typisiert" auf, von denen einige widersprüchlich sind. Dies weist darauf hin, dass die Wahrscheinlichkeit einer Verwirrung bei Gesprächen mit dem Begriff "stark typisiert" oder "schwach typisiert" hoch ist.

Alles, was Sie wirklich mit Sicherheit sagen können, ist, dass eine "stark typisierte" Sprache, über die diskutiert wird, entweder zur Laufzeit oder zur Kompilierungszeit eine zusätzliche Einschränkung im Typensystem aufweist, die einer "schwach typisierten" Sprache, über die diskutiert wird, fehlt. Was diese Einschränkung sein könnte, kann nicht ohne weiteren Kontext bestimmt werden.

Anstatt "stark typisiert" und "schwach typisiert" zu verwenden, sollten Sie detailliert beschreiben, welche Art von Typensicherheit Sie meinen. Zum Beispiel ist C # eine statisch typisierte Sprache und eine typsichere Sprache und eine speichersichere Sprache. zum größten Teil. Mit C # können alle drei Formen der "starken" Typisierung verletzt werden. Der Cast-Operator verletzt die statische Typisierung. Dem Compiler heißt es: "Ich weiß mehr über den Laufzeittyp dieses Ausdrucks als Sie." Wenn der Entwickler falsch liegt, löst die Laufzeit eine Ausnahme aus, um die Typensicherheit zu schützen. Wenn der Entwickler die Typensicherheit oder die Speichersicherheit aufheben möchte, kann er dies tun, indem er das Typensicherheitssystem ausschaltet, indem er einen "unsicheren" Block erstellt. In einem unsicheren Block können Sie Zeigermagie verwenden, um ein Int als Float zu behandeln (was die Typensicherheit verletzt) ​​oder um in einen Speicher zu schreiben, den Sie nicht besitzen. (Verletzung der Speichersicherheit.)

C # legt Typeinschränkungen fest, die sowohl zur Kompilierungszeit als auch zur Laufzeit überprüft werden. Dadurch wird C # zu einer "stark typisierten" Sprache im Vergleich zu Sprachen, die weniger Überprüfungen zur Kompilierungszeit oder weniger zur Laufzeit durchführen. Mit C # können Sie unter bestimmten Umständen auch einen Endlauf um diese Einschränkungen durchführen. Dies macht es zu einer "schwach typisierten" Sprache im Vergleich zu Sprachen, in denen Sie einen solchen Endlauf nicht durchführen können.

Welches ist es wirklich? Es ist unmöglich zu sagen; Dies hängt von der Sichtweise des Sprechers und seiner Einstellung zu den verschiedenen Sprachmerkmalen ab.

Eric Lippert
quelle
14
@edalorzo: Es basiert auf Geschmack und persönlichen Meinungen darüber, (1) welche Aspekte der Typentheorie relevant und welche irrelevant sind und (2) ob eine Sprache erforderlich ist, um Typbeschränkungen durchzusetzen oder lediglich zu fördern . Wie ich bereits betont habe, könnte man vernünftigerweise sagen, dass C # stark typisiert ist, weil es statisches Tippen erlaubt und fördert , und man könnte genauso vernünftigerweise sagen, dass es schwach typisiert ist, weil es die Möglichkeit erlaubt, die Typensicherheit zu verletzen.
Eric Lippert
4
@edalorzo: Auch bei der Montage ist es Ansichtssache. Mit einem Assembler-Compiler können Sie kein 64-Bit-Double vom Stapel in ein 32-Bit-Register verschieben. Damit können Sie einen 32-Bit-Zeiger auf ein 64-Bit-Double vom Stapel in ein 32-Bit-Register verschieben. In diesem Sinne ist die Sprache "typsicher" - sie schränkt die Rechtmäßigkeit des Programms auf der Grundlage einer Typklassifizierung von Daten ein. Ob diese Einschränkung "stark" oder "schwach" ist, ist Ansichtssache, aber eindeutig eine Einschränkung.
Eric Lippert
2
Ich denke, ich verstehe Ihren Standpunkt jetzt, eine wirklich schwach typisierte Sprache müsste völlig untypisiert oder monotypisiert sein, was im wirklichen Leben praktisch unmöglich ist. Als solche hat jede Sprache eine bestimmte Definition von Typen, die sicher sind, und abhängig von der Anzahl der Lücken, die die Sprache bietet, um ihre Daten oder Datentypen zu verletzen oder zu manipulieren, kann es sein, dass Sie sie als mehr oder weniger schwach typisiert betrachten, vielleicht sogar in nur bestimmte Kontexte.
Edwin Dalorzo
7
@edalorzo: Richtig. Zum Beispiel ist der untypisierte Lambda-Kalkül so schwach typisiert, wie es nur geht. Jede Funktion ist eine Funktion von einer Funktion zu einer Funktion; Alle Daten können ohne Einschränkung an jede Funktion übergeben werden, da alles vom "gleichen Typ" ist. Die Gültigkeit eines Ausdrucks im untypisierten Lambda-Kalkül hängt nur von seiner syntaktischen Form ab, nicht von einer semantischen Analyse, die bestimmte Ausdrücke als bestimmte Typen klassifiziert.
Eric Lippert
3
@Mark Ich würde ihm eine weitere +1 geben, um vorherzusagen, dass jeder unterschiedliche Interpretationen zu diesem Thema liefern würde. Diese "schwache Typisierung" scheint ein "mythisches Konzept" oder eine "urbane Legende" zu sein, jeder hat es gesehen, aber niemand kann beweisen, dass es existiert :-)
Edwin Dalorzo
64

Wie andere angemerkt haben, haben die Begriffe "stark typisiert" und "schwach typisiert" so viele verschiedene Bedeutungen, dass es keine einzige Antwort auf Ihre Frage gibt. Da Sie Perl in Ihrer Frage ausdrücklich erwähnt haben, möchte ich versuchen zu erklären, in welchem ​​Sinne Perl schwach typisiert ist.

Der Punkt ist, dass es in Perl keine "Integer-Variable", "Float-Variable", "String-Variable" oder "Boolesche Variable" gibt. Soweit der Benutzer (normalerweise) sagen kann, gibt es nicht einmal ganzzahlige, float-, string- oder boolesche Werte : Alles, was Sie haben, sind "Skalare", die all diese Dinge gleichzeitig sind. So können Sie zum Beispiel schreiben:

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

Natürlich kann all dies, wie Sie richtig bemerken, nur als Typenzwang angesehen werden. Der Punkt ist jedoch, dass in Perl Typen immer gezwungen werden. Tatsächlich ist es für einen Benutzer ziemlich schwierig zu sagen, wie der interne "Typ" einer Variablen aussehen könnte: In Zeile 2 in meinem obigen Beispiel ist die Frage, ob der Wert von $bardie Zeichenfolge "9"oder die Zahl 9ist, ziemlich bedeutungslos, da as Für Perl sind das die gleichen . In der Tat ist es sogar möglich, dass ein Perl-Skalar intern gleichzeitig eine Zeichenfolge und einen numerischen Wert hat, wie dies beispielsweise der Fall ist$foo nach Zeile 2 oben .

Die Kehrseite all dessen ist, dass Operatoren nicht überladen werden können, um verschiedene Dinge für verschiedene Arten von Argumenten zu tun, da Perl-Variablen untypisiert sind (oder ihren internen Typ nicht dem Benutzer zugänglich machen). Sie können nicht einfach sagen, dass dieser Operator X für Zahlen und Y für Zeichenfolgen ausführt, da der Operator nicht sagen kann (wird), welche Art von Werten seine Argumente sind.

So hat und benötigt Perl beispielsweise sowohl einen numerischen Additionsoperator ( +) als auch einen String-Verkettungsoperator ( .): Wie Sie oben gesehen haben, ist es vollkommen in Ordnung, Strings ( "1" + "2" == "3") hinzuzufügen oder Zahlen ( 1 . 2 == 12) zu verketten . Ähnlich sind die numerischen Vergleichsoperatoren ==, !=, <, >, <=, >=und <=>vergleichen die numerischen Werte ihrer Argumente, während die Zeichenfolge Vergleichsoperatoren eq, ne, lt, gt, le, geund cmpsie vergleichen lexikografisch als Strings. Also 2 < 10, aber 2 gt 10(aber "02" lt 10, während "02" == 2). (Wohlgemerkt, sicher andere Sprachen, wie JavaScript, versuchen dabei, Perl-ähnliche schwache Eingaben zu berücksichtigenauch Bedienerüberlastung. Dies führt oft zu Hässlichkeit, wie zum Beispiel dem Verlust der Assoziativität für +.)

(Die Fliege in der Salbe hier ist, dass Perl 5 aus historischen Gründen einige Eckfälle hat, wie die bitweisen logischen Operatoren, deren Verhalten von der internen Darstellung ihrer Argumente abhängt. Diese werden im Allgemeinen als störender Konstruktionsfehler angesehen, da Die interne Darstellung kann sich aus überraschenden Gründen ändern. Daher kann es schwierig sein, genau vorherzusagen, was diese Bediener in einer bestimmten Situation tun.)

Alles , was gesagt, könnte man argumentieren , dass Perl tut starke Typen haben; Sie sind einfach nicht die Art von Typen, die Sie erwarten könnten. Zusätzlich hat Perl zusätzlich zu dem oben diskutierten "Skalartyp" auch zwei strukturierte Typen: "Array" und "Hash". Diese unterscheiden sich stark von Skalaren bis zu dem Punkt, an dem Perl-Variablen unterschiedliche Siegel aufweisen , die ihren Typ angeben ( $für Skalare, @für Arrays, %für Hashes) 1 . Es gibt Zwangsregeln zwischen diesen Typen, so dass Sie zB schreiben können%foo = @bar , aber viele von ihnen sind ziemlich verlustbehaftet: Weist beispielsweise $foo = @bardie Länge des Arrays @barzu$foo, nicht sein Inhalt. (Es gibt auch einige andere seltsame Typen, wie Typeglobs und E / A-Handles, die Sie nicht oft offen sehen.)

Auch eine leichte Ritze in diesem schönen Design ist die Existenz von Referenztypen, die eine besondere Art von Skalaren sind (und das kann von normalen Skalaren zu unterscheiden, mit dem refOperator). Es ist möglich, Referenzen als normale Skalare zu verwenden, aber ihre Zeichenfolge / numerischen Werte sind nicht besonders nützlich, und sie neigen dazu, ihre spezielle Referenz zu verlieren, wenn Sie sie mit normalen Skalaroperationen ändern. Außerdem kann jede Perl-Variable 2 einbless zu einer Klasse ED, es in ein Objekt dieser Klasse drehen; Das OO-Klassensystem in Perl ist etwas orthogonal zu dem oben beschriebenen System des primitiven Typs (oder der Typlosigkeit), obwohl es auch im Sinne der Verfolgung der Ententypisierung "schwach" istParadigma. Die allgemeine Meinung ist, dass Sie etwas falsch machen, wenn Sie die Klasse eines Objekts in Perl überprüfen.


1 Tatsächlich bezeichnet das Siegel den Typ des Werts, auf den zugegriffen wird, so dass z. B. der erste Skalar im Array angegeben @foowird $foo[0]. Siehe perlfaq4 für weitere Details.

2 Auf Objekte in Perl wird (normalerweise) durch Verweise auf sie zugegriffen, aber was tatsächlich bearbeitet wird, blessist die (möglicherweise anonyme) Variable, auf die die Referenz verweist. Der Segen ist jedoch in der Tat eine Eigenschaft der Variablen, nicht ihres Wertes. Wenn Sie beispielsweise die tatsächliche gesegnete Variable einer anderen zuweisen, erhalten Sie nur eine flache, nicht gesegnete Kopie davon. Siehe perlobj für weitere Details.

Ilmari Karonen
quelle
19

Beachten Sie zusätzlich zu den Aussagen von Eric den folgenden C-Code:

void f(void* x);

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

Im Gegensatz zu Sprachen wie Python, C #, Java oder so weiter ist das Obige schwach typisiert, da wir Typinformationen verlieren . Eric hat richtig darauf hingewiesen, dass wir in C # den Compiler umgehen können, indem wir ihn umwandeln und ihm effektiv sagen: "Ich weiß mehr über den Typ dieser Variablen als Sie".

Aber selbst dann überprüft die Laufzeit immer noch den Typ! Wenn die Umwandlung ungültig ist, wird sie vom Laufzeitsystem abgefangen und eine Ausnahme ausgelöst.

Beim Löschen von Typen geschieht dies nicht - Typinformationen werden weggeworfen. Eine Besetzung void*in C macht genau das. In dieser Hinsicht unterscheidet sich das Obige grundlegend von einer C # -Methodendeklaration wie z void f(Object x).

(Technisch gesehen ermöglicht C # auch das Löschen von Typen durch unsicheren Code oder Marshalling.)

Dies ist so schwach getippt wie es nur geht. Alles andere ist nur eine Frage der statischen vs. dynamische Typprüfung, dh der Zeit , wenn eine Art überprüft wird.

Konrad Rudolph
quelle
1
+1 Guter Punkt, Sie haben mich jetzt dazu gebracht, die Typlöschung als eine Funktion zu betrachten, die auch "schwache Typisierung" implizieren kann. Es gibt auch in Java eine Typlöschung, und zur Laufzeit können Sie mit dem Typsystem Einschränkungen verletzen, die der Compiler niemals genehmigen würde. Das C-Beispiel ist hervorragend, um den Punkt zu veranschaulichen.
Edwin Dalorzo
1
Einverstanden, es gibt Schichten zur Zwiebel oder zum Inferno. Dies scheint eine bedeutendere Definition der Typschwäche zu sein.
Jodrell
1
@edalorzo Ich denke nicht, dass dies ganz dasselbe ist, da Java zwar die Umgehung des Compilers ermöglicht, das System vom Typ Laufzeit jedoch dennoch die Verletzung abfängt . Daher ist das Java-Laufzeittypsystem in dieser Hinsicht stark typisiert (es gibt Ausnahmen, z. B. wo Reflektion verwendet werden kann, um die Zugriffskontrolle zu umgehen).
Konrad Rudolph
1
@edalorzo Sie können den Compiler nur auf diese Weise umgehen, nicht das Laufzeitsystem. Es ist wichtig zu wissen, dass Sprachen wie Java und C # (und bis zu einem gewissen Grad auch C ++) ein Typsystem haben, das zweimal gewährleistet ist: einmal zur Kompilierungszeit und einmal zur Laufzeit. void*durchbricht beide Typprüfungen. Das Löschen mit generischem Typ funktioniert nicht, es umgeht nur die Überprüfungen zur Kompilierungszeit. Diesbezüglich ist es genau wie bei expliziten Besetzungen (von Eric erwähnt).
Konrad Rudolph
1
@edalorzo Re Ihre Verwirrung: Wir sollten nicht. Die Unterscheidung ist fließend. Und ja, durch das Löschen von Schriftarten wird Java in dieser Hinsicht schwach typisiert. Mein Punkt war, dass Sie auch mit generischer Löschung immer noch nicht die Laufzeittypprüfungen umgehen können , wenn Sie auch Reflexion verwenden .
Konrad Rudolph
14

Ein perfektes Beispiel stammt aus dem Wikipedia-Artikel von Strong Typing :

Im Allgemeinen bedeutet eine starke Typisierung, dass die Programmiersprache die zulässige Vermischung stark einschränkt.

Schwache Eingabe

a = 2
b = "2"

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

Starkes Tippen

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

Beachten Sie, dass eine schwache Tippsprache verschiedene Typen fehlerfrei vermischen kann. Für eine starke Typensprache müssen die Eingabetypen die erwarteten Typen sein. In einer starken Typensprache kann ein Typ konvertiert ( str(a)konvertiert eine Ganzzahl in eine Zeichenfolge) oder cast ( int(b)) werden.

Dies alles hängt von der Interpretation der Eingabe ab.

SaulBack
quelle
3
Dies führt jedoch zu den widersprüchlichen Beispielen in der Frage. Eine stark typisierte Sprache kann impliziten Zwang enthalten, was bedeutet, dass eines (oder beide) Ihrer beiden "Typfehler" -Beispiele automatisch in das relevante der beiden zweiten Beispiele konvertiert wird, aber im Allgemeinen ist diese Sprache immer noch stark typisiert.
Mark Hurd
3
Wahr. Ich denke, man könnte sagen, dass es unterschiedliche Grade starker und schwacher Typisierung gibt. Implizite Konvertierung kann bedeuten, dass die Sprache weniger stark typisiert ist als eine Sprache, die keine implizite Konvertierung durchführt.
SaulBack
4

Ich möchte mit meiner eigenen Forschung zu diesem Thema zur Diskussion beitragen, da andere Kommentare abgeben und Beiträge leisten. Ich habe ihre Antworten gelesen und ihren Referenzen gefolgt und interessante Informationen gefunden. Wie vorgeschlagen, ist es wahrscheinlich, dass das meiste davon besser im Programmiererforum diskutiert wird, da es eher theoretisch als praktisch zu sein scheint.

Aus theoretischer Sicht denke ich, dass der Artikel von Luca Cardelli und Peter Wegner mit dem Titel Über das Verständnis von Typen, Datenabstraktion und Polymorphismus eines der besten Argumente hat, die ich gelesen habe.

Ein Typ kann als ein Satz Kleidung (oder ein Rüstungsanzug) angesehen werden, der eine zugrunde liegende untypisierte Darstellung vor willkürlicher oder unbeabsichtigter Verwendung schützt . Es bietet eine Schutzhülle, die die zugrunde liegende Darstellung verbirgt und die Interaktion von Objekten mit anderen Objekten einschränkt. In einem untypisierten System sind untypisierte Objekte insofern nackt , als die zugrunde liegende Darstellung für alle sichtbar ist. Bei Verstößen gegen das Typensystem wird das Schutzkleidungsset entfernt und die nackte Darstellung direkt bearbeitet.

Diese Aussage scheint darauf hinzudeuten, dass eine schwache Typisierung es uns ermöglichen würde, auf die innere Struktur eines Typs zuzugreifen und ihn so zu manipulieren, als wäre es etwas anderes (ein anderer Typ). Vielleicht, was wir mit unsicherem Code (von Eric erwähnt) oder mit von Konrad erwähnten c-Typ-gelöschten Zeigern tun könnten.

Der Artikel geht weiter ...

Sprachen, in denen alle Ausdrücke typkonsistent sind, werden als stark typisierte Sprachen bezeichnet. Wenn eine Sprache stark typisiert ist, kann ihr Compiler garantieren, dass die von ihm akzeptierten Programme ohne Tippfehler ausgeführt werden. Im Allgemeinen sollten wir uns um eine starke Typisierung bemühen und, wann immer möglich, eine statische Typisierung anwenden. Beachten Sie, dass jede statisch typisierte Sprache stark typisiert ist, das Gegenteil jedoch nicht unbedingt der Fall ist.

Starke Typisierung bedeutet daher das Fehlen von Typfehlern. Ich kann nur davon ausgehen, dass schwache Typisierung das Gegenteil bedeutet: das wahrscheinliche Vorhandensein von Typfehlern. Zur Laufzeit oder zur Kompilierungszeit? Scheint hier irrelevant.

Lustige Sache, gemäß dieser Definition, würde eine Sprache mit mächtigen Typenzwängen wie Perl als stark typisiert angesehen, da das System nicht ausfällt, sondern sich mit den Typen befasst, indem sie in geeignete und genau definierte Äquivalenzen gezwungen werden.

Auf der anderen Seite könnte ich sagen, als die Erlaubnis von ClassCastExceptionund ArrayStoreException(in Java) und InvalidCastException,ArrayTypeMismatchException (in C #) würde ein Niveau von schwach Typisierung zeigen, zumindest zum Zeitpunkt der Kompilierung? Erics Antwort scheint damit übereinzustimmen.

In einem zweiten Artikel mit dem Titel Typeful Programming, der in einer der Referenzen in einer der Antworten in dieser Frage enthalten ist, befasst sich Luca Cardelli mit dem Konzept von Typverletzungen:

Die meisten Systemprogrammiersprachen erlauben willkürliche Typverletzungen, einige wahllos, andere nur in eingeschränkten Teilen eines Programms. Vorgänge mit Typverletzungen werden als nicht einwandfrei bezeichnet. Typverstöße fallen in mehrere Klassen [unter denen wir erwähnen können]:

Grundwert Nötigungen : Dazu gehören Konvertierungen zwischen ganzen Zahlen, Boolesche Werte, Zeichen, Sätzen, etc. Es besteht keine Notwendigkeit für Typen Verletzungen hier, weil integrierten Schnittstellen kann die Nötigungen in einem typgerecht auszuführen zur Verfügung gestellt werden.

Als solche können Typzwänge, wie sie von Operatoren bereitgestellt werden, als Typverletzungen angesehen werden. Wenn sie jedoch nicht die Konsistenz des Typsystems beeinträchtigen, können wir sagen, dass sie nicht zu einem schwach typisierten System führen.

Basierend darauf sind weder Python, Perl, Java noch C # schwach typisiert.

Cardelli erwähnt zwei Arten von Verleumdungen, die ich sehr gut als Fälle von wirklich schwacher Typisierung betrachte:

Adressarithmetik. Falls erforderlich, sollte eine integrierte (nicht einwandfreie) Schnittstelle vorhanden sein, die die entsprechenden Operationen für Adressen und Typkonvertierungen bereitstellt. Verschiedene Situationen umfassen Zeiger in den Heap (sehr gefährlich beim Verschieben von Kollektoren), Zeiger auf den Stapel, Zeiger auf statische Bereiche und Zeiger auf andere Adressräume. Manchmal kann die Array-Indizierung die Adressarithmetik ersetzen. Speicherzuordnung. Dies beinhaltet das Betrachten eines Speicherbereichs als unstrukturiertes Array, obwohl es strukturierte Daten enthält. Dies ist typisch für Speicherzuordnungen und -kollektoren.

Diese Art von Dingen, die in Sprachen wie C (von Konrad erwähnt) oder durch unsicheren Code in .Net (von Eric erwähnt) möglich sind, würden wirklich eine schwache Eingabe bedeuten.

Ich glaube, die beste Antwort ist bisher die von Eric, da die Definition dieser Konzepte sehr theoretisch ist und die Interpretation all dieser Konzepte zu unterschiedlichen umstrittenen Schlussfolgerungen führen kann, wenn es um eine bestimmte Sprache geht.

Edwin Dalorzo
quelle
4

Eine schwache Typisierung bedeutet in der Tat, dass ein hoher Prozentsatz der Typen implizit erzwungen werden kann, um zu erraten, was der Codierer beabsichtigt hat.

Starke Typisierung bedeutet, dass Typen nicht oder zumindest weniger gezwungen werden.

Statische Typisierung bedeutet, dass die Typen Ihrer Variablen zur Kompilierungszeit bestimmt werden.

Viele Leute haben in letzter Zeit "offensichtlich getippt" mit "stark getippt" verwechselt. "Offensichtlich typisiert" bedeutet, dass Sie die Typen Ihrer Variablen explizit deklarieren.

Python ist meistens stark typisiert, obwohl Sie fast alles in einem booleschen Kontext verwenden können, und Boolesche Werte können in einem ganzzahligen Kontext verwendet werden, und Sie können eine Ganzzahl in einem Float-Kontext verwenden. Es ist nicht offensichtlich typisiert, da Sie Ihre Typen nicht deklarieren müssen (mit Ausnahme von Cython, das nicht ganz Python ist, wenn auch interessant). Es ist auch nicht statisch typisiert.

C und C ++ sind offensichtlich typisiert, statisch typisiert und etwas stark typisiert, da Sie Ihre Typen deklarieren, Typen zur Kompilierungszeit festgelegt werden und Sie Ganzzahlen und Zeiger oder Ganzzahlen und Doppelwerte mischen oder sogar einen Zeiger auf einen Typ umwandeln können ein Zeiger auf einen anderen Typ.

Haskell ist ein interessantes Beispiel, da es nicht offensichtlich typisiert ist, sondern auch statisch und stark typisiert.

user1277476
quelle
+1 Weil mir der geprägte Begriff "offensichtlich typisiert" gefällt, der Sprachen wie Java und C # kategorisiert, in denen Sie Typen explizit deklarieren und von anderen statisch typisierten Sprachen wie Haskell und Scala unterscheiden müssen, in denen Typinferenz eine wichtige Rolle spielt, und dies ist typisch verwirrt die Leute, wie Sie sagen, und lässt sie glauben, dass diese Sprachen dynamisch typisiert sind.
Edwin Dalorzo
3

Bei der starken <=> schwachen Typisierung geht es nicht nur um das Kontinuum, wie viel oder wie wenig der Werte automatisch von der Sprache für einen Datentyp zu einem anderen gezwungen werden, sondern auch darum, wie stark oder schwach die tatsächlichen Werte typisiert werden. In Python und Java und hauptsächlich in C # sind die Typen der Werte in Stein gemeißelt. In Perl nicht so sehr - es gibt wirklich nur eine Handvoll verschiedener Werttypen, die in einer Variablen gespeichert werden können.

Lassen Sie uns die Fälle einzeln öffnen.


Python

In Python Beispiel 1 + "1", +Operator ruft die __add__für Typ intdie Zeichenfolge geben "1"als Argument - aber diese Ergebnisse in NotImplemented:

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

Als nächstes versucht der Interpreter das __radd__von str:

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

Wenn dies fehlschlägt, schlägt der +Bediener mit dem Ergebnis fehl TypeError: unsupported operand type(s) for +: 'int' and 'str'. Insofern sagt die Ausnahme nicht viel über starke Typisierung aus, aber die Tatsache, dass der Operator seine Argumente nicht automatisch auf denselben Typ + zwingt , ist ein Hinweis darauf, dass Python nicht die am schwächsten typisierte Sprache im Kontinuum ist.

Auf der anderen Seite, in Python 'a' * 5 ist implementiert:

>>> 'a' * 5
'aaaaa'

Das ist,

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

Die Tatsache, dass die Operation anders ist, erfordert eine starke Typisierung - das Gegenteil *davon, die Werte vor dem Multiplizieren zu Zahlen zu zwingen, würde die Werte jedoch nicht unbedingt schwach typisieren.


Java

Das Java-Beispiel String result = "1" + 1;funktioniert nur, weil der Operator der Einfachheit +halber für Zeichenfolgen überladen ist. Der Java- +Operator ersetzt die Sequenz durch das Erstellen eines StringBuilder(siehe hier ):

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

Dies ist eher ein Beispiel für eine sehr statische Typisierung ohne tatsächlichen Zwang - StringBuilderhat eine Methode append(Object), die hier speziell verwendet wird. In der Dokumentation heißt es:

Hängt die Zeichenfolgendarstellung des ObjectArguments an.

Der Gesamteffekt ist genau so, als ob das Argument von der Methode in eine Zeichenfolge konvertiert würde String.valueOf(Object)und die Zeichen dieser Zeichenfolge dann an diese Zeichenfolge angehängt würden.

Wo String.valueOfdann ?

Gibt die Zeichenfolgendarstellung des Object-Arguments zurück. [Gibt zurück] Wenn das Argument lautet null, ist eine Zeichenfolge gleich "null"; Andernfalls wird der Wert von obj.toString()zurückgegeben.

Dies ist also ein Fall, in dem die Sprache absolut keinen Zwang ausübt und jedes Anliegen an die Objekte selbst delegiert.


C #

Laut der Antwort von Jon Skeet ist der Operator +für die stringKlasse nicht einmal überlastet - ähnlich wie bei Java. Dies ist nur die Bequemlichkeit, die der Compiler dank statischer und starker Typisierung generiert.


Perl

Wie die Perldata erklärt,

Perl verfügt über drei integrierte Datentypen: Skalare, Arrays von Skalaren und assoziative Arrays von Skalaren, die als "Hashes" bezeichnet werden. Ein Skalar ist eine einzelne Zeichenfolge (von beliebiger Größe, die nur durch den verfügbaren Speicher begrenzt ist), eine Zahl oder ein Verweis auf etwas (das in Perlref erläutert wird). Normale Arrays sind geordnete Listen von Skalaren, die nach Zahlen indiziert sind, beginnend mit 0. Hashes sind ungeordnete Sammlungen von Skalarwerten, die durch den zugehörigen Zeichenfolgenschlüssel indiziert werden.

Perl hat jedoch keinen separaten Datentyp für Zahlen, Boolesche Werte, Zeichenfolgen, Nullen, undefineds, Verweise auf andere Objekte usw. - es gibt nur einen Typ für diese alle, den Skalartyp. 0 ist ein Skalarwert ebenso wie "0". Eine skalare Variable , die als Zeichenfolge festgelegt wurde, kann sich wirklich in eine Zahl ändern und sich von da an anders verhalten als "nur eine Zeichenfolge" wenn in einem numerischen Kontext darauf zugegriffen wird. Der Skalar kann alles in Perl enthalten, er ist genauso das Objekt wie es im System existiert. Während sich in Python die Namen nur auf die Objekte beziehen, sind in Perl die Skalarwerte in den Namen veränderbare Objekte. Darüber hinaus wird das objektorientierte Typsystem aufgeklebt: Perl-Skalare, Listen und Hashes enthalten nur 3 Datentypen.blesszu einem Paket - Sie können jeden solchen Wert jederzeit für jede Klasse segnen.

Mit Perl können Sie sogar die Werteklassen nach Belieben ändern. Dies ist in Python nicht möglich. Wenn Sie einen Wert für eine Klasse erstellen möchten, müssen Sie den zu dieser Klasse gehörenden Wert explizit mit object.__new__oder ähnlich konstruieren . In Python können Sie die Essenz des Objekts nach der Erstellung nicht wirklich ändern, in Perl können Sie vieles tun:

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";

Somit ist die Typidentität schwach an die Variable gebunden und kann durch jede Referenz im laufenden Betrieb geändert werden. In der Tat, wenn Sie dies tun

my $another = $val;

\$anotherhat nicht die Klassenidentität, obwohl \$valimmer noch die gesegnete Referenz geben wird.


TL; DR

Schwaches Tippen in Perl ist viel mehr als nur automatisches Erzwingen, und es geht mehr darum, dass die Typen der Werte selbst nicht in Stein gemeißelt sind, im Gegensatz zu Python, das eine dynamisch und dennoch sehr stark typisierte Sprache ist. Das Python gibt TypeErrorauf 1 + "1"ist ein Hinweis darauf , dass die Sprache eingegeben wird , stark, obwohl das Gegenteil einer etwas Nützliches, wie in Java oder C # tut nicht ausschließen , sie stark typisierte Sprachen zu sein.

Antti Haapala
quelle
Das ist total verwirrt. Dass Perl 5- Variablen keine Typen haben, hat keinen Einfluss auf Werte , die immer einen Typ haben.
Jim Balter
@JimBalter Nun ja, ein Wert hat den Typ, dass es sich um eine Zeichenfolge oder eine Zahl handelt, und er kann sich in einem bestimmten Kontext unterschiedlich verhalten, je nachdem, ob die skalare Variable eine Zeichenfolge oder eine Zahl enthält. Der in einer Variablen enthaltene Wert kann jedoch den Typ ändern, indem nur auf die Variable zugegriffen wird. Da der Wert selbst in der Variablen gespeichert ist, können die Werte selbst als zwischen Typen veränderbar betrachtet werden.
Antti Haapala
Werte ändern nicht den Typ - das ist inkohärent; Ein Wert ist immer von einem Typ . Der Wert, den eine Variable enthält, kann sich ändern. Eine Änderung von 1 auf "1" ist ebenso eine Wertänderung wie eine Änderung von 1 auf 2.
Jim Balter
Eine schwach typisierte Sprache wie Perl ermöglicht es, dass die frühere Art der Wertänderung je nach Kontext implizit auftritt . Aber auch C ++ erlaubt solche impliziten Konvertierungen über Operatordefinitionen. Schwaches Tippen ist eine sehr informelle Eigenschaft und wirklich keine nützliche Art, Sprachen zu beschreiben, wie Eric Lippert betonte.
Jim Balter
PS Es kann gezeigt werden, dass <digits> und "<digits>" auch in Perl unterschiedliche Werte haben, nicht nur unterschiedliche Typen. Perl macht <Ziffern> und „<Ziffern>“ erscheinen den gleichen Wert in den meisten Fällen über haben implizite Konvertierungen , aber die Illusion ist nicht vollständig; zB "12" | "34" ist 36, während 12 | 34 ist 46. Ein anderes Beispiel ist, dass "00" in den meisten Kontexten numerisch gleich 00 ist, jedoch nicht im booleschen Kontext, in dem "00" wahr ist, 00 jedoch falsch.
Jim Balter
1

Wie viele andere zum Ausdruck gebracht haben, ist der gesamte Begriff der "starken" vs. "schwachen" Typisierung problematisch.

Als Archetyp ist Smalltalk sehr stark typisiert - es wird immer eine Ausnahme ausgelöst, wenn eine Operation zwischen zwei Objekten nicht kompatibel ist. Ich vermute jedoch, dass nur wenige auf dieser Liste Smalltalk als stark typisierte Sprache bezeichnen würden, da sie dynamisch typisiert ist.

Ich finde den Begriff "statisch" versus "dynamisch" nützlicher als "stark" versus "schwach". In einer statisch typisierten Sprache werden alle Typen zur Kompilierungszeit ermittelt, und der Programmierer muss dies explizit angeben, wenn dies nicht der Fall ist.

Im Gegensatz zu einer dynamisch typisierten Sprache, in der die Eingabe zur Laufzeit erfolgt. Dies ist normalerweise eine Voraussetzung für polymorphe Sprachen, sodass Entscheidungen darüber, ob eine Operation zwischen zwei Objekten legal ist, nicht im Voraus vom Programmierer entschieden werden müssen.

In polymorphen, dynamisch typisierten Sprachen (wie Smalltalk und Ruby) ist es sinnvoller, sich einen "Typ" als "Konformität mit dem Protokoll" vorzustellen. Wenn ein Objekt einem Protokoll auf die gleiche Weise gehorcht wie ein anderes Objekt - auch wenn die beiden Objekte keine Vererbung, Mixins oder andere Voodoos gemeinsam haben - werden sie vom Laufzeitsystem als der gleiche "Typ" betrachtet. Richtiger ist, dass ein Objekt in solchen Systemen autonom ist und entscheiden kann, ob es sinnvoll ist, auf eine bestimmte Nachricht zu antworten, die sich auf ein bestimmtes Argument bezieht.

Möchten Sie ein Objekt, das mit einem Objektargument, das die Farbe Blau beschreibt, eine aussagekräftige Antwort auf die Nachricht "+" geben kann? Sie können dies in dynamisch typisierten Sprachen tun, aber es ist ein Schmerz in statisch typisierten Sprachen.

Jan Steinman
quelle
3
Ich denke, dass das Konzept der dynamischen und statischen Typisierung nicht diskutiert wird. Obwohl ich sagen muss, dass ich nicht glaube, dass Polymorphismus in statisch typisierten Sprachen ohnehin behindert ist. Letztendlich überprüft das Typsystem, ob eine bestimmte Operation auf die angegebenen Operanden anwendbar ist, sei es zur Laufzeit oder zur Kompilierungszeit. Andere Formen des Polymorphismus, wie parametrische Funktionen und Klassen, ermöglichen es auch, Typen in statisch typisierten Sprachen auf eine Weise zu kombinieren, die Sie im Vergleich zu dynamisch typisierten als sehr schwierig beschrieben haben, und noch schöner, wenn Typinferenz bereitgestellt wird.
Edwin Dalorzo
0

Ich mag die Antwort von @Eric Lippert , aber um die Frage zu beantworten - stark typisierte Sprachen haben normalerweise explizite Kenntnisse über die Variablentypen an jedem Punkt des Programms. Schwach typisierte Sprachen tun dies nicht, sodass sie versuchen können, eine Operation auszuführen, die für einen bestimmten Typ möglicherweise nicht möglich ist. Ich denke, der einfachste Weg, dies zu sehen, ist eine Funktion. C ++:

void func(string a) {...}

Es aist bekannt, dass die Variable vom Typ string ist und jede inkompatible Operation zur Kompilierungszeit abgefangen wird.

Python:

def func(a)
  ...

Die Variable akann alles sein und wir können Code haben, der eine ungültige Methode aufruft, die nur zur Laufzeit abgefangen wird.

Lubo Antonov
quelle
12
Ich denke, Sie verwechseln möglicherweise dynamisches Tippen mit statischem Tippen mit starkem Tippen mit schwachem Tippen. In beiden Versionen Ihres Codes wissen die Laufzeitsysteme sehr gut, dass a eine Zeichenfolge ist. Es ist nur so, dass der Compiler Ihnen im ersten Fall sagen kann, dass er es im zweiten Fall nicht kann. Dies macht jedoch keine dieser Sprachen schwach typisiert.
Edwin Dalorzo