And a mouse and a frog. Universal Compiler

In a series about reliable programming [1], [2] Swift remained undeservedly forgotten. To be honest, I just did not consider it cross-platform, but working exclusively for macOS / iOS. It happened by chance that Swift is also supported by a development environment such as RemObjects Elements .

It turned out she had a Universal Compiler. Can compile programs in C #, Go, Java, Oxygene Object Pascal, Swift for: Android, Cocoa (MacOS, iOS, tvOS), JVM, Linux (x64, armv6, aarch64), .NET / .NET Core / Mono, Native Windows (x86 / x64), WebAssembly.

And it does this in almost any combination of language → target system! For example, you can write a program in Java that will use WPF for the target .NET platform, and this is all in the examples that come with the package.

So, I present a mini-note about RemObjects Elements, and at the same time about the reliability of the two languages ​​supported in it - Swift and Oxygene.

Figure from radionetplus


And in addition to discussions about the problem of interoperability of different runtimes - native, JVM, .NET, GC, ARC, it turned out to be simply unbelievable, and in both senses - since during discussions we came to the conclusion that normal IO is impossible.

Briefly about RemObjects Elements


This is either an independent development environment with a debugger, including a remote one, and code completion for Windows or macOS, or it integrates with Visual Studio, and Xcode is required for Cocoa. For Swift, it’s free, for the rest it costs from $ 199 for the version for personal projects, up to $ 799 for a commercial license for all languages. For work with client - server DBMS it is necessary to pay extra substantially.

Water is the name of the Windows version, it’s quite ascetic, it doesn’t have a visual form editor, but sympathizes with strictness, and the installer takes only 700MB, of course, not including JRE, .NET, Android NDK, etc.

To scold her, or I will not praise much, she performs her functions, she has never fallen (but memory is flowing).

The bonus to the IDE is a lot:

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


Of course, there is a small catch for those who think that (c) you can just take and transfer any program to another platform.

The OI here is that the program can only use the capabilities of its RTL _and_ target platform for all languages ​​and their combinations. That is, for the purpose of .NET, you can use WPF, but not rt.jar, but for Native, only WinAPI or GTK, respectively. But the base library written in Oxygene is available everywhere, as is the layer of migration with Delphi - by the way they are available on the github .

A project can include subprojects in different languages ​​supported by RO. And with the import of external libraries, there are also well-developed options.

Memory management will be used from a target platform such as ARC for Cocoa. I did not go into details, for Native GC is used by default, although there is a choice - it is checked, there is in my project on github .

Add one observation. If the program was written under manual memory management (malloc / free), it will work simply under ARC. And if the program was under ARC, then it will work without changes under the GC (except for immediate destruction).

Reliability


Oxygene


This is basically an old familiar Object Pascal, but on steroids. The Oxygene for .NET version was also marketed as Delphi Prism.

It looks safer due to:

  • Gc
  • nullable control support
  • availability of contracts and invariants
  • identity register control
  • the ability to control the indentation of begin / end pairs (it is not clear how it works)
  • locked / lazy thread safe class methods
  • using using, in the sense of RAII

Minuses. GC also has a downside - temporary non-determinism, excessive memory consumption. You can also write down the disadvantages that the scale of the company and the community is small, and therefore there are probably more implementation errors than for techno monsters. The documentation is weak, and the native framework is minimal, which can pull a bunch of “bikes” into projects if your target is not .NET or JRE.

On the technical side, it added sugar relative to Delphi:

  • async / await / future, await - so far only for .NET
  • LINQ
  • generic / dynamic types (different from Delphi)
  • tuples
  • sequence / yield
  • string interpolation

Interlude. C #


It was previously eliminated due to limited cross-platform, but with the release of .NET Core 3, and the advent of the native compiler, including for Linux, it became a little better. Additionally, the language is equally supported in RemObjects with an arbitrary selection of targets.

Actually, with reliability, C # is more or less all right. GC spoils the beauty, not very comfortable working with nullable, which leads to regular NPEs, the ability to get an exception from an innocent looking function, and problems with unsuccessful use of LINQ.

Due to GC and resource consumption, it is not very suitable for Embedded. Moreover, only the interpreter without JIT .NET Micro Framework has been abandoned for embedd since 2015, although it was developed as TinyCLR

Now about Swift


By and large, a C-like machine-compiled language, one of the newest and created taking into account the history of its predecessors. Made by Apple for itself, the official compiler is still under Ubuntu, transferred to Open Source.

Opportunities at the level of reliability paid decent attention. I can complain about the possibility of changing constant classes and the possibility of confusing a reference class with a structure - a value, however here as in C #.

Memory management is in the original ARC, and RemObjects for platforms other than Apple has GC.
Heavy for Embedded, no real-time abilities.

The rest that I would like to see is almost everything. There are no contracts (there are non-standard extensions in RemObjects), but some kind of emulation is possible through Property Observers or @propertyWrapper that appeared in 5.2 ( has not yet been implemented in RemObjects )

Tuples should be noted, parameter names functions, array.insert(x, at: i)explicit unwrap for Optional, and generally thought out work with it.

An essential nuance, I would call the evolution of language. Many examples from the official tutorial (5.2) simply do not work on the same Repl.it (5.0), not to mention RemObjects, which are unclear which versions correspond exactly .

The implementation in RemObjects differs from the reference one - Array is referenced here, and String is immutable, and the interfaces also differ from those described in the tutorial, and the library interfaces not only have their own, but vary slightly between platforms. Which brings some variety to the boring coding process =) The

final summary updated reliability table

Actually, I remind you that this is only superficial according to the reference data, and all the pros who write in these languages ​​are invited to hard edits.

Compilation quality


Let's try to make a Swift program similar to the Levenshtein distance in the article 0xd34df00d and compare it with the programs from there.

Swift
/*    
	  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


The rest with minimal changes, although of course the most severe flaws, I trimmed on github . Those who are not afraid and too lazy to download the environment and compile, there are also binaries there, if you wish, you can run relatively safe JVM and NET options, or in the sandbox.

The changes relate to the edits for RO, the removal of Encoding.UTF8.GetBytes from the measurement part, and it turned out that in the Pascal version the formation of lines in the form for i := 1 to 20000 do s1 := s1 + 'a';can take up to 4 minutes (outside the measurement part).

Results table (normalized by Java as universal)
TonguePlatformCompilerTime sNormParameters
C #NET4.7NET4.7.306213,075445%-o +
C #NET Core 3.1Elements 10.011,327385%release,> dotnet cs.dll
C #Win x64Elements 10.06,312215%release
JavaJvmJDK 1.8.0.2422,941100%-g: none
$ java LevDist
JavaJvmElements 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
It would be interesting to test WASM, but I missed this point.

The measurements were carried out in a virtual machine with Win7, which slowed this task down by about three times relative to my real hardware, the average was taken from 5 measurements.

So far there is only one conclusion - life on Swift is also possible outside the apple ecosystem. But so far not fast. The hypothesis that there is a problem with Win7, because the Win10 SDK is used for native compilation, has not been confirmed - on Server 2016 1607 the times are about the same.

[1] Reliable programming in the context of languages ​​- noob review. Part 1
[2] Reliable programming in the context of languages. Part 2 - Challengers

All Articles