Hacker News new | comments | show | ask | jobs | submit login
JSweet: A transpiler from Java to TypeScript/JavaScript (jsweet.org)
54 points by renaudpawlak on Dec 15, 2015 | hide | past | web | favorite | 54 comments



The GWT team has been working on a next-gen compiler that transpiles directly to readable, JsDoc typed ES6, designed for high levels of optimization by the Closure Compiler. Because of the new JsInterop system was designed for GWT 2.8, that is shared by both GWT and this new compiler, low impedance interop between Java and JS will be possible, as well as a tool that reads WebIDL and typescript libraries, and generates Java types.

As an example if the level of low impedance between the languages, consider the @JsFunction annotation,

@JsType(isNative = true)

interface EventTarget {

  void addEventListener(String event, EventListener listener);
}

@JsFunction

interface EventListener {

  void onEvent(Event event);
}

Using Java8, you can write code like:

document.createElement("button").addEventListener("click", x -> Console.log("Hello World"));

The difference between GWT running this, and the new compiler, is that the output of the new compiler is a standard ES6 module.

The JSweet claims about GWT are out of date. First of all, in-browser Java debugging has been available for years. GWT was in fact, the very first transpiler to leverage Chrome source maps. I demoed this three years ago (https://www.youtube.com/watch?v=-xJl22Kvgjg)

Secondly, GWT has a "reversible and compatible" JsInterop system that provides full two way low-impedance bidirectional interop, you can even do things like subclass JS objects from Java, or define "fields" via @JsProperty which invoke methods ala Object.defineProperty()

Third, the JSweet stuff looks like it doesn't obey standard JLS semantics. Does it even compile the JRE or Guava? This is absolutely vital for code sharing between platforms (e.g. server, Android, j2objc). If you can't run standard JLS semantics, there's little point to writing the code in Java in the first place.


If JSweet claims about GWT are inaccurate they will be fixed. But you have to understand that JSweet is a syntax mapper to TypeScript (explained here: http://www.jsweet.org/papers-and-publications/). So they are really different techniques. With JSweet, what you see is what you get. There is a direct correspondence between the written Java code and the output TS/JS, and that for ALL the program (not only fractions of it). So we are probably not always referring to the same features and certainly not the same ways of doing things, which probably explains some misunderstanding in JSweet assertions about GWT.

JSweet is not meant to obey JLS because JSweet is "just" Java syntax. It is not Java anymore. However, some specific transformations (very few) have been implemented so that Java programmers are not confused by JavaScript runtime behavior (as explained here for instance: http://www.jsweet.org/language-specifications/#Variable_scop...).

JSweet is not meant to be at all compatible with Java legacy APIs. With JSweet, you will not even be able to access the Java Object API nor the Java String API. Instead you directly access the Object and String JS APIs. So who cares about JLS semantics then? What JSweet is trying to compile is JavaScript applications, not Java, and the examples show that it works for not-that-trival code.

In short, JSweet is for writing real JavaScript applications in a Java compile-time environment (not runtime). Nothing more, nothing less. I understand that GWT new generation it aiming somewhat at integrating JS apis too... But AFAIK, JSweet is the first project to give access to hundreds of JavaScript libraries in Java thanks to the TypeScript to Java API translator.


If you can't run standard JLS semantics, there's little point to writing the code in Java in the first place.

I'm an old user of GWT and hopeful for its revival, but I strongly disagree with this statement. There are two ways of going about this - one is to hew closer to Java semantics (the GWT approach), the other is to hew closer to Javascript semantics. Strict JLS semantics is great for bulk compiling huge projects, but sucks for interop. In the new GWT, does Long become a javascript number? In the old GWT it was an opaque object, which was a constant irritant when interacting with JS.

There is room for both approaches, but as a web developer, I need to interop with JS frameworks more than I need to compile Quake.


Emulation is a matter of degree. There are certainly many places where GWT takes a shortcut or doesn't support a feature compared to a real Java VM. But the idea is to get it right for most of the code a Java developer is likely to write, so you have a chance of writing portable code and getting it to work everywhere without a wholesale rewrite.

In particular, mapping Java longs to doubles doesn't make sense. If it's your own code, why are you using long instead of int (or double) which perform much better and interoperate well with JavaScript? If it's someone else's code, how do you know it doesn't need the full 64-bit range?


It makes a ton of sense when working with identifiers, which is so common that it's almost the standard usage of java.util.Long. In most practical applications you can safely assume that identifiers are not going to use the full 64 bit range. And being able to pass them to javascript frameworks is incredibly useful - which is to say, the inability to use them in JS was incredibly frustrating with GWT.


GWT 2.8 already does this. Long are passed as doubles unless they exceed 2^32. Also, Double and Boolean are now unboxed native values. And Object[] can be transparently passed to JS and back. Moreover, varargs work between JS and Java, maintaining the semantics of both sides to work correctly.

If you want to build true hybrid apps that combine Java and JS programs, there's a lot more considerations that have to be made, we learned some hard lessons the last 3 years on Inbox, which is why JsInterop was redesigned 3 times to get the semantics sound.


Do you need java.lang.Object and java.lang.String to work properly? Do you need Java interfaces and 'instanceof' to even work?

We're not talking about 100% JLS semantics, but if you can't even compile code that utilizes java.lang.* and java.util.*, why not just use Kotlin, Dart, Typescript, and other languages provide high level typed JS like languages.

Using a language that doesn't compile properly leads to all kinds of nasty surprises and hard to diagnose bugs.


Is there any story for using GWT to go from Java to LLVM bitcode/IR? RoboVM just closed source, and VMKit is dead along with DragonEgg (maybe GCJ had a hope of working at some time?). I guess I'm looking for a sort of GWT + reverse Emscripten? Too bad the LLVM bits of ART were discarded.


There is j2objc, which is a sister project to the new compiler. J2ObjC is the compiler behind Google Inbox and Google Spreadsheets on iOS.

It doesn't go to LLVM, it goes straight to ObjC/C. You can then import this into any XCode project.


Hey Ray! Glad to see the GWT team is still making strides :)


How can you tell it's disobeying standard JLS semantics?


I looked at the source. Doesn't seem to support class initialization properly. It models lambdas as just arrow functions, which won't work if you try to pass them as Objects to other Java functions (where is Object.getClass()/hashCode()/equals() ?) It doesn't handle runtime checks of Java interfaces at all, for example:

interface Foo {} class FooImpl implements Foo {}

Object o = new FooImpl(); if (o instanceof Foo) -> compile error

The inability to even handle Java interfaces should be a clue right off the bat that this won't compile any non-trivial Java code base.


Class initialization is handled with semantics that approach the Java ones so that it is not confusing for programmers. AFAIK it works but in case it doesn't I'd be curious to know what's wrong.

Lambdas are meant to be used like in JavaScript. It totally works. JSweet supports function references (MyClass::myFunction) and defines the JS Function object for passing plain object functions. No need for getClass(), hashCode() or equals() in JavaScript.

Regarding interfaces, the situation is quite different. JSweet does not support interfaces in the way Java supports interfaces. JSweet interfaces are mapped to TypeScript interfaces which are pure compile-time entities used only for typing (which differ strongly from Java's). That's why the code you quote will not compile in JSweet, but it does not mean that JSweet is wrong: it means that your way of understanding an interface in JSweet is wrong (unlike Java, JSweet interfaces have no runtime representation). This part requires further documentation though... JSweet is a very young project.

Generally speaking, when you program with JSweet, you have to stop thinking too much in terms of Java semantics, and start thinking more in TypeScript or JavaScript, at least for some cases. The very most of it remains close to Java and very intuitive to programmers, since IMO, Java and TypeScript are quite in the same language family.


<soapbox>I really dislike the term 'transpile'. It's just a compiler. There's nothing really different in theory or implementation of a "source"->"source" vs a "source"->"binary" compiler. If I were to write a CPU in HDL that natively executed JavaScript, would this change from a 'transpiler' to a 'compiler'. The distinction made is simply what the end user is going to do with the result.</soapbox>


I like the distinction in the sense that transpilers generally don't do optimization. GWT, Dart2JS, and Closure Compiler aren't transpilers in this sense. They are full blown optimizing compilers.

Putting GWT or Closure in the same category as JSweet, TypeScript, or CoffeeScript obfuscates what these tools do.


So if I run gcc with -O0, it's a transpiler? Remember that gcc outputs a human readable language that another program (the assembler) converts into machine code.


In one sense yes, but GCC the utility is an optimizing compiler.

Would you call a sed script which converted Java to ES6 via regexes a compiler, even though not an ounce of compiler theory was used?

I would define transpilers as a subset of compilers that are are only interested in high level language conversion.

It's the difference between say, translating books between foreign languages, and being an editor who rewrites a book to be better.


I mean... sed is Turing complete. Yeah, it's in the Turing tarpit, but if you were able to successfully translate Java to ES6 in a sed script, yes I would call that a compiler. In a lot of ways, much of compiler theory is concerned with the best way to write a compiler (for many different optimization functions of "best"), not the only ways to do so. Just being a crappy compiler doesn't make it any less of a compiler.


In that same vein, what's the point of the term "interpreter"? Isn't an interpreter just compiling source code into an in-memory representation, and then executing that?

I'd argue that terms like "interpreter", "compiler", "decompiler", "transpiler", etc. are helpful for establishing a general taxonomy of program transformation tools, even if the boundaries aren't so clear.


The distinction between execution and transformation is a useful one. For instance, depending on the semantics of the source language, you could be able to guarantee termination of a specific transformation of that source, but not be able to guarantee termination of execution of that source.


This comes up every time the term is used, but I don't get it. It's hardly the first time we've introduced redundancy in English, and it won't be the last.

As to your question, it doesn't really make sense for something to "change" from a transpiler to a compiler, any more than it makes sense to change from a Ford to a car. All transpilers are compilers; "transpiler" is just a little more specific. You know, like "gigantic" is a little more specific than "big", or "square" is a little more specific than "rectangle".

FWIW, I disagree with cromwellian that it's anything to do with optimization. I wouldn't personally say CPython transpiles Python to bytecode, but I'd also say an optimized transform from Dart to JS is a transpile. In my eyes, a transpiler is just any compiler where the target language is only intended to be written by humans.

(This changes over time, though. I'd probably not call Nim to C a transpile, although I might have in the past. We're probably reaching the point where X to JS stops being a transpile, too.)


> This changes over time, though. I'd probably not call Nim to C a transpile, although I might have in the past.

You think that I can take a program, run it on some input in the past, and it's a transpiler, assuming that it fits the other part of your definition. Then I wait, say 10 years, run that same program on the same input and now it's not a transpiler?


I don't get why this library is better than GWT.


It is different. The focus is different. JSweet focuses on TypeScript/JavaScript while GWT focuses on Java and see JavaScript as just an execution engine. Now GWT is obviously adding bridges to JavaScript, but still, it is not the main purpose. As I understand GWT, it still relies mostly on the GWT SDK and on Java semantics at runtime.

If I may add some hopefully-constructive criticism, it feels that GWT (like some other well-known Google frameworks) is trying to address too many issues at the same time... But that's my own opinion and it does not mean I don't appreciate it.


The essential argument seems to be 2 things:

1. It can generate node.js compatible JavaScript so you can run Java code in node. I'm not sure how many people have felt the need to do that, but it's not something GWT can do.

2. It understands TypeScript, so it can make use of the work people have done to (essentially) create type annotations over various JavaScript libraries, and automatically (? or at least painlessly) build Java wrappers for them.


GWT in Node JS https://github.com/cretz/gwt-node

Generating Java interfaces from TypeScript is coming soon. We have done this in the past from WebIDL in the Elemental library provided by GWT.

A new tool which can do it from WebIDL, Closure JsDoc, or Typescript (all three) is being developed.


Well it is already done in JSweet: checkout these APIs http://www.jsweet.org/candies-snapshots/ It is all compiled and deployed as Maven artifacts on our repo. And we even give access to all the JavaDocs if you follow the links to the end... checkout this Angular JavaDoc for instance: https://jsweet.org/apidocs/snapshots/org/jsweet/candies/angu...

Let us know if you are interested in collaborating on this (never hurts asking).


Why anybody wants to run node.js instead of Java. Java is much better suitable for the server side than node.


Nobody has commented yet. I can only guess this is because all agree: it goes without saying that this sweeping generalisation is patently untrue.

Still, allow me to humor you and give just one (of the many) reason(s) why: async I/O.

Yes, it's possible in Java. Yes, Java has futures that make it not just possible, but also a reality in this universe.

Unfortunately, Java has made the inconvenient decision to retain backwards compatibility and keep all its synchronous I/O operations available. Perhaps not a complete folly. For all of the good that decision has brought, there is one downside: any code (any lib) can still lock up an entire OS thread. Result: all libs actually do this. No matter how Future-istic your codebase; use any lib and you're back to sync I/O.[1] This is absolutely killing for high concurrency, I/O bound servers, who now lock up not just an FD or two per conn, but also an entire OS thread.

Javascript's threading model, on the other hand, is so shit that it came out the other side. By being inherently single threaded, all blocking I/O must be done through callbacks. Result: you can't block an OS level thread in javascript! So all libraries are inherently async (from the OS level).

There are, of course, many more reasons why Java is not "much better suitable for the server side than node", but this is just one of them (libraries, existing knowledge in your org, community, ...). In reality, both have their uses. Node has proven itself by now.

Disclaimer: I actually hate Javascript. And Java. Including Java 8 (for all the shit it, wisely, keeps around).

[1] E.g. Amazon's official AWS SDK, which is sync IO. Want async IO for S3 (or anything there)? Write it yourself. Which you won't. Because there's already a lib. "But it's not async!" // TODO.


I hope we can agree that, whether Netty or Node can accept 100M connections per millisecond or not -- is irrelevant if the rest of your stack cannot keep up.

In most applications, eventually you need to hit the database to process the request... and the database does not care if your original http request was asynchronous or not... it will still take its sweet time in milliseconds (or seconds for complex queries) to respond.

And the best way to prevent that is caching.

And that is where Node is at disadvantage.

In Java (and C, and C#, and a bunch of others), if your server has 16 cores, you will be running a single process on 16 cores and probably 32 concurrent threads, and they can all access some shared data (notably, database cache) with low intra-process latency, using some simple sharing primitives.

Now, Node... if a Node process is single-threaded, then one would need to spawn 16(32) Node processes to fully utilize the server, and they will be using inter-process communication to talk to each other -- much slower than Java's intra-process.


In Java (and C, and C#, and a bunch of others), if your server has 16 cores, you will be running a single process on 16 cores and probably 32 concurrent threads, and they can all access some shared data (notably, database cache) with low intra-process latency, using some simple sharing primitives.

You're right but there are downsides to this approach.

First, you're creating a single point of failure. If any part of the application crashes for whatever reason it brings everything down with it.

If you intend to have fault tolerance, maintain uptime during updates and/or provide A/B testing capabilities, you'll still require at least a redundant server and a load balancer to funnel incoming requests between the two.

Second, sharing memory across cores comes at a cost.

A simple mutex/lock strategy will work but will be inefficient as it blocks on both reads and writes to shared data.

You could choose to use a CAS (Compare and Swap) strategy to avoid locks altogether. As long as you can ensure your models as well as all of the internal data structures they use are immutable and thread safe. Considering the nested nature of OOP inheritance as well as language level access restrictions on classes/methods/members, it can be difficult/impossible to ensure immutability without hand-rolling your own data structures.

Third, sharing state across contexts comes with hidden costs that need to be taken into consideration. For instance, if the shared state for all threads is updated (ie a cached value is added/updated) it will invalidate the L1, L2, and L3 caches on the processor. The overhead of cache invalidation increases in proportion to the number of cores/processors involved.

Source: http://www.drdobbs.com/architecture-and-design/sharing-is-th...

I assume you have experience with multi-threaded programming. I won't touch on the transient nature and inherent difficulty in replicating bugs introduced in multi-threaded programming. Other than to say, I've done enough of it in the past to develop a very healthy fear.

-----------------------

What about Node...

You're right to assume that Node instances will run as a cluster. Ideally one instance per core, to reduce context switching and prevent cache invalidations between the foreground and background workers internal to a Node instance.

As for a cache layer between the DB and Node instances. Since communication between Node instances will happen at the OS level via IPC, there's no benefit to implementing a global cache internal to the Node application. Instead, it would be better to offload the responsibility to a separate service that is specialized for caching (ex redis). This reduces the complexity of the application, surface area for bugs, and development time.

Load balancing will be required anyway, so it makes sense to use Nginx. Nginx is much more fault tolerant, providing sane limits for incoming requests, as well as the ability to define routes that short-circuit requests to static files.

What's interesting about deploying a Node cluster is its 'redundant by default' nature including auto-respawning of cluster instances https://www.npmjs.com/package/express-cluster.

If you desire a more granular control over cluster management you could use https://github.com/Unitech/pm2.

To make this work the servers themselves will need to be stateless. Which means, handling session management requires additional work-arounds.

-----------------------

Java is likely 'faster' when deployed in a strictly vertically scaled setup. Node.js is more geared to horizontal scaling by default. Just like Java now includes good support for async request handling, I have no doubt the community will design tools that also favor scaling out.

Either way, I don't think raw performance is going to be the deciding factor. Choice of platform is a business decision that depends on the resources and skills and/or preference of the team responsible for development.


Recently I've been doing a bit of research on what tech stack to choose for my next project. I'm a long time Java dev and been doing JS/TypeScript dev the last year. I'd been deciding with a newer Java/JVM stack or node.js/TS.

There's a library that fixes the sync I/O issue in Java call Quasar which implements lightweight threads called Fibers. I remember seeing it a while ago and thought it was pretty interesting and it seems reasonably mature now. One of the nicest things about it is you can get async IO with a sync programming model. So no callbacks and chaining of promises.

I'll probably start with Java 8 but the other JVM language I quite like it Kotlin, which has a similar style to TypeScript in some ways, and is designed for Java interop. Kotlin + Quasar fibers seems like a pretty nice combo on the JVM.

http://blog.paralleluniverse.co/2015/06/04/quasar-kotlin/ http://blog.paralleluniverse.co/2015/08/28/quasar-0.7.3-coms...


The problem is not the lack of async IO, but the existence of sync IO. As long as sync IO is a possibility, it will spread through your program like a cancer. Even without using it yourself, all your libs will.

Does Quasar address this?

Edit: just to clarify: it's not as bad as I make it sound. Java may still be the best tool for your project. It's definitely got a great VM and good libs. If it makes sense, use it.


Sync I/O API's are still an issue, the main one being JDBC. The approach with Quasar is to have a dedicate thread pool for the JDBC calls, which in that case isn't necessarily too bad as you don't want to overload your database.

Comsat is the companion project which provides the quasar fiber integration to common libraries/services (http, servlet, db etc) which gives you the nice synchronous programming model (via bytecode instrumentation) with the async/light thread scalability.

So your incoming servlet request would use the async servlet API integration to move the execution to a fiber. Then calls to an external http service, out-of-process cache, files, NoSQL db etc would all happen within the fibers. Its mainly the JDBC that requires a pool of normal threads.

I've only just decided this is what I want to use next, so I'll be able to tell from experience over the coming months!


Easy on the 'rage'

Javascript's threading model, on the other hand, is so shit that it came out the other side. By being inherently single threaded, all blocking I/O must be done through callbacks. Result: you can't block an OS level thread in javascript! So all libraries are inherently async (from the OS level).

This statement is patently false. I/O requests are fired off asynchronously in the main context but fetch data via background threads. In addition, in a multi-core/processor system you can use the 'cluster' module to fire off multiple instances (ideally 1 per core) of the HTTPD.

As for libraries. Node doesn't provide much in terms of 'batteries included' but the NPM registry has eclipsed the library ecosystem of every other language. If anything, the issue is too much choice as there are usually 3-5 alternatives for everything you can imagine.

The minimalist nature of the Node core is a 'double edge sword'. The downside is, it requires a lot more cognitive load and research to choose the libraries to use in a dev stack. The upside is, since most of the functionality is developed independent of Node the core devs can focus the entirety of their effort improving the platform/language/packaging. The effort of building all additional functionality is the responsibility of the community.

I have used Java some and C# a lot so I can understand why devs love the ecosystem and tooling. The tooling for JS is getting better over time but there's something to be said for IDEs that come prepackaged with all the tools necesssary to be productive.

Personally, I shifted over time embracing the flexibility of dynamic languages and shifted to a mindset that favors composition over structure.


>I actually hate Javascript. And Java. Including Java 8 (for all the shit it, wisely, keeps around).

Could you give me an example of any technology which you don't hate?


Yes, but let's keep that talk for over a drink some time :) I put that in to clarify that Im not coming at this from fanboyism. It wasnt about me, but to contextualize the comment.


There's a "Features and comparison" section on the linked page that addresses exactly this.


They didn't persuade me in anything. Even worse, I don't understand some of them. What're well typed Java libraries.

BTW, GWT, support in browser java debug via sourcemaps for a long time.


Source maps doesn't compare to actual debugging that GWT used to be able to do (via a browser plugin). GWT died the day they decided that they will no longer support that.


It wasn't the GWT team that made this decision, it was the browser vendors. They killed the NPAPI. For good reason too, it was a major source of insecurity. PPAPI (NaCL) could not replace what was lost, because modeling JVM -> JSVM -> JVM calls requires synchronous re-entrant execution.

BTW, you don't have to debug in Chrome. If you attach IntelliJ to the JSVM, you can debug right inside your IDE, stepping over breakpoints in Java, etc. SDBG for Eclipse does the same things. (https://www.youtube.com/watch?v=icJEa5lcJaQ)


As far as I can tell, you cannot eval arbitrary expressions. I used to use it like a REPL back then. That's what I would miss now with source map


It might be possible to pull this off with the new compiler because it's draft mode is a true incremental unoptimized draft, and translating an expression to call the JS is a relatively straightforward ABI.


Actually, source maps and browser based debugging are quite good. They aren't as good as Java debugger, but good enough. I am working on a large scale project which uses GWT, and we are absolutely ok with source map debugging.


A few weeks back I was looking though some java code and I was hopeful I'd be able to transpile it in order to run it in the browser. I'm very experienced with JS and its ecosystem, and I figured that java was so popular that it should be straightforward to achieve this. But unfortunately, after 2 days of screwing around, I didn't have any luck.

I'm not sure if it's due to my unfamiliarity with the java ecosystem or if the tooling or documentation were just poor.

In any case, this tool looks friendlier than GWT, so perhaps I'll give it another try at some point.


I would rather make TS to JVM & CLR Bytecode, to have a unified ecosystem from the opposite side.


What are the good options to convert from Javascript to Java or to other high level languages?


Transpiling from an untyped language to a typed one would give you horrid results in most cases. The practical way would be to use TypeScript to compile your program (JavaScript programs compile fine with TypeScript) and start to manually transform your JavaScript code to add objects and types.

That said, that would be a really cool tool to have...



Why ES5 instead of ES6/2015?


I am not sure to what your question is referring to, but FYI, JSweet follows the same pace as TypeScript in terms of JavaScript generation. So it already supports most of ES6 AFAIK.


None of the sample code exposes a) es6 module format b) amd module format c) commonjs module format....

It's all closure-based jquery plugin module format.......

If you're gonna compile to "javascript" either target a version of node that's been released in the past year or target webassembly.

Otherwise you're just another coffeescript.


ES6 is only partially implemented in Node.js and requires transpilation to ES5 for client-side code.

ES5 will be continue to be the target to until either/both platforms are feature-complete.


possibly related: can some news hacker please create an internet crawling worm that untranspiles all the javascripts that it crawls and turns them into wars for everyone who wants to go the other way? I guess we also need mozilla developer network to create a browser plugin to run the war files for a given site, perhaps npm would host the wars, so we can use node to get the wars from npm for the plugin. thanks




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

Search: