Und eine Maus und ein Frosch. Universal Compiler

In einer Reihe über zuverlässige Programmierung [1] [2] blieb Swift unverdient vergessen. Um ehrlich zu sein, habe ich es einfach nicht als plattformübergreifend angesehen, sondern ausschließlich für macOS / iOS. Es ist Zufall, dass Swift auch von einer Entwicklungsumgebung wie RemObjects Elements unterstützt wird .

Es stellte sich heraus, dass sie einen Universal Compiler hatte. Kann Programme in C #, Go, Java, Oxygene Object Pascal, Swift für: Android, Cocoa (MacOS, iOS, tvOS), JVM, Linux (x64, armv6, aarch64), .NET / .NET Core / Mono, Native Windows kompilieren (x86 / x64), WebAssembly.

Und das in fast jeder Kombination von Sprache → Zielsystem! Sie können beispielsweise ein Programm in Java schreiben, das WPF für die Ziel-.NET-Plattform verwendet. Dies finden Sie in den Beispielen, die mit dem Paket geliefert werden.

Daher präsentiere ich einen kurzen Hinweis zu RemObjects Elements und gleichzeitig zur Zuverlässigkeit der beiden darin unterstützten Sprachen - Swift und Oxygene.

Figur aus Radionetplus


Zusätzlich zu den Diskussionen über das Problem der Interoperabilität verschiedener Laufzeitumgebungen - native, JVM, .NET, GC, ARC - stellte sich heraus, dass dies einfach unglaublich und in beiden Sinnen war, da sie während der Diskussionen zu dem Schluss kamen, dass normale E / A unmöglich sind.

Kurz zu RemObjects Elements


Dies ist entweder eine unabhängige Entwicklungsumgebung mit einem Debugger, einschließlich eines Remote-Debuggers, und Code-Vervollständigung für Windows oder MacOS, oder sie lässt sich in Visual Studio integrieren, und Cocoa ist für Xcode erforderlich. Für Swift ist es kostenlos, für den Rest kostet es von 199 USD für die Version für persönliche Projekte bis zu 799 USD für eine kommerzielle Lizenz für alle Sprachen. Für die Arbeit mit Client-Server-DBMS müssen zusätzliche Gebühren erhoben werden.

Wasser ist der Name der Windows-Version, es ist ziemlich asketisch, es hat keinen visuellen Formulareditor, aber es sympathisiert mit Strenge, und das Installationsprogramm benötigt natürlich nur 700 MB, ohne JRE, .NET, Android NDK usw.

Um sie zu schelten, oder ich werde nicht viel loben, sie erfüllt ihre Funktionen, sie ist nie gefallen (aber die Erinnerung fließt).

Der Bonus für die IDE ist viel:

  • ( Water , -)
  • -
  • C#, Swift, Java, Delphi, C, Obj-C Oxygene, C#, Swift, Java, Go
  • Go- . Go
  • -
  • Delphi/VCL


Natürlich gibt es einen kleinen Haken für diejenigen, die denken, dass (c) Sie einfach jedes Programm nehmen und auf eine andere Plattform übertragen können.

Die OI hier ist, dass das Programm nur die Funktionen seiner RTL-Zielplattform für alle Sprachen und deren Kombinationen verwenden kann. Das heißt, für .NET können Sie WPF verwenden, jedoch nicht rt.jar, sondern für Native nur WinAPI bzw. GTK. Die in Oxygene geschriebene Basisbibliothek ist jedoch überall verfügbar, ebenso wie die Migrationsschicht mit Delphi - übrigens, wie sie auf dem Github verfügbar sind .

Ein Projekt kann Teilprojekte in verschiedenen Sprachen enthalten, die von RO unterstützt werden. Und mit dem Import externer Bibliotheken gibt es auch gut entwickelte Optionen.

Die Speicherverwaltung wird von einer Zielplattform wie ARC for Cocoa aus verwendet. Ich habe nicht auf Details eingegangen , da Native GC standardmäßig verwendet wird, obwohl es eine Auswahl gibt - es ist aktiviert , es gibt in meinem Projekt auf Github .

Fügen Sie eine Beobachtung hinzu. Wenn das Programm unter manueller Speicherverwaltung (malloc / free) geschrieben wurde, funktioniert es einfach unter ARC. Und wenn das Programm unter ARC war, funktioniert es ohne Änderungen unter dem GC (mit Ausnahme der sofortigen Zerstörung).

Verlässlichkeit


Sauerstoff


Dies ist im Grunde ein altes bekanntes Objekt Pascal, aber auf Steroiden. Die Oxygene for .NET-Version wurde auch als Delphi Prism vermarktet.

Es sieht sicherer aus aufgrund von:

  • Gc
  • Unterstützung für nullfähige Steuerelemente
  • Verfügbarkeit von Verträgen und Invarianten
  • Identitätsregistersteuerung
  • die Fähigkeit, die Einrückung von Anfang / Ende-Paaren zu steuern (es ist nicht klar, wie es funktioniert)
  • gesicherte / faule Thread-sichere Klassenmethoden
  • using using im Sinne von RAII

Minuspunkte. GC hat auch einen Nachteil - vorübergehender Nichtdeterminismus, übermäßiger Speicherverbrauch. Sie können auch die Nachteile aufschreiben, dass der Umfang des Unternehmens und der Community gering ist und daher wahrscheinlich mehr Implementierungsfehler auftreten als bei Techno-Monstern. Die Dokumentation ist schwach und das native Framework ist minimal, was eine Reihe von „Fahrrädern“ in Projekte ziehen kann, wenn Ihr Ziel nicht .NET oder JRE ist.

Auf der technischen Seite wurde Zucker im Vergleich zu Delphi hinzugefügt:

  • async / await / future, warte - bisher nur für .NET
  • LINQ
  • generische / dynamische Typen (anders als Delphi)
  • Tupel
  • Sequenz / Ausbeute
  • String-Interpolation

Zwischenspiel. C #


Es wurde zuvor aufgrund begrenzter plattformübergreifender Funktionen entfernt, aber mit der Veröffentlichung von .NET Core 3 und dem Aufkommen des nativen Compilers, auch für Linux, wurde es etwas besser. Darüber hinaus wird die Sprache in RemObjects mit einer beliebigen Auswahl von Zielen gleichermaßen unterstützt.

Tatsächlich ist C # mit Zuverlässigkeit mehr oder weniger in Ordnung. GC verdirbt die Schönheit, die nicht sehr angenehm mit nullable zu arbeiten ist, was zu regulären NPEs, der Fähigkeit, eine Ausnahme von einer unschuldig aussehenden Funktion zu erhalten, und Problemen mit der erfolglosen Verwendung von LINQ führt.

Aufgrund des GC- und Ressourcenverbrauchs ist es nicht sehr gut für Embedded geeignet. Darüber hinaus wurde seit 2015 nur der Interpreter ohne JIT .NET Micro Framework für die Einbettung aufgegeben, obwohl er als TinyCLR entwickelt wurde

Nun zu Swift


Im Großen und Ganzen eine C-ähnliche maschinenkompilierte Sprache, eine der neuesten und unter Berücksichtigung der Geschichte ihrer Vorgänger erstellten. Der offizielle Compiler wurde von Apple für sich selbst erstellt und befindet sich immer noch unter Ubuntu, das auf Open Source übertragen wurde.

Chancen auf der Ebene der Zuverlässigkeit wurden angemessen berücksichtigt. Ich kann mich über die Möglichkeit beschweren, konstante Klassen zu ändern und eine Referenzklasse mit einer Struktur zu verwechseln - ein Wert, jedoch hier wie in C #.

Die Speicherverwaltung erfolgt im ursprünglichen ARC, und RemObjects für andere Plattformen als Apple verfügt über GC.
Schwer für Embedded, keine Echtzeitfähigkeiten.

Der Rest, den ich gerne sehen würde, ist fast alles. Es gibt keine Verträge (es gibt nicht standardmäßige Erweiterungen in RemObjects), aber eine Art Emulation ist über Property Observers oder @propertyWrapper möglich, die in 5.2 erschienen sind ( noch nicht in RemObjects implementiert ).

Tupel sollten notiert werden, Parameternamen Funktionen, array.insert(x, at: i)explizites Auspacken für Optional und allgemein durchdachte Arbeit damit.

Eine wesentliche Nuance würde ich die Evolution der Sprache nennen. Viele Beispiele aus dem offiziellen Tutorial (5.2) funktionieren einfach nicht mit demselben Repl.it (5.0), ganz zu schweigen von RemObjects, bei denen unklar ist, welche Versionen genau übereinstimmen .

Die Implementierung in RemObjects unterscheidet sich von der Referenz - Array wird hier referenziert, und String ist unveränderlich, die Schnittstellen unterscheiden sich auch von den im Lernprogramm beschriebenen, und die Bibliotheksschnittstellen haben nicht nur ihre eigenen, sondern variieren geringfügig zwischen den Plattformen. Das bringt etwas Abwechslung in den langweiligen Codierungsprozess =) Die

endgültige Zusammenfassung aktualisiert Zuverlässigkeitstabelle

Eigentlich erinnere ich Sie daran, dass dies gemäß den Referenzdaten nur oberflächlich ist und alle Profis, die in diesen Sprachen schreiben, zu harten Änderungen eingeladen sind.

Kompilierungsqualität


Versuchen wir, ein Swift-Programm zu erstellen , das der Levenshtein-Distanz im Artikel ähnelt 0xd34df00d und vergleichen Sie es mit den Programmen von dort.

Schnell
/*    
	  Windows.Native, Elements.Island
*/

class Program {
	 public static func LevDist(_ s1: [UInt8], _ s2: [UInt8]) -> Int32! {
		 let m = s1.count
		 let n = s2.count
		 //  Edge cases.
		 if m == 0 {
			 return n
		 } else {
			 if n == 0 {
				 return m
			 }
		 }

		 var range = Range(0 ... (n + 1))
		 var v0: Swift.Array<Int64> = range.GetSequence()  //17ms

//         var v1 = v0;    // in Swift must copy, but RO make a ref
		 var v1 = Swift.Array<Int64>(v0);

		 var i = 0
		 while i < m {
			 v1[0] = i + 1
			 var j = 0
			 while j < n {
				 let substCost = (s1[i] == s2[j] ? v0[j] : v0[j] + 1)
				 let delCost = v0[j + 1] + 1
				 let insCost = v1[j] + 1
				 v1[j + 1] = substCost < delCost ? substCost : delCost
				 if insCost < v1[j + 1] {
					 v1[j + 1] = insCost
				 }
				 j += 1
			 }

			 let temp = v0; v0 = v1; v1 = temp // copy refs
			 i += 1
		 }
		 return v0[n]
	 }
 }


var b1 = [UInt8](repeating: 61 as! UInt8, count: 20000)
var b2: [UInt8] = b1
var b3 = Swift.Array<UInt8>(repeating: UInt8(63), count: 20000)

print("Start Distance");

var execTimer: StopWatch! = StopWatch()
execTimer.Start()

print(Program.LevDist(b1, b2))
print(Program.LevDist(b1, b3))

execTimer.Stop()

var execTime: Float64 = execTimer.ElapsedMilliseconds / 1000.0 / 10

print("\(execTime) s")
return 0


Den Rest mit minimalen Änderungen, obwohl natürlich die schwerwiegendsten Mängel, habe ich auf Github getrimmt . Für diejenigen, die keine Angst haben und zu faul sind, um die Umgebung herunterzuladen und zu kompilieren, gibt es dort auch Binärdateien. Wenn Sie möchten, können Sie relativ sichere JVM- und NET-Optionen oder in der Sandbox ausführen.

Die Änderungen beziehen sich auf die Änderungen für RO, das Entfernen von Encoding.UTF8.GetBytes aus dem Messteil, und es stellte sich heraus, dass in der Pascal-Version die Bildung von Linien in der Form for i := 1 to 20000 do s1 := s1 + 'a';bis zu 4 Minuten dauern kann (außerhalb des Messteils).

Ergebnistabelle (von Java als universell normalisiert)
ZungePlattformCompilerZeit sNormParameter
C #NET4.7NET4.7.306213.075445%-o +
C #NET Core 3.1Elemente 10.011.327385%release,> dotnet cs.dll
C #Win x64Elemente 10.06,312215%Veröffentlichung
JavaJvmJDK 1.8.0.2422,941100%-g: keine
$ java LevDist
JavaJvmElemente 10.02,85197%release
$java -jar java8.jar
OxygeneNET4.7Elements 10.022,079751%release, range checking off
OxygeneWin x64Elements 10.09,421320%release, range checking off
SwiftJVMElements 10.023,733807%release
$java -jar swiftjvm.jar
SwiftWin x64Elements 10.0131,4004468%release
Go (Beta)Win x64Elements 10.089,2433034%release
Es wäre interessant, WASM zu testen, aber ich habe diesen Punkt verpasst.

Die Messungen wurden in einer virtuellen Maschine mit Win7 durchgeführt, was diese Aufgabe im Vergleich zu meiner realen Hardware um etwa das Dreifache verlangsamte. Der Durchschnitt wurde aus 5 Messungen ermittelt.

Bisher gibt es nur eine Schlussfolgerung: Das Leben auf Swift ist auch außerhalb des Apfel-Ökosystems möglich. Aber bisher nicht schnell. Die Hypothese, dass es ein Problem mit Win7 gibt, weil das Win10 SDK für die native Kompilierung verwendet wird, wurde nicht bestätigt - auf Server 2016 1607 sind die Zeiten ungefähr gleich.

[1] Zuverlässige Programmierung im Kontext von Sprachen - Noob Review. Teil 1
[2] Zuverlässige Programmierung im Kontext von Sprachen. Teil 2 - Herausforderer

All Articles