I love Swift and work on the web, so I would love to see a serious Swift to JS compiler. Unfortunately, this isn't it.
One major part of Swift is its value types; many core types, including arrays, are passed by value, like a C struct. So
var a = [1, 2]
var b = a
b.append(3)
should make `a` equal to [1, 2] and `b` equal to [1, 2, 3]. ShiftJS eschews these semantics and instead passes arrays (and other value types) by reference, which makes it basically impossible to share code between real Swift and ShiftJS.
I have successfully used Haxe in isolated cases to build games for WebGL and Canvas. I think transpilers are most useful when you rarely want to reach outside of your own code, or you only need to rely on a few key libraries that are known to be well supported within the ecosystem of your transpiler language. I also struggle without a static compiler, so for my case it makes sense to use a transpiler, even with all of its pitfalls.
If there is one thing however that I have learned from using transpilers it is that externs as a whole are a huge waste of time, as it can be difficult to determine their quality upfront. If you are building a Node backend or SPA, you are going to need to rely heavily on existing libraries and will probably find yourself constantly hitting walls while you try to swim against the current of the ecosystem if you try to use a transpiler.
I disagree about externs being a waste of time for Haxe. I've written several, and they've been instrumental in helping me understand a codebase, and manage an upgrade process.
Haxe has a lot of metadata mechanisms that enable mapping language-specific semantics to Haxe standards (@:selfCall, and @:native for example). Haxe also has abstract types, which enable automatic inline runtime conversions (e.g., converting a native type to a standard Haxe type automatically depending on usage in haxe methods).
It's true you can't expect to find a high quality extern for a given version of any given library. However, it's pretty easy to write a small one for the subset of functionality that you require. It always ends up paying off for me.
I should have elaborated a bit more on this point, and I mistakenly used the wrong terminology. I was thinking specifically of Typescript type definitions when I wrote about externs, and was meaning to refer more so to the concept in a general sense (across all transpilers). That said I do use a number of high quality externs with Haxe (the Pixi.JS one is excellent).
I agree with what you say about writing your own externs with a subset of the functionality, and I think it is often a better approach than relying on potentially flakey externs. Still I think this sort of workflow is only suited to particular types of projects and especially in a team environment it makes more sense to avoid niche ecosystems.
I don't have an opinion on this particular compiler, or really any other similar one, but: Can't one make the same argument about a compiler from C++ to x86 machine code? C++ and x86 certainly don't have anything like a 1:1 mapping, but people use those all the time.
I think an important question is whether the compiler output will need to be touched by humans. If it's just going to be run or compiled more or whatever then it doesn't really matter how the various features map.
As someone working on a compiler that turns a C# library into Java and JavaScript ... for us a significant benefit is that we can share code between our different products. We write the code in C# and about 95 % of the library code is the same for every platform, so implementing a feature for one platform automatically benefits all of them (which have been at one point: Java Swing, JavaFX, HTML5/JavaScript, .NET/WPF, .NET/WinForms, .NET/Silverlight, and Flash). In very many cases all that's needed is a simple merge and converting the code again.
Yes, languages differ and language features rarely map 1:1 onto each other; that's about the other 5 % (as well as platform differences). But a lot of things can be done automatically if you control the conversion process, e.g. converting C#'s extension methods into default methods in Java. Of course, if you don't require the resulting converted code to be worked with, and just need it to run, then you can do a lot of shortcuts. We translate at the source code level because at least the Java code is still maintained and extended on the Java side. Doing it at the IL level would be simpler in some cases, but is impractical for a lot of others.
> If behavior of compiled program matches the original one, that is.
Unfortunately, that does make it difficult when your target is JavaScript. Every mature transpiler I've used has had some semantic gotchas that aren't present in the "proper" compiler.
Routines can be created to mimic desired behavior. Sprinkle in some conventions and limitations, and you have a useful tool for a large set of use-cases.
In the X->JS transpiler space it's really only Emscripten that can take a library not written with JS in mind, and get it working. Scala.js is close, but in my experience the transpilers are really only suited for projects written from scratch.
Not to say there's not value in building JS output with semantics very close to to Swift.
GWT's JAva->JS transpiler does a pretty good job too. As long as a library isn't too reflection-heavy it can compile without modification. And if a library is reflection-heavy odds are you're not going to have a use for it anyway.
JSIL can run pretty much all modern .NET code with correct semantics in JS (i.e. the standard library + real enterprise apps/games), but the performance cost to match semantics is pretty significant. And then there's stuff you just can't do, like weak references :(
WeakMap isn't weak references. Easy mistake to make (most TC39 members don't understand the difference). WeakMap is a weird approximation of weak properties, where the weak relationship is in the opposite direction.
It was not the only one, it crops back up periodically and the purpose/nuances of weakrefs have to be re-explained each time. To be fair, there are a lot of subtleties involved...
Experience with the Scala.js ecosystem suggests that it is very well suited to cross-compiling existing code after the fact. All the major libraries of the Scala/JVM ecosystem have been ported to Scala.js by now, several of them only by changing the build script! And I'm talking about serious, language feature-intensive libraries like Scalaz and Shapeless.
BuckleScript looks very interesting. Semantic mapping and very small potential output are rare. GPLv2 would present some challenges using it commercially though.
Many compilers are released under the GPL license. Generally speaking[1], the output of the compiler is not subject to the license terms of the GPL, just as a document created by a GPL'ed word processor need not be licensed under the GPL.
[1]: There could conceivably be GPL code linked with your code in the final output, but I don't think people would release a compiler like that without big notices unless they're specifically trying to trick people into having to buy commercial licenses or something like that.
Passing on type inference can be solved by using Flow as an intermediate before transpiling down to Javascript. From my experience, modules and named arguments are the most important but hardest constructs to take care of when converting to js.
I'm currently working on something like this for the Crystal language.
The true "Swift in web" will happen when WebAssembly is ready and LLVM has a WASM backend. Then I can just compile my Swift to WASM and bypass JavaScript altogether because Swift is already a LLVM front-end.
8-9 months without any update to codebase is not a good sign for most open-source projects, especially when both JS and Swift are marching quickly. This alone can turn me off on this.
If the projects GitHub page is anything to go by, I doubt that. The only thing that's been changed in 9 months is the readme, and the change was to remove the note about the project being in "active early development".
Why didn't they fork the actual swift compiler that does all the lexing, parsing, syntax checking, type checking, SIL generation, LLVMIR generation, and add a final phase that compiles to JS?
This whole project seem like a lot of duplicated, wasted (and error prone) work
Wondered the same thing. I thought maybe a sil to js project could be a good target, keeping enough semantics to be able to be smart in the generated js code, rather thab llvmir.
I looked for such a project a few days ago but didn't find anything.
I do wish people would stop using the term transpiler as if there is something novel or different about source-to-source translation.
If you're taking a higher level language and compiling it to assembly, it's a compiler.
If you're taking a higher level language and compiling it to LLVM IR, it's a compiler.
If you're taking a higher level language, and compiling it to another higher level language...it's a compiler.
I am not sure where this term was first coined, but we've been doing source-to-source compilation for a very long term and it's just within the last 7-10 years I start hearing people use this term, which IMO adds no actual value.
It's perfectly fine to say "Swift to JS compiler".
> it's just within the last 7-10 years I start hearing people use this term
If I recall, the term arose out of the JS community around the time CoffeeScript, Traceur and other languages that compile to JS got popular. Brendan Eich did a lot of popularization of the term.
> IMO adds no actual value.
I think it's a really useful term. Compilers and transpilers are structurally different and have different high level aims.
If you tell me you are writing a language that compiles to JS, then my assumption is that you treat JS as effectively an "assembly language for the web". You will output whatever bizarre JS code that happens to correctly implement your language's semantics efficiently and compactly. It might make my eyes bleed, but that's OK. I'm not supposed to read it, maintain it, or debug it. Think Emscripten or dart2js.
If you tell me you are writing a language that transpiles to JS, then my assumption is that you treat the output JS as a first-class developer artifact. The generated output should, as much as possible, match the structure, formatting, and naming of the original code. I should be able to read it and step through it in my debugger. If the source language's maintainers disappear, I can check in the compiled output and move on with my life using it as vanilla JS. Think CoffeeScript or TypeScript.
Those are very very different kinds of tools, and it's handy to have words to distinguish them.
I agree that in "Swift to JavaScript transpiler" the word would be replaced with compiler and nothing would be lost; however, by your own definitions you can see that 'transpiler' is more specific than 'compiler.' Someone out there saw a need for the word, just like someone saw a need for coupe and sedan.
I don't think it's a meaningful term because there is no reasonable definition, specifically of where you draw the line.
cfront compiled C++ to C, so it's a transpiler, right? Or is that just an unimportant implementation detail?
Everything targeting LLVM IR is a transpiler, because it's just source-to-source, with the second source being LLVM IR.
Similarly things targeting the JVM or CLR - all transpilers because both of these are relatively high level targets. They might not be something you would consider a "source language", but for many people assembly language is a perfectly reasonable source language, and thus...
Everything compiling to assembly language is a transpiler. The target language is assembly.
Having worked on compilers for over two decades it just comes across as a made-up term by people who don't understand that what a compiler does is translate from one language to another (be it another "source language", ASTs, linear IRs, VM bytecodes, assembly languages, or executable code).
I don't think I'm being particularly pedantic here. Compilers simply generate another form that one person or another might consider a source language (in fact people working on JITs most definitely think of their input as the source language).
Yes, cfront could be considered a transpiler, but the term wasn't coined back then.
IRs, ASTs and bytecodes are not programming languages. If you insist otherwise because "some might consider it that way", you have no business criticizing the use of words, to be honest.
There are textual representations for these things, but even then people rarely program in those (LISP being an exception). Assembly language is a textual representation for humans to read and sometimes program in. If you have some program that actually translates to assembly language as a target, by all means, call it a transpiler if you want.
> IRs, ASTs and bytecodes are not programming languages. If you insist otherwise because "some might consider it that way", you have no business criticizing the use of words, to be honest.
That's not what I am saying. The point is that compilers make many transformations from "language" to "language", it just happens that other "source languages" are sometimes the target.
> If you have some program that actually translates to assembly language as a target, by all means, call it a transpiler if you want.
For a very long time that was the model most compilers used, and in fact people (mostly those espousing to be advocates of the UNIX philosophy of building small tools that could be chained together) would rail against compilers that directly generated machine code rather than generating assembly that was run through a separate assembler.
So if a compiler that generates assembly source is a transpiler, then the by definition most compilers are transpilers (and all transpilers are compilers which people seem to agree with).
I agree with munificent. ‘Transpiler’ implies very similar semantics between the source and target language; ‘compiler’ implies fairly different semantics (where a translation between the source and target is not obvious).
GCC is an optimizing compiler, at which point all bets are off for obvious/trivial translation between C and asm. You could, hypothetically, write a optimizing TypeScript compiler that emits asm.js.
However, while you can reasonably argue C started as a portable assembly, significant differences like a type system, pointer arithmetic, a calling convention, etc, exist now. I don't think it's unreasonable to say C is significantly higher-level than assembly.
1. My parenthetical at the end was anticipating this—then again, the translation between C and asm frequently is easy, albeit tedious.
I guess you could replace gcc with a dumb compiler / turn off optimizations. And throw in compiler intrinsics while we're at it, after all people write hot loops with them. And pointer arithmetics actually translate nicely to all those fancy addressing modes you find on x86.
And in the other direction one could argue that those so-called "transpilers" actually compile from a higher-level language when you consider complex type-inference and static type checking. Optimizing is not all a compiler does.
First, the "semantics" had better not change, either by an optimizing or non-optimizing compiler, or you've got a miscompile.
Second, I've seen several source-to-source compilers (that the proponents of the term transpiler would certainly call transpilers) that do non-trivial transformation, e.g. to try to capture certain idioms, or that are required as a result of an "impedance mismatch" between the languages.
Sorry but can you point to the particular use of "transpiler"?
I've skimmed this several times and have now run it through a PDF OCR program, and am not finding it.
Even if this paper does use the term saying "we've been using it all along!" is hardly accurate because this has hardly been a well-known term until very recently - I've read hundreds of academic compilers (mostly on the optimization & code generation side of things) going back to the late fifties, and it's only a term I've seen used in the very recent past.
The second to last paragraph. And they use the term in quotes which makes it appear like it could have been new at that time.
Transpiler means high-level to high-level translation. Compilers which implement such a translation have properties and challenges in common. It's a subset of compiler. I don't know how anyone can think it's wrong to have a term, whether it's new or not, to refer to a useful subset like that.
What harm or misunderstanding could it possibly cause?
Thanks for pointing out the reference - the OCR apparently stopped part-way through the document.
The harm comes from having a definition that is so loose as to be meaningless. You're now saying "high-level to high-level", but most people use the terms "source language to source language", and several people here have admitted that a compiler that generates assembly should be considered a transpiler.
If you're going to define it as something that goes from a "high-level" language to another, then you need to define high level. Is C high level? is C++? is HLSL (there are lots of HLSL <-> GLSL translators)?
Also, in almost every context that I see "transpiler" used, people say that it's a transpiler from X-to-Y, which adds zero value above saying it's a compiler from X-to-Y.
People usually don't feel the need to qualify what they are compiling from or to, despite the fact that many compilers target (or use as a source) many kinds of languages and/or intermediate representations.
The overall point here is that compilers (of all kinds) are simply translators - some from a high level source language to one form of (textual or otherwise) IR, some from one form of IR to another, some from one source language to another (be it high level or not), etc. Saying one is a "transpiler" and others are not, especially when you give adequate context and say what the source and destination forms are, just doesn't add value.
Yep saying transpiler is just being more specific, like saying car instead of automobile - although it is probably superfluous when including the names of both input and output languages.
By that definition every compiler that generates assembly language is a transpiler, rendering the term meaningless.
Perhaps you don't think you could "just as well" program in assembly language, but there are many many people who do, perhaps not in your own problem domain.
>Perhaps you don't think you could "just as well" program in assembly language, but there are many many people who do, perhaps not in your own problem domain.
I don't think it matters if there are "many people who do" -- as long as they are still a small minority compared to those who don't. After all you can find people believing everything, I'm sure some are even writing web apps in Assembly.
That said, C to Assembly (as opposed to binary executive) could be said to be transpiling too -- from "portable assembly to assembly".
Err, number of people agreeing on a specific meaning is exactly what determines the definitions of a word.
Same for usage of things.
There were always be outliers for whom a thing is better used for Y rather than X, but in the end is what the majority sees the tool as useful for that determines how it's defined (in casual use, dictionaries etc).
Forget that ++ and -- operators are deprecated and not available in Swift 3.0; simple but profound mistakes like this do not instill confidence at all.
FWIW:BuckleScript(https://github.com/bloomberg/bucklescript) is to my knowledge the only compiler which compiles an existing language(not a language designed for compilation to JS) to JS without name mangling.
That's a poisonous chalice not the holy grail. How can someone to prefer JS to Swift is beyond my comprehension.
Or is that "if the only tool you have is a hammer" situation?
I haven't tried Reactive Native or other options, but my gut tells me it's less safe to write in Javascript than Swift. What would be an advantage that outweighs that?
This has a very similar name to the Japanese text encoding, Shift_JIS. I doubt people will be confused since these are two different domains, but they sound very similar to pronounce.
I'm not sure if the examples are actual results from the transpiler, but there are semicolons in the Swift code which are not necessary unless you have multiple statements on the same line.
How I would love to be able to write front-end code with all the safety checks of Swift, and have it transpiled on a commit hook or something! Wonder how this could work converting to React?
I hadn't! I just started writing React last week, and coming from writing Swift it feels more wild-westy. Of course, that's how I felt going from Java to Objective C long before Swift came around, so I'm sure I'll get used to it. Nice to know about these kinds of things though!
This is a cool project. I wonder what inspired the developer to make this and what could be some use cases for this? I could see it being used a tool for learning.
Yes, read the browser docs, many browsers support languages other than JS.
However, if the browser only supports a given language than the final target must be that language, similar to how only machine code executes of a CPU, everything else is just a turing machine emulating another turing machine.
ActionScript is only supported through the Flash plugin, and inside the Flash environment -- and even that is taken out altogether in the new Chrome release.
C++, not at all. Except if you mean Chrome is written in C++ itself, or you can use Native Client, which again is another plugin like environment.
So, nothing similar to JS at all.
Might as well have mentioned emscripten, which does end up running as a web page.
TypeScript would be a simpler translation target because it's very similar to Swift, and then TypeScript could handle the translation down to ES6, ES5, ES3, etc. Why reinvent what's already available?
I wonder why doing this. What's the applicability of doing this? Will this applicability still remain valid when WebAsembly hits with a real swift binary?
That's an unfortunate name. Way too close to Shift JIS, an encoding for Japanese characters. IANAL, but it seems close enough to warrant copyright issue.
First of all, if it's the name of a an encoding there's no "copyright issue".
Second, nobody (except the Japanese) really gives a duck about Shift JIS (and not many people care about Shift JS either) to warrant anything on either side.
One major part of Swift is its value types; many core types, including arrays, are passed by value, like a C struct. So
should make `a` equal to [1, 2] and `b` equal to [1, 2, 3]. ShiftJS eschews these semantics and instead passes arrays (and other value types) by reference, which makes it basically impossible to share code between real Swift and ShiftJS.Also, the demo compiles
to which doesn't instill confidence.