Hacker Newsnew | past | comments | ask | show | jobs | submitlogin

> - don't pretend the JVM and JS have identical runtimes when it comes to concurrency or numerics. Clojure is a language, not a platform abstraction.

> - Emit efficient, optimally minifiable javascript code, at the cost of offering something less traditionally lisp-y when it comes to eval, macros, and other forms of code loading. Those are still possible, but some discipline is imposed.

This is exactly the kind of thing I'm talking about: Scala.js doesn't need to make these tradeoffs at all! Concurrency works identically (just no parallelism), numerics work identically, macros work identically. The list of pure-Scala things that behave differently in Scala.js is shockingly short (https://www.scala-js.org/doc/semantics.html)

Scala.js sacrifices none of these and still emits efficient (sometimes faster than idiomatic JS!), optimally minifiable javascript code. The fact that Clojure needs to make these tradeoffs and Scala doesn't is largely due to Scala's static nature allowing more aggressive analysis and optimisation at compile time. For example, the reason Scala.js can provide exact semantics for numerics while maintaining high performance is precisely because it knows what exactly types things are, and can use that information to optimize the generated JS in a semantics-preserving way (http://lampwww.epfl.ch/~doeraene/presentations/jslongs-vmm20...)



ScalaJS is one of the projects which I don't understand why more people don't rave about. It's /extremely/ nice that I can have a single project which share code between them for validation; no need for swagger or OpenAPI.


I think you're overshooting how many differences there are between Clojure and ClojureScript. They're mostly identical.

The big difference is the interop, and from your link with Scala.js it seems that's also true of it.

I'd say in practice, where ClojureScript and Scala.js maybe can differ a bit is that idiomatic Clojure itself relies a lot more on interop than Scala. So probably more Scala code doesn't depend on Java interop and thus can be more easily ported to Scala.js, whereas more Clojure code relies on interop making it more difficult to port to ClojureScript.

The other big difference is that Clojure supports runtime macros and eval, and those would require the compiler be bundled with your frontend app, which would increase the bundle size a lot. So ClojureScript needs to drop some of the dynamism. It's very rare to have Clojure code that actually depends on this though.

Overall in practice, what you'd want to share between backend and frontend is sharable as-is between Clojure and ClojureScript, which is all that's really needed. You'll build a React JS based frontend in ClojureScript, and share data-model, validation, transforms over it between backend and frontend. And you'll want your render functions to be shared between them as well so you can do server side hydration for React.

P.S.: I also want to point out there is a camaraderie between ClojureScript and Scala.js, and the devs exchange ideas, taking inspiration from one another as well. Since they face a lot of the same challenges.


The JVM offers real threads, and JS not. The JVM offers nanosecond-precision time measurement, and JS not. JS has a single type for representing numbers. And so on.

How can those possibly be abstracted away and unified? If doing so, what would have the solution have to do with type inference at all?

i.e., this is simply a difference in how one approaches platform interop (raw vs abstracted/unified). Clojure occasionally offers cross-platform abstractions (e.g. core.async) but it's not its main philosophy.


> The JVM offers real threads, and JS not.

How does this make a difference to the Clojure interface to threads?

After all, if you're running your JVM on hardware that doesn't support parallelism, you're not getting parallelism anyway.

> JS has a single type for representing numbers.

The comment you're replying to already covered that:

> For example, the reason Scala.js can provide exact semantics for numerics while maintaining high performance is precisely because it knows what exactly types things are, and can use that information to optimize the generated JS in a semantics-preserving way

> Clojure occasionally offers cross-platform abstractions (e.g. core.async) but it's not its main philosophy.

I can understand providing different libraries for different platforms, but language semantics should remain (as much as is possible), e.g. numerics.


> How does this make a difference to the Clojure interface to threads?

Clojure futures, promises, and some of its other concurrency primitives have synchronous semantics. So they don't take callbacks, instead the calling thread blocks on the result when it's at the point where it needs to wait for it.

So that interface doesn't work with JavaScript's concurrency model. On the other hand, Scala futures have a asynchronous interface, and you specify a callback for success and error, and cannot block waiting for them, which happen to be 1:1 with the JavaScript promise interface.

That's why on ClojureScript you don't have access to those concurrency construct, since they just don't work within the JavaScript model.

The compiler maybe could have gone through amazing lengths to rewrite all use to an async one, but it just so happens there is already in Clojure something that does so, but it rewrites a CSP interface to an async concurrent model (not futures and promises). It's called core.async and this one exist in both Clojure and ClojureScript.

> I can understand providing different libraries for different platforms, but language semantics should remain (as much as is possible), e.g. numerics

Even Scala.js has some differences in numeric types they list in their page. There's always the possibility of mismatch of a language over some runtime. In ClojureScript I think it was planned to eventually bring the same numeric semantics over, but they started without, and it seems no one minded and then never bothered.


This is also what I love about js_of_ocaml. It preserves the language semantics.


How is concurrency implemented in Scala.js? Eg switching tasks when you use Future for concurrent long running tasks that do IO or sleep?


Scala's Futures work unchanged, it just has a single-threaded threadpool instead of a multithreaded one. Anything built on Futures (e.g. my Castor actor library) is unchanged as well.

Scala Futures are more or less a 1:1 mapping to Javascript Promises, and work identically.

There isn't long running IO or sleeps, same JS, but this isn't a big problem in practice. As long as you're not juggling threads directly - and most Scala code doesn't - your code should run unchanged concurrency-wise in Scala.js just with no parallelism. Though of course you can't use OS interfaces like java.nio.file.Files in the browser!


Thanks for the explanation!

I think in this case Clojure provides pretty strong competition with core.async - you do have to use the async specific calls for sleeping and channel IO but it still works using the same API on JVM Clojure and ClojureScript sides.




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

Search: