Gibt es statische Entensprachen?
-
08-07-2019 - |
Frage
Kann ich Schnittstellen angeben, wenn ich ein Mitglied deklariere?
Nachdem ich eine Weile über diese Frage nachgedacht hatte, kam mir der Gedanke, dass eine Sprache vom Typ „Static Duck“ tatsächlich funktionieren könnte.Warum können vordefinierte Klassen zur Kompilierungszeit nicht an eine Schnittstelle gebunden werden?Beispiel:
public interface IMyInterface
{
public void MyMethod();
}
public class MyClass //Does not explicitly implement IMyInterface
{
public void MyMethod() //But contains a compatible method definition
{
Console.WriteLine("Hello, world!");
}
}
...
public void CallMyMethod(IMyInterface m)
{
m.MyMethod();
}
...
MyClass obj = new MyClass();
CallMyMethod(obj); // Automatically recognize that MyClass "fits"
// MyInterface, and force a type-cast.
Kennen Sie Sprachen, die eine solche Funktion unterstützen?Wäre es in Java oder C# hilfreich?Ist es in irgendeiner Weise grundlegend fehlerhaft?Ich verstehe, dass Sie eine Unterklasse von MyClass erstellen und die Schnittstelle implementieren oder das Adapter-Entwurfsmuster verwenden könnten, um dasselbe zu erreichen, aber diese Ansätze scheinen einfach unnötiger Standardcode zu sein.
Lösung
Statisch typisierte Sprachen per Definition überprüfen Arten bei kompilieren Zeit , nicht Laufzeit . Einer der offensichtlichen Probleme mit dem oben beschriebenen System ist, dass die Compiler-Typen wird überprüfen, wenn das Programm kompiliert wird, nicht zur Laufzeit.
Nun könnte man mehr Intelligenz in die Compiler bauen, so könnte es derive Typen, anstatt die Programmierer explizit mit deklarieren Typen; der Compiler in der Lage könnte, dass MyClass
implementiert eine MyMethod()
Methode und behandelt diesen Fall entsprechend, ohne die Notwendigkeit zu erklären ausdrücklich, Schnittstellen zu sehen (wie Sie vorschlagen). Ein solcher Compiler könnte Typinferenz, wie Hindley-Milner nutzen.
Natürlich einige statisch typisierten Sprachen wie Haskell schon etwas tun ähnlich zu dem, was Sie vorschlagen; der Haskell Compiler in der Lage, Typen zu schließen (die meiste Zeit) ohne die Notwendigkeit, sie ausdrücklich zu erklären. Aber natürlich, Java / C # nicht über diese Fähigkeit hat.
Andere Tipps
Eine ganz neue Antwort auf diese Frage, Go hat genau diese Funktion . Ich denke, es ist wirklich cool und klug (obwohl ich daran interessiert sein zu sehen, wie es spielt im wirklichen Leben aus) und ein dickes Lob an dem Denken davon.
Wie dokumentiert in der offiziellen Dokumentation (als Teil der Tour von Go, mit Beispiel-Code) :
Schnittstellen implementiert implizit
Eine Art implementiert eine Schnittstelle, die durch ihre Methoden zu implementieren. Es gibt keine explizite Absichtserklärung, keine „Geräte“ Schlüsselwort.
Implizite Schnittstellen entkoppeln die Definition einer Schnittstelle von seinem Implementierung, die dann in jedem Paket erscheinen könnte, ohne prearrangement.
Wie wäre es Vorlagen in C ++ verwenden?
class IMyInterface // Inheritance from this is optional
{
public:
virtual void MyMethod() = 0;
}
class MyClass // Does not explicitly implement IMyInterface
{
public:
void MyMethod() // But contains a compatible method definition
{
std::cout << "Hello, world!" "\n";
}
}
template<typename MyInterface>
void CallMyMethod(MyInterface& m)
{
m.MyMethod(); // instantiation succeeds iff MyInterface has MyMethod
}
MyClass obj;
CallMyMethod(obj); // Automatically generate code with MyClass as
// MyInterface
habe ich nicht wirklich diesen Code kompiliert, aber ich glaube, es ist praktikabel und ein ziemlich trivial C ++ -. Isierung des ursprünglichen vorgeschlagen (aber nicht funktionierenden) Code
Ich sehe den Punkt nicht. Warum nicht explizit sein, dass die Klasse die Schnittstelle implementiert und hat mit ihm getan? die Schnittstelle implementiert ist, was andere Programmierer sagt, dass diese Klasse in der Art und Weise soll die Schnittstelle definiert verhalten. mit einfach den gleichen Namen und Unterschrift auf einem Verfahren keine Garantien vermitteln, dass die Absicht des Designers ähnliche Aktionen mit dem Verfahren durchzuführen war. Das mag sein, aber warum es für Interpretation lassen (und Missbrauch)?
Der Grund, warum Sie mit diesem Erfolg in dynamischen Sprachen „weg“ können, hat mehr mit TDD zu tun, als mit der Sprache selbst. Meiner Meinung nach, wenn die Sprache bietet die Möglichkeit, diese Art von Führung, anderen zu geben, die / verwenden Sie den Code zu sehen, sollten Sie es verwenden. Es verbessert die eigentlich Klarheit und die wenigen zusätzlichen Zeichen wert. In dem Fall, dass Sie den Zugriff nicht, dies zu tun haben, dann dient einem Adapter den gleichen Zweck ausdrücklich erklärt, wie die Schnittstelle zu der anderen Klasse bezieht.
F # unterstützt statische Ente eingeben, wenn auch mit einem Haken: Sie Mitglied Einschränkungen verwenden. Details sind in diesem Blog-Eintrag .
Beispiel aus dem zitierten Blog:
let inline speak (a: ^a) =
let x = (^a : (member speak: unit -> string) (a))
printfn "It said: %s" x
let y = (^a : (member talk: unit -> string) (a))
printfn "Then it said %s" y
type duck() =
member x.speak() = "quack"
member x.talk() = "quackity quack"
type dog() =
member x.speak() = "woof"
member x.talk() = "arrrr"
let x = new duck()
let y = new dog()
speak x
speak y
Die meisten Sprachen der ML-Familie werden unterstützt Strukturtypen mit Inferenz und eingeschränkten Typschemata, das ist die geekige Sprachdesigner-Terminologie, die höchstwahrscheinlich das ist, was Sie in der ursprünglichen Frage mit dem Ausdruck „statisches Duck-Typing“ meinen.
Zu den beliebtesten Sprachen dieser Familie, die mir in den Sinn kommen, gehören:Haskell, Objective Caml, F# und Scala.Dasjenige, das Ihrem Beispiel am ehesten entspricht, wäre natürlich Objective Caml.Hier ist eine Übersetzung Ihres Beispiels:
open Printf
class type iMyInterface = object
method myMethod: unit
end
class myClass = object
method myMethod = printf "Hello, world!"
end
let callMyMethod: #iMyInterface -> unit = fun m -> m#myMethod
let myClass = new myClass
callMyMethod myClass
Notiz:Einige der von Ihnen verwendeten Namen müssen geändert werden, um der OCaml-Vorstellung der Groß- und Kleinschreibung von Bezeichnern zu entsprechen, aber ansonsten ist dies eine ziemlich einfache Übersetzung.
Erwähnenswert ist auch, dass weder die Typanmerkung in der callMyMethod
Funktion noch die Definition der iMyInterface
Der Klassentyp ist unbedingt erforderlich.Objective Caml kann alles in Ihrem Beispiel ohne jegliche Typdeklarationen ableiten.
Nun, ok ... So ist es ein Javascript Obermenge und vielleicht auch nicht eine „Sprache“ darstellt, aber diese Art von statischer Ente-Typisierung ist von entscheidenden Bedeutung in Typoskript.
Boo definitiv ist eine statische Ente typisierte Sprache: http://boo.codehaus.org/Duck + Typing
Ein Auszug:
Boo ist eine statisch typisierte Sprache, wie Java oder C #. Dies bedeutet, dass Ihr boo Anwendungen werden so schnell laufen etwa als diejenigen, die in anderen codierten statisch typisierte Sprachen für .NET oder Mono. aber der Einsatz von eine statisch typisierte Sprache manchmal zwingt Sie zu einem unflexiblen und ausführlicher Stil Codierung, mit der manchmal notwendig Typdeklarationen (Wie „x als int“, aber das ist nicht notwendig oft aufgrund boo des Typs Inference) und manchmal notwendig Typ Abgüsse (siehe Casting-Typen). Boo Unterstützung für Typ Inference und schließlich Generika helfen hier, aber ...
Manchmal ist es angebracht, aufgeben das Sicherheitsnetz durch statische vorgesehen Tippen. Vielleicht möchten Sie einfach erkunden eine API, ohne zu viel Sorgen über Methodensignaturen oder vielleicht sind Sie Erstellen von Code, der auf externe Gespräche Komponenten wie COM-Objekte. Entweder Art und Weise sollte die Wahl nicht sein Mine.
Neben den normalen Typen wie Objekt, int, string ... boo hat ein spezielle Art „Ente“ genannt. Der Begriff wird durch die rubin Programmierung inspiriert Duck Typing Funktion der Sprache ( "Wenn es läuft wie eine Ente und quakt wie eine Ente, muss es eine Ente "sein).
Neue Versionen von C ++ in Richtung der statischen Ente eingeben bewegen. Sie können eines Tages (? Heute) schreiben Sie etwas wie folgt aus:
auto plus(auto x, auto y){
return x+y;
}
, und es würde nicht kompilieren, wenn kein passender Funktionsaufruf für x+y
gibt.
Wie für Ihre Kritik:
Eine neue „CallMyMethod“ wird für jede unterschiedliche Art erstellt Sie es passieren, so ist es nicht wirklich Inferenz geben.
Aber es ist Typinferenz (man kann sagen, wo foo(bar)
foo
eine Templat-Funktion ist), und hat die gleiche Wirkung, es sei denn, es ist zeiteffizienter und mehr Platz im kompilierten Code nimmt.
Ansonsten würden Sie die Methode während der Laufzeit nachschlagen müssen. Sie müßten einen Namen finden, dann überprüfen Sie, dass der Name ein Verfahren mit den richtigen Parametern hat.
Oder Sie würden alle diese Informationen über passende Schnittstellen speichern haben, und schauen Sie in jeder Klasse, die eine Schnittstelle übereinstimmt, dann fügen Sie automatisch diese Schnittstelle.
In jedem Fall, die Sie implizit zu können und aus Versehen die Klassenhierarchie brechen, die für ein neues Feature schlecht ist, weil es gegen die Gewohnheiten geht, was Programmierer von C # / Java verwendet werden. Mit C ++ Vorlagen, wissen Sie bereits, Sie in einem Minenfeld sind (und sie sind auch das Hinzufügen von Funktionen ( „Konzepte“), damit Einschränkungen für Template-Parameter).
Strukturtypen in Scala tun so etwas.
Ein Pre-Release-Design für Visual Basic hatte 9-Unterstützung für statische Typisierung Ente mit dynamischen Schnittstellen, aber sie schneiden Sie die Funktion * , um pünktlich zu versenden.
D ( http://dlang.org ) ist eine statisch kompilierte Sprache und bietet Ente-Eingabe über Wrap () und unwrap () ( http://dlang.org/phobos-prerelease/std_typecons.html # .unwrap ).
Klingt wie Mixins oder Traits:
http://en.wikipedia.org/wiki/Mixin
http://www.iam.unibe.ch/~scg/ Archiv / Paper / Scha03aTraits.pdf
In der neuesten Version meiner Programmiersprache Heron es etwas ähnliches durch einen strukturell-Subtypisierung Zwang unterstützt Betreiber genannt as
. Anstatt also:
MyClass obj = new MyClass();
CallMyMethod(obj);
Sie würde schreiben:
MyClass obj = new MyClass();
CallMyMethod(obj as IMyInterface);
Genau wie in Ihrem Beispiel, in diesem Fall MyClass
muß nicht explizit IMyInterface
implementieren, aber wenn es die Besetzung implizit geschehen tut konnte und der as
Betreiber verzichtet werden kann.
Ich schrieb ein bisschen mehr über die Technik, die ich in die explizite Strukturunter Typisierung nennen dieser Artikel .