Welche Funktionalität ermöglicht die dynamische Eingabe? [geschlossen]

91

Ich benutze Python seit ein paar Tagen und ich denke, ich verstehe den Unterschied zwischen dynamischer und statischer Typisierung. Was ich nicht verstehe, ist, unter welchen Umständen es bevorzugt würde. Es ist flexibel und lesbar, jedoch auf Kosten von mehr Laufzeitprüfungen und zusätzlichen erforderlichen Komponententests.

Welche Gründe sprechen neben nichtfunktionalen Kriterien wie Flexibilität und Lesbarkeit für dynamisches Tippen? Was kann ich mit dynamischer Eingabe tun, die sonst nicht möglich ist? Welches konkrete Codebeispiel veranschaulicht einen konkreten Vorteil der dynamischen Typisierung?

Justin984
quelle
5
Theoretisch gibt es auch nichts, was Sie nicht tun können, solange die Sprachen Turing Complete sind . Die interessantere Frage für mich ist, was in einem gegen den anderen einfach oder natürlich ist. Es gibt Dinge, die ich regelmäßig in Python mache, die ich in C ++ nicht einmal in Betracht ziehen würde, obwohl ich weiß, dass es in der Lage ist.
Mark Ransom
28
Wie Chris Smith in seinem hervorragenden Aufsatz " Was Sie vor der Diskussion von Typensystemen wissen sollten " schreibt : "Das Problem in diesem Fall ist, dass die meisten Programmierer nur über begrenzte Erfahrung verfügen und nicht viele Sprachen ausprobiert haben. Für den Kontext hier sechs oder sieben zählt nicht als "viel" ... Zwei interessante Konsequenzen daraus sind: (1) Viele Programmierer haben sehr schlechte statisch typisierte Sprachen verwendet. (2) Viele Programmierer haben dynamisch typisierte Sprachen sehr schlecht verwendet. "
Daniel Pryden
3
@suslik: Wenn Sprachprimitive unsinnige Typen haben, können Sie natürlich unsinnige Dinge mit Typen tun. Das hat nichts mit dem Unterschied zwischen statischer und dynamischer Typisierung zu tun.
Jon Purdy
10
@CzarekTomczak: Das ist eine Funktion einiger dynamisch getippter Sprachen, ja. Es ist jedoch möglich, dass eine statisch typisierte Sprache zur Laufzeit geändert werden kann. In Visual Studio können Sie beispielsweise C # -Code neu schreiben, während Sie sich an einem Haltepunkt im Debugger befinden, und sogar den Anweisungszeiger zurückspulen, um den Code mit neuen Änderungen erneut auszuführen. Wie ich in meinem anderen Kommentar Chris Smith zitierte: "Viele Programmierer haben sehr schlechte statisch typisierte Sprachen verwendet" - beurteilen Sie nicht alle statisch typisierten Sprachen nach den Ihnen bekannten.
Daniel Pryden
11
@WarrenP: Sie behaupten, dass "dynamische Typsysteme die Menge der zusätzlichen Cruft reduzieren, die ich eintippen muss" - aber dann vergleichen Sie Python mit C ++. Das ist kein fairer Vergleich: Natürlich ist C ++ ausführlicher als Python, aber das liegt nicht an den Unterschieden in ihren Typsystemen, sondern an den Unterschieden in ihren Grammatiken. Wenn Sie nur die Anzahl der Zeichen in Ihrer Programmquelle reduzieren möchten, lernen Sie J oder APL: Ich garantiere, dass sie kürzer sind. Ein fairerer Vergleich wäre, Python mit Haskell zu vergleichen. (Fürs Protokoll: Ich liebe Python und bevorzuge es C ++, aber ich mag Haskell noch mehr.)
Daniel Pryden

Antworten:

50

Da Sie nach einem bestimmten Beispiel gefragt haben, gebe ich Ihnen eines.

Rob Conerys massiver ORM besteht aus 400 Codezeilen. Es ist so klein, weil Rob in der Lage ist, SQL-Tabellen abzubilden und Objektergebnisse bereitzustellen, ohne dass viele statische Typen erforderlich sind, um die SQL-Tabellen zu spiegeln. Dies wird mithilfe des dynamicDatentyps in C # erreicht. Robs Webseite beschreibt diesen Prozess im Detail, aber es scheint klar zu sein, dass in diesem speziellen Anwendungsfall die dynamische Typisierung zum großen Teil für die Kürze des Codes verantwortlich ist.

Vergleichen Sie mit Sam Saffrons Dapper , der statische Typen verwendet. Die SQLMapperKlasse allein besteht aus 3000 Codezeilen.

Beachten Sie, dass die üblichen Haftungsausschlüsse gelten und Ihr Kilometerstand variieren kann. Dapper hat andere Ziele als Massive. Ich zeige dies nur als Beispiel für eine Aktion, die Sie in 400 Codezeilen ausführen können, die ohne dynamisches Tippen wahrscheinlich nicht möglich wäre.


Mit der dynamischen Typisierung können Sie Ihre Typentscheidungen auf die Laufzeit verschieben. Das ist alles.

Unabhängig davon, ob Sie eine dynamisch oder statisch getippte Sprache verwenden, müssen Ihre Typwahlen dennoch sinnvoll sein. Sie werden nicht zwei Zeichenfolgen zusammenfügen und eine numerische Antwort erwarten, es sei denn, die Zeichenfolgen enthalten numerische Daten. Andernfalls erhalten Sie unerwartete Ergebnisse. Bei einer statisch getippten Sprache können Sie dies nicht in erster Linie tun.

Befürworter von Sprachen mit statischem Typ weisen darauf hin, dass der Compiler beim Kompilieren eine erhebliche Menge an "Sanity Checking" Ihres Codes durchführen kann, bevor eine einzelne Zeile ausgeführt wird. Dies ist eine gute Sache ™.

C # hat das dynamicSchlüsselwort, mit dem Sie die Typentscheidung auf die Laufzeit verschieben können, ohne die Vorteile der statischen Typensicherheit im Rest Ihres Codes zu verlieren. Typinferenz ( var) beseitigt den Aufwand für das Schreiben in einer statisch typisierten Sprache, indem es nicht mehr erforderlich ist, Typen immer explizit zu deklarieren.


Dynamische Sprachen scheinen eine interaktivere, unmittelbarere Herangehensweise an die Programmierung zu bevorzugen. Niemand erwartet, dass Sie eine Klasse schreiben und einen Kompilierungszyklus durchlaufen müssen, um ein bisschen Lisp-Code einzutippen und die Ausführung zu beobachten. Genau das soll ich aber in C # tun.

Robert Harvey
quelle
22
Wenn ich zwei numerische Zeichenfolgen zusammenaddiere, würde ich immer noch kein numerisches Ergebnis erwarten.
pdr
22
@ Robert Ich bin mit den meisten Ihrer Antwort einverstanden. Beachten Sie jedoch, dass es statisch typisierte Sprachen mit interaktiven Read-Eval-Print-Schleifen gibt, z. B. Scala und Haskell. Es kann sein, dass C # keine besonders interaktive Sprache ist.
Andres F.
14
Muss mir einen Haskell beibringen.
Robert Harvey
7
@RobertHarvey: Sie könnten von F # überrascht / beeindruckt sein, wenn Sie es noch nicht ausprobiert haben. Sie erhalten die gesamte (zur Kompilierungszeit) verfügbare Typensicherheit, die Sie normalerweise in einer .NET-Sprache erhalten, mit der Ausnahme, dass Sie selten Typen deklarieren müssen. Die Typinferenz in F # geht über das hinaus, was in C # verfügbar ist / funktioniert. Außerdem: Ähnlich wie Andres und Daniel, ist F # interactive Teil von Visual Studio ...
Steven Evers
8
"Sie werden nicht zwei Zeichenfolgen zusammenfügen und eine numerische Antwort erwarten, es sei denn, die Zeichenfolgen enthalten numerische Daten. Andernfalls erhalten Sie unerwartete Ergebnisse." Entschuldigung, dies hat nichts mit dynamischer oder statischer Typisierung zu tun Dies ist stark gegen schwache Typisierung.
Vartec
26

Ausdrücke wie "statisches Tippen" und "dynamisches Tippen" werden häufig verwendet, und die Leute tendieren dazu, subtil unterschiedliche Definitionen zu verwenden. Beginnen wir also damit, zu klären, was wir meinen.

Stellen Sie sich eine Sprache mit statischen Typen vor, die zur Kompilierungszeit überprüft werden. Nehmen wir jedoch an, dass ein Typfehler nur eine nicht schwerwiegende Warnung generiert und dass zur Laufzeit alles vom Typ "Ente" ist. Diese statischen Typen dienen nur dem Komfort des Programmierers und wirken sich nicht auf das Codegen aus. Dies zeigt, dass die statische Typisierung für sich genommen keine Einschränkungen mit sich bringt und sich bei der dynamischen Typisierung nicht gegenseitig ausschließt. (Objective-C ist sehr ähnlich.)

Die meisten statischen Systeme verhalten sich jedoch nicht so. Es gibt zwei allgemeine Eigenschaften von statischen Typsystemen, die Einschränkungen auferlegen können:

Der Compiler kann ein Programm ablehnen, das einen statischen Typfehler enthält.

Dies ist eine Einschränkung, da viele typsichere Programme notwendigerweise einen statischen Typfehler enthalten.

Ich habe beispielsweise ein Python-Skript, das sowohl als Python 2 als auch als Python 3 ausgeführt werden muss. Einige Funktionen haben ihre Parametertypen zwischen Python 2 und 3 geändert, sodass ich folgenden Code habe:

if sys.version_info[0] == 2:
    wfile.write(txt)
else:
    wfile.write(bytes(txt, 'utf-8'))

Eine statische Python 2-Typprüfung würde den Python 3-Code (und umgekehrt) ablehnen, obwohl er niemals ausgeführt würde. Mein typsicheres Programm enthält einen statischen Typfehler.

Ein weiteres Beispiel ist ein Mac-Programm, das unter OS X 10.6 ausgeführt werden soll, jedoch die neuen Funktionen in 10.7 nutzt. Die 10.7-Methoden können zur Laufzeit vorhanden sein oder auch nicht, und es liegt an mir, dem Programmierer, sie zu erkennen. Eine statische Typprüfung muss entweder mein Programm ablehnen, um die Typensicherheit zu gewährleisten, oder das Programm akzeptieren, zusammen mit der Möglichkeit, zur Laufzeit einen Typfehler (fehlende Funktion) zu erzeugen.

Bei der statischen Typprüfung wird davon ausgegangen, dass die Laufzeitumgebung durch die Informationen zur Kompilierungszeit angemessen beschrieben wird. Aber die Zukunft vorherzusagen ist gefährlich!

Hier ist noch eine Einschränkung:

Der Compiler generiert möglicherweise Code, der davon ausgeht, dass der Laufzeittyp der statische Typ ist.

Unter der Annahme, dass die statischen Typen "korrekt" sind, ergeben sich viele Optimierungsmöglichkeiten, die jedoch einschränkend sein können. Ein gutes Beispiel sind Proxy-Objekte, zB Remoting. Angenommen, Sie möchten ein lokales Proxy-Objekt haben, das Methodenaufrufe an ein reales Objekt in einem anderen Prozess weiterleitet. Es wäre schön, wenn der Proxy generisch wäre (so dass er sich als jedes Objekt tarnen kann) und transparent (so dass der vorhandene Code nicht wissen muss, dass er mit einem Proxy kommuniziert). Dazu kann der Compiler jedoch keinen Code generieren, der davon ausgeht, dass die statischen Typen korrekt sind, z. B. durch statische Inlining-Methodenaufrufe, da dies fehlschlägt, wenn das Objekt tatsächlich ein Proxy ist.

Beispiele für ein solches Remoting in Aktion sind NSXPCConnection von ObjC oder TransparentProxy von C # (für dessen Implementierung einige Pessimierungen in der Laufzeit erforderlich waren - siehe hier für eine Diskussion).

Wenn der Codegen nicht von den statischen Typen abhängig ist und Sie über Funktionen wie die Nachrichtenweiterleitung verfügen, können Sie mit Proxy-Objekten, dem Debuggen usw. viele interessante Aufgaben ausführen.

Das ist also eine Auswahl von Dingen, die Sie tun können, wenn Sie keinen Typprüfer benötigen. Die Einschränkungen werden nicht durch statische Typen auferlegt, sondern durch erzwungene statische Typprüfung.

lächerlich_fisch
quelle
2
"Eine statische Python 2-Typprüfung würde den Python 3-Code (und umgekehrt) ablehnen, obwohl er niemals ausgeführt würde. Mein typsicheres Programm enthält einen statischen Typfehler." Klingt wie das, was Sie wirklich brauchen, es gibt eine Art "statisches Wenn", bei dem der Compiler / Interpreter den Code nicht einmal sieht, wenn die Bedingung falsch ist.
David Stone
@ Davidstone Das gibt es in c ++
Milind R
A Python 2 static type checker would reject the Python 3 code (and vice versa), even though it would never be executed. My type safe program contains a static type error. In jeder vernünftigen statischen Sprache können Sie dies mit einer IFDEFTyp-Präprozessor-Anweisung tun , wobei in beiden Fällen die Typensicherheit gewahrt bleibt.
Mason Wheeler
1
@MasonWheeler, davidstone Nein, Präprozessor-Tricks und static_if sind beide zu statisch. In meinem Beispiel habe ich Python2 und Python3 verwendet, aber es hätte genauso gut AmazingModule2.0 und AmazingModule3.0 sein können, bei denen sich einige Benutzeroberflächen zwischen den Versionen geändert haben. Sie können die Schnittstelle frühestens zum Zeitpunkt des Modulimports kennen, der notwendigerweise zur Laufzeit erfolgt (zumindest, wenn Sie dynamische Verknüpfungen unterstützen möchten).
ridiculous_fish
18

Ententypisierte Variablen sind das erste, woran jeder denkt, aber in den meisten Fällen können Sie dieselben Vorteile durch statische Typinferenz erzielen.

In dynamisch erstellten Sammlungen zu tippen, ist jedoch auf andere Weise schwierig:

>>> d = JSON.parse(foo)
>>> d['bar'][3]
12
>>> d['baz']['qux']
'quux'

Welcher Typ kehrt also JSON.parsezurück? Ein Wörterbuch mit Arrays von ganzen Zahlen oder Wörterbüchern? Nein, auch das ist nicht allgemein genug.

JSON.parsemuss eine Art "varianten Wert" zurückgeben, der rekursiv null, bool, float, string, ein Array eines dieser Typen oder ein Dictionary von string zu einem dieser Typen sein kann. Die Hauptstärken der dynamischen Typisierung sind solche Variantentypen.

Bisher ist dies ein Vorteil von dynamischen Typen , nicht von dynamisch typisierten Sprachen. Eine anständige statische Sprache kann einen solchen Typ perfekt simulieren. (Und selbst "schlechte" Sprachen können sie oft simulieren, indem sie die Sicherheit unter der Haube unterbrechen und / oder eine ungeschickte Zugriffssyntax erfordern.)

Der Vorteil von dynamisch typisierten Sprachen besteht darin, dass solche Typen nicht von statischen Typ-Inferenzsystemen abgeleitet werden können. Sie müssen den Typ explizit schreiben. Aber in vielen Fällen - einschließlich dieses einmaligen - ist der Code zur Beschreibung des Typs genauso kompliziert wie der Code zum Parsen / Konstruieren der Objekte ohne Beschreibung des Typs, so dass dies immer noch nicht unbedingt ein Vorteil ist.

abarnert
quelle
21
Ihr JSON-Parsing-Beispiel kann von einem algebraischen Datentyp problemlos statisch verarbeitet werden.
2
OK, meine Antwort war nicht klar genug; Vielen Dank. Dieser JSValue ist eine explizite Definition eines dynamischen Typs, genau das, worüber ich gesprochen habe. Es sind diese dynamischen Typen, die nützlich sind, und nicht Sprachen, die dynamisches Tippen erfordern. Es ist jedoch immer noch relevant, dass dynamische Typen nicht automatisch von einem realen Typ-Inferenz-System generiert werden können, während die meisten gängigen Beispiele für Personen trivialerweise nicht abgeleitet werden können. Ich hoffe die neue Version erklärt es besser.
3.
4
Algebraische Datentypen von @MattFenwick sind (in der Praxis) weitgehend auf funktionale Sprachen beschränkt. Was ist mit Sprachen wie Java und C #?
spirc
4
ADTs existieren in C / C ++ als getaggte Gewerkschaften. Dies gilt nicht nur für funktionale Sprachen.
Clark Gaebel
2
@spirc Sie können ADTs in einer klassischen OO-Sprache emulieren, indem Sie mehrere Klassen verwenden, die alle von einer gemeinsamen Schnittstelle, Laufzeitaufrufen von getClass () oder GetType () und Gleichheitsprüfungen abgeleitet sind. Oder Sie können Double Dispatch verwenden, aber ich denke, das zahlt sich in C ++ mehr aus. Möglicherweise verfügen Sie über eine JSObject-Schnittstelle sowie über die Klassen JSString, JSNumber, JSHash und JSArray. Sie benötigen dann Code, um diese "untypisierte" Datenstruktur in eine "anwendungsspezifische" Datenstruktur umzuwandeln. Aber wahrscheinlich möchten Sie dies auch in einer dynamisch geschriebenen Sprache tun.
Daniel Yankowsky
12

Da jedes fernpraktische statische Typsystem im Vergleich zu der Programmiersprache, um die es sich handelt, stark eingeschränkt ist, kann es nicht alle Invarianten ausdrücken, die der Code zur Laufzeit prüfen könnte. Um die Garantien, die ein Typensystem zu geben versucht, nicht zu umgehen, wählt es konservative und verbotene Anwendungsfälle, die diese Prüfungen bestehen würden, die jedoch (im Typensystem) nicht nachgewiesen werden können.

Ich werde ein Beispiel machen. Angenommen, Sie implementieren ein einfaches Datenmodell zur Beschreibung von Datenobjekten, Sammlungen von Daten usw., das statisch in dem Sinne geschrieben ist, dass, wenn das Modell xangibt, dass das Attribut des Objekttyps Foo eine Ganzzahl enthält, es immer eine Ganzzahl enthalten muss. Da dies ein Laufzeitkonstrukt ist, können Sie es nicht statisch eingeben. Angenommen, Sie speichern die in YAML-Dateien beschriebenen Daten. Sie erstellen eine Hash-Map (die später an eine YAML-Bibliothek übergeben wird), rufen das xAttribut ab, speichern es in der Map, rufen das andere Attribut ab, das zufällig eine Zeichenfolge ist, ... eine Sekunde warten? Was ist die Art von the_map[some_key]jetzt? Nun, schießen Sie, wir wissen, dass some_keyist 'x'und das Ergebnis daher eine ganze Zahl sein muss, aber das Typensystem kann noch nicht einmal anfangen, darüber nachzudenken.

Einige aktiv recherchierte Typsysteme funktionieren möglicherweise für dieses spezielle Beispiel, aber diese sind außerordentlich kompliziert (sowohl für Compiler-Autoren als auch für Programmierer), insbesondere für etwas so "Einfaches" (ich meine, ich habe es gerade in einem erklärt) Absatz).

Die heutige Lösung besteht natürlich darin, alles zu boxen und dann zu gießen (oder eine Reihe von überschriebenen Methoden zu haben, von denen die meisten "nicht implementierte" Ausnahmen auslösen). Dies ist jedoch nicht statisch typisiert, sondern ein Hack um das Typsystem , um die Typprüfungen zur Laufzeit durchzuführen.

user7043
quelle
Generische Typen haben nicht die Boxing-Anforderung.
Robert Harvey
@RobertHarvey Ja. Ich habe nicht mit dem Boxen in Java C # gesprochen, sondern darüber, "es in eine Wrapper-Klasse zu packen, deren einziger Zweck darin besteht, einen Wert von T in einem Subtyp von U darzustellen". Der parametrische Polymorphismus (was Sie als generische Typisierung bezeichnen) gilt jedoch nicht für mein Beispiel. Es ist eine Abstraktion zur Kompilierungszeit über konkrete Typen, aber wir benötigen einen Laufzeit-Typisierungsmechanismus.
Es kann sich lohnen, darauf hinzuweisen, dass das Typensystem von Scala vollständig ist. So können Typsysteme weniger trivial sein als Sie sich vorstellen.
Andrea
@Andrea Ich habe meine Beschreibung absichtlich nicht auf Vollständigkeit reduziert. Schon mal in einem turing Tarpit programmiert? Oder haben Sie versucht, diese Dinge in Typen zu kodieren? Irgendwann wird es viel zu kompliziert, um machbar zu sein.
@delnan Ich stimme zu. Ich habe nur darauf hingewiesen, dass Typsysteme ziemlich komplizierte Dinge tun können. Ich hatte den Eindruck, dass Ihre Antwort bedeutet, dass das Typensystem nur eine triviale Überprüfung durchführen kann, aber bei einem zweiten Lesevorgang haben Sie so etwas nicht geschrieben!
Andrea
7

Es gibt nichts, was Sie mit dynamischer Eingabe tun können, das Sie nicht mit statischer Eingabe tun können, da Sie dynamische Eingabe zusätzlich zu einer statisch eingegebenen Sprache implementieren können.

Ein kurzes Beispiel in Haskell:

data Data = DString String | DInt Int | DDouble Double

-- defining a '+' operator here, with explicit promotion behavior
DString a + DString b = DString (a ++ b)
DString a + DInt b = DString (a ++ show b)
DString a + DDouble b = DString (a ++ show b)
DInt a + DString b = DString (show a ++ b)
DInt a + DInt b = DInt (a + b)
DInt a + DDouble b = DDouble (fromIntegral a + b)
DDouble a + DString b = DString (show a ++ b)
DDouble a + DInt b = DDouble (a + fromIntegral b)
DDouble a + DDouble b = DDouble (a + b)

Mit genügend Fällen können Sie ein beliebiges dynamisches Typsystem implementieren.

Umgekehrt können Sie auch jedes statisch typisierte Programm in ein äquivalentes dynamisches übersetzen. Natürlich würden Sie alle Gewährleistungen für die Richtigkeit während der Kompilierung verlieren, die die statisch typisierte Sprache bietet.

Bearbeiten: Ich wollte dies einfach halten, aber hier sind weitere Details zu einem Objektmodell

Eine Funktion verwendet eine Liste von Daten als Argumente, führt in ImplMonad Berechnungen mit Nebenwirkungen durch und gibt Daten zurück.

type Function = [Data] -> ImplMonad Data

DMember ist entweder ein Mitgliedswert oder eine Funktion.

data DMember = DMemValue Data | DMemFunction Function

Erweiterung Dataum Objekte und Funktionen. Objekte sind Listen benannter Mitglieder.

data Data = .... | DObject [(String, DMember)] | DFunction Function

Diese statischen Typen reichen aus, um jedes mir vertraute dynamisch typisierte Objektsystem zu implementieren.

NovaDenizen
quelle
Dies ist überhaupt nicht dasselbe, da Sie keine neuen Typen hinzufügen können, ohne die Definition von zu überarbeiten Data.
Jed
5
Sie mischen in Ihrem Beispiel Konzepte des dynamischen Schreibens mit dem schwachen Schreiben. Bei der dynamischen Typisierung geht es darum, unbekannte Typen zu bearbeiten, keine Liste zulässiger Typen zu definieren und Operationen zwischen diesen zu überladen.
Hcalves
2
@Jed Nachdem Sie das Objektmodell, die Grundtypen und die primitiven Operationen implementiert haben, sind keine weiteren Grundlagen erforderlich. Sie können Programme in der ursprünglichen dynamischen Sprache einfach und automatisch in diesen Dialekt übersetzen.
NovaDenizen
2
@hcalves Da Sie in meinem Haskell-Code von Überladung sprechen, vermute ich, dass Sie nicht ganz die richtige Vorstellung von der Semantik haben. Dort habe ich einen neuen +Operator definiert , der zwei DataWerte zu einem anderen DataWert kombiniert . Datarepräsentiert die Standardwerte im dynamischen Typsystem.
NovaDenizen
1
@Jed: Die meisten dynamischen Sprachen haben eine kleine Menge von "primitiven" Typen und eine induktive Art, neue Werte einzuführen (Datenstrukturen wie Listen). Schema kommt zum Beispiel mit wenig mehr als Atomen, Paaren und Vektoren ziemlich weit. Sie sollten in der Lage sein, diese wie den Rest des angegebenen dynamischen Typs zu implementieren.
Tikhon Jelvis
3

Membranen :

Eine Membran ist eine Hülle um ein gesamtes Objektdiagramm, im Gegensatz zu einer Hülle für nur ein einzelnes Objekt. Normalerweise beginnt der Ersteller einer Membran damit, nur ein einzelnes Objekt in eine Membran zu hüllen. Die Schlüsselidee ist, dass jede Objektreferenz, die die Membran kreuzt, selbst transitiv in dieselbe Membran eingewickelt wird.

Bildbeschreibung hier eingeben

Jeder Typ wird von einem Typ umschlossen, der dieselbe Schnittstelle hat, jedoch Nachrichten abfängt und Werte umschließt und auspackt, wenn sie die Membran passieren. Was ist die Art der Wrap-Funktion in Ihrer bevorzugten statisch getippten Sprache? Vielleicht hat Haskell einen Typ für diese Funktionen, aber die meisten statisch getippten Sprachen tun dies nicht, oder sie verwenden am Ende Objekt → Objekt, wodurch sie ihre Verantwortung als Typprüfer praktisch aufgeben.

Mike Samuel
quelle
4
Ja, Haskell kann dazu tatsächlich existenzielle Typen verwenden. Wenn Sie eine Typklasse Foo haben, können Sie einen Wrapper um jeden Typ erstellen, der diese Schnittstelle instanziiert. class Foo a where ... data Wrapper = forall a. Foo a => Wrapper a
Jake McArthur
@JakeMcArthur, Danke für die Erklärung. Das ist ein weiterer Grund für mich, mich zu setzen und Haskell zu lernen.
Mike Samuel
2
Ihre Membran ist eine "Schnittstelle" und die Arten der Objekte sind "existentiell typisiert" - das heißt, wir wissen, dass sie unter der Schnittstelle existieren, aber das ist alles, was wir wissen. Existenzielle Typen für die Datenabstraktion sind seit den 80er Jahren bekannt. Ein guter Hinweis ist cs.cmu.edu/~rwh/plbook/book.pdf Kapitel 21.1
Don Stewart
@ DonStewart. Sind Java-Proxy-Klassen dann ein existenzieller Typmechanismus? Eine Stelle, an der Membranen schwierig werden, sind Sprachen mit nominalen Typsystemen, bei denen Namen für konkrete Typen außerhalb der Typdefinition sichtbar sind. Zum Beispiel kann man nicht umbrechen, Stringda es ein konkreter Typ in Java ist. Smalltalk hat dieses Problem nicht, da es nicht versucht zu tippen #doesNotUnderstand.
Mike Samuel
1

Wie bereits erwähnt, können Sie mit dynamischer Eingabe theoretisch nicht viel anfangen, was Sie mit statischer Eingabe nicht tun könnten, wenn Sie bestimmte Mechanismen selbst implementieren würden. Die meisten Sprachen bieten die Mechanismen zur Typentspannung, um die Flexibilität von Typen wie Leerzeiger und Stammobjekttyp oder leere Schnittstelle zu unterstützen.

Die bessere Frage ist, warum dynamisches Tippen in bestimmten Situationen und Problemen geeigneter und angemessener ist.

Lassen Sie uns zunächst definieren

Entität - Ich würde eine allgemeine Vorstellung von einer Entität im Code benötigen. Es kann alles sein, von primitiven Zahlen bis hin zu komplexen Daten.

Verhalten - Nehmen wir an, unsere Entität verfügt über einen bestimmten Status und eine Reihe von Methoden, mit denen die Außenwelt die Entität auf bestimmte Reaktionen anweisen kann. Nennen wir die State + -Schnittstelle dieser Entität ihr Verhalten. Eine Entität kann mehr als ein Verhalten aufweisen, das in einer bestimmten Weise durch die Toolsprache kombiniert wird.

Definitionen von Entitäten und deren Verhalten - Jede Sprache bietet einige Abstraktionsmöglichkeiten, mit denen Sie das Verhalten (Methodensatz + interner Zustand) bestimmter Entitäten im Programm definieren können. Sie können diesen Verhalten einen Namen zuweisen und angeben, dass alle Instanzen mit diesem Verhalten von einem bestimmten Typ sind .

Dies ist wahrscheinlich etwas, das nicht so ungewohnt ist. Und wie Sie sagten, haben Sie den Unterschied verstanden, aber immer noch. Wahrscheinlich nicht vollständig und genaueste Erklärung, aber ich hoffe, dass es Spaß macht, etwas Wert zu bringen :)

Statische Typisierung - Das Verhalten aller Entitäten in Ihrem Programm wird zur Kompilierungszeit überprüft, bevor der Code ausgeführt wird. Dies bedeutet, dass Sie, wenn Sie möchten, dass Ihre Entität vom Typ Person sich wie Magier verhält, die Entität MagicianPerson definieren und ihr das Verhalten eines Magiers wie throwMagic () geben müssen. Wenn Sie in Ihrem Code, versehentlich zu gewöhnlichen Person.throwMagic () Compiler erzählen Sie"Error >>> hell, this Person has no this behavior, dunno throwing magics, no run!".

Dynamische Eingabe - In Umgebungen mit dynamischer Eingabe werden verfügbare Verhaltensweisen von Entitäten erst überprüft, wenn Sie wirklich versuchen, etwas mit einer bestimmten Entität zu tun. Das Ausführen von Ruby-Code, der eine Person.throwMagic () auffordert, wird erst abgefangen, wenn Ihr Code wirklich da ist. Das klingt frustrierend, nicht wahr? Aber es klingt auch offenbarend. Aufgrund dieser Eigenschaft können Sie interessante Dinge tun. Nehmen wir an, Sie entwerfen ein Spiel, in dem sich alles an Magier wenden kann und Sie nicht genau wissen, wer das sein wird, bis Sie zu einem bestimmten Punkt im Code gelangen. Und dann kommt Frosch und du sagstHeyYouConcreteInstanceOfFrog.include Magicund von da an wird dieser Frosch ein besonderer Frosch, der magische Kräfte besitzt. Andere Frösche immer noch nicht. Sie sehen, in statischen Schreibsprachen müssten Sie diese Beziehung durch einen Standardmittelwert für die Kombination von Verhalten definieren (wie die Implementierung von Schnittstellen). In der dynamischen Eingabesprache können Sie dies zur Laufzeit tun, und niemand wird sich darum kümmern.

Die meisten dynamischen Eingabesprachen verfügen über Mechanismen, um ein generisches Verhalten bereitzustellen, mit dem alle Nachrichten abgefangen werden, die an ihre Benutzeroberfläche übergeben werden. Zum Beispiel Ruby method_missingund PHP, __callwenn ich mich gut erinnere. Das bedeutet, dass Sie zur Laufzeit des Programms alle Arten von interessanten Dingen ausführen und die Typentscheidung auf der Grundlage des aktuellen Programmstatus treffen können. Dies bringt Werkzeuge für die Modellierung eines Problems mit sich, die viel flexibler sind als in einer konservativen statischen Programmiersprache wie Java.

ivanjovanovic
quelle