Hacker News new | past | comments | ask | show | jobs | submit login
ShiftJS – Swift to JavaScript transpiler (shiftjs.com)
121 points by _zhqs on Aug 15, 2016 | hide | past | favorite | 98 comments



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.

Also, the demo compiles

  let a = 3 4
to

  var a = '3 4';
which doesn't instill confidence.


I'm curious, when are these kinds of compilers truly useful?

Maybe its my own ignorance, but language design often doesn't map 1:1, so wouldn't it end up feeling forced?


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.


That's the point, to use a "better" language. Especially if designs aren't similar.

If behavior of compiled program matches the original one, that is.


> 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.


Any language can be compiled to any Turing complete machine.

It is possible to compile Swift to JavaScript.

The only thing impeding that is the quality of the project.


I personally would never work on a project that used one. It is just one more layer of headache for anything non trivial.


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 :(


I'm pretty sure that's doable now that there's WeakMap in ES2015.


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.


> most TC39 members don't understand the difference

This seems unlikely.


I beg to differ. Observe long threads like this one (which I started) https://esdiscuss.org/topic/what-is-the-status-of-weak-refer...

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.


js_of_ocaml and BuckleScript both do a very good job with OCaml.


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.


Oh, ClojureScript also seems nice.


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.


And then you need to wait for browsers to support it...


Browser support landscape has changed. The only concern is Safari but I'm optimistic


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.


It doesn't seem to handle some pretty basic swift code:

    var a = 3
    let b = { a * 2 }()
http://i.imgur.com/D1FxZcN.png


How about this, too:

    var a = 1 ^ 2
As it stands, I find the thing entirely useless.


Same here, tried a basic loop and it broke:

for i in 0...10 { print(i) }

Then again, the project does say it's in early development. Perhaps a re-visit in a month or two will yield some nice progress.


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.

You shouldn't worry until academia starts using it. Oops. Here's an article with 'transpiler' on the title: http://dl.acm.org/citation.cfm?id=2173694

It's a good thing that English is descriptive and not prescriptive.


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).


Some people treat C as portable assembly.

Does that make gcc a transpiler for converting portable assembly to platform-specific assembly due to their similar semantics?


I knew someone would bring this up.¹

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.


I don't buy this definition at all.

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.


> I am not sure where this term was first coined

The term has been in use in academic literature since at least the early 1960s.

http://comjnl.oxfordjournals.org/content/7/1/28.full.pdf+htm...

> we've been doing source-to-source compilation for a very long term

Yes and we've been calling them transpilers all along!

I'm a programming language researcher and I think the term is very useful to distinguish between different kinds of compilers.


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.


Calling it a compiler is certainly accurate but transpiler seems more appropriate in this context.


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.



X86 to Microcode "Transpiler"

Microcode to Electron "Transpiler"


Transpiler vs compiler is a useful distinction.

If you could just as well program directly in the target output/language for the problem domain, then it's a transpiler.


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".


So the number of people doing something is how you determine the definition of a word?

That really makes no sense.


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).


Language can change. You gave three distinct concepts that all map to the same word. That alone shows why that change may be beneficial.


From their examples.

Swift: var n = m--;

JS var n = --m;

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.


Also, --m and m-- are completely different. One decrements first and returns the new value, the other returns the new value and then decrements.


I hope these sort of madness of everything to JS goes away with the advent of webassembly.


Or just goes away.


It doesn't hurt you.


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.

It is a serious piece of work, the compiler is compiled to JS too, so you can play it in the playground(even work offline): https://bloomberg.github.io/bucklescript/js-demo/

It also generates very small code output, for example `hello world` is 20 bytes instead of 100K bytes


The holy grail would still seem to be the opposite: writing native iOS apps in JavaScript.


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?


Less safe in what sense?


Type safety & all that jazz. Swift is a much more powerful language than javascript (and typescript) is.


Why? JS is crap compared to Swift. There are various technologies that allow you to do that anyway(i.e nativeScript)


Getting closer with React Native.


Some are already dong this with Apple's JSCore.


There's already NativeScript and React Native.


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?



flow (http://www.flowtype.org) is also an alternative. Developed at Facebook.


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.


First there was "everything that can be written in JS will be written in JS". Now it's "everything that can be compiled to JS will be compiled to JS".


"Everything that has been written in JS will be rewritten in every language that transpiles to JS"


Are there other ways to use other languages in the browser asides from transpiler, and asm?


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.


Where many = IE, and other languages = VBScript (if that's even supported anymore)?

Because that's the only actual and in use browser I can think of that supports a language other than JS natively.


Chrome supports ActionScript natively, as well as C++


No, it doesn't support either.

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.


If you install Chrome it supports these things, that's 'native' to me.

Does it act like JS? No, but its still native.


It would be interesting if it first converted the swift code to TypeScript then JS


Why would that be interesting? Presumably a Swift compiler handles its own type checking.


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?


TypeScript is merely JS with added type annotations, annotations which are stripped off when it compiles to JS.

There's no usefulness of TypeScript as a target for a compiler for Swift, because Swift already has static typing.


Totally agree :) I'm working on something similar that uses flow and Crystal (which shares a lot of language features with Swift).


someVar++ and -- is deprecated already.

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.




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: