I feel like you've not seriously used each of them. Ghcjs is very interesting, but also very, very early. The ecosystem is tiny, the compiler produces huge and slow builds, the interop is tricky, the semantics imperfect. It may be a very long time before any of those are resolved, too! Compiling a lazy language to Javascript is no simple trick. Luite deserves all the praise in the world for getting it this far... but there's just a long way to go.
So, in the terms of this article, Ghcjs might be around the same place Scala.js was 2 years ago. In the mean time, Scala.js has taken off in a lot of ways. It's yet to be seen what will happen with Ghcjs.
Your hunch is correct, I have not really used either, I'm just generally interested. From a distance, they both appeared to be ambitious and somewhat immature. I'm curious about your comment regarding tricky interop and "imperfect semantics" of ghcjs, could you elaborate? Certainly laziness does make FFI harder, but I wouldn't call that a problem with the semantics.
Laziness doesn't just make FFI harder (although it does do that too) it also makes code generation harder and implies the need for a sophisticated runtime. All of this makes the generated code bigger, slower, and more opaque.
A strict language can just skip all of that so long as it's willing to live within the limitations of the Javascript model. Scala.js does that for the most part and ends up with both nice FFI and a minimal runtime.
I don't see being strict or lazy as being the main source of the size of the deployed code (especially as Ghcjs inherits an excellent strictness analyser from GHC). Scalajs would have to emulate many JVM characteristics and port many JDK classes. Then you have huge core Scala libraries like collections, which are far larger and more complex than the Haskell equivalents. You will have to back up your argument with numbers to convince me.
You also haven't elaborated on your comment about "imperfect semantics".
I'm not really interested in putting in the effort to convince you. This opinion arises from conversation I've had with users of ghcjs, developers of ghcjs, and people very familiar with GHC compilation pipeline details and runtime details.
The last I heard, the primary dev of ghcjs intended to build a new codegen which would alleviate some of the issues here, but that hasn't happened yet to my knowledge.
The comment about imperfect semantics arises from a few weirdnesses around JS interaction with laziness. Perhaps the biggest one I remember is that when calling Haskell from JS you have to be prepared for the idea that results may not be fully evaluated. There wasn't any real mechanism to force and block on the Javascript side and certainly no mechanism to handle it asynchronously (promises wouldn't work unless they decorated inner layers of a data types which would be terrible).
This also plays out on the Haskell side in terms of managing retention strategies.
In a strict language both of these obviously just vanish.
The main difference is that Scala.js people sat down and did all the painstaking work, while Haskell devs spent the time talking how intellectually superior the are and got nothing done.
> Haskell devs spent the time talking how intellectually superior the are and got nothing done.
I could give a recap of Haskell development that's happened while Scala.js has been developed to debunk this silly accusation, but it wouldn't change your mind.
Well, Scala.js went from not existing to rock solid in 3 years. The various Haskell-to-JS efforts existed much longer, but none have the support, completeness and maturity of Scala.js.
That is down to who has the most funding. Javascript itself has even more "support, completeness and maturity" than Scalajs. But obviously that's not the deciding factor.
Scala.js was largely done by a single student on top of his other university duties.
I think it's mostly down to determination. Some things are hard, but need to be done.
Haskell people just seem to give up more easily - see all the half-working projects. Nobody finishes stuff, the next dev just starts his own new project, and abandons it later.
Scala.js handily beats JavaScript in terms of IDE support and tooling
That's nonsense. I think it's great that outside contributions (like allowing the use of the Scala's CI infrastructure) are valued, but the actual commits show a clear picture.
There is no determination in failing to get anything done and doing NIH for a decade.
This project has been released to exchange some ideas and collect feedback from the OCaml and JavaScript communities.
It is still in an very early stage and more like research projects though we already had some good results.
Note that ocamlscript is inspired from typescript and js_of_ocaml, we like the readability of typescript
generated js code, while don't want to loose the expressivity of a powerful langauge, ocaml, here in particular.
The main difference from js_of_ocaml is that js_of_ocaml maps the whole bytecode
program into a js file while ocamlscript maps an ocaml module to es6 module.
Indeed, there is some difference in the runtime representation between js_of_ocaml and ocaml, for example, ocamlscript uses
js string and array for ocaml string and arrray(we do this since we want an optimal js output at the price of a bit runtime incompability),
this may make interaction between js_of_ocaml and ocaml a bit harder, but that's exactly why we made an early release to hear what other people may think.
js_of_ocaml is an excellent project and we learned a lot from it and will seek some collaborations to share some workload.
OCamlScript does some cross-module optimization it seems. That's really cool, and js_of_ocaml does several optimizations as well, but for both approaches, it seems we'll not be able to take advantage of all really great f-lambda optimizations to come because they happen at a compiler stage after OCamlScript and js_of_ocaml have already intercepted their intermediate representations. Do you (or Drup) have thoughts on that?
It would be great to see a list of known/anticipated compatibility issues. For example, I notice that when compiling `String.set` with OCamlScript, the generated JS ends up trying to set/mutate a character in the JS string, which has no effect because JS strings are immutable. (I'll file a github issue). I imagine there will be several small issues like this and it would be good to get a sense for how much work it would be to solve that long tail of compatibility work. In the process, I think the community can help determine if this approach could ever practically match the near-perfect level of compatibility that js_of_ocaml has achieved via byte code compilation - and whether or not it matters. To me, compatibility is very important, but so is debugability. It might be clear that it's okay to compromise the readability of OcamlScript in order to achieve more compatibility without compromising debugability (for example, using a `new MLString("xyz")` wrapper that uses a JS `String` by default for performance, but then swaps it out upon any mutation. (Or perhaps you can limit it to programs that have disabled string mutations in later versions of OCaml).
The good news is that if you can achieve perfect compatibility with this approach, then I believe that runtime performance becomes less of a concern and OCamlScript's job gets a lot easier. This is because, if you've achieved perfect compatibility, you can easily compile your project with another toolchain such as js_of_ocaml, that has equal compatibility, but (in this hypothetical scenario) had chosen to focus more on performance instead of debugability - without changing anything in your program.
This would free you up to not have to worry about applying any optimizations in this readable/debuggable mode - you get to focus entirely on whatever makes the development flow faster/easier. (You'd probably still want tail call optimization because order of memory usage could be considered a key part of compatibility).
Similarly, I wonder if the existence of a fully compatible, (hypothetically) slow runtime performance, but perfectly debugable compiler backend, would then free up js_of_ocaml from some of the constraints/work that is required to make their code output reasonably simple/debuggable. Does the existence of a fully compatible OCamlScript mean that js_of_ocaml's job gets easier?
I'm excited to discuss how the two projects could potentially become an asset to each other.
Author here, just created a new account : ). I think ocamlscript is complementary to js_of_ocaml, actually it is inspired from js_of_ocaml.
For those who like to write ocaml only application, js_of_ocaml is perfect, ocamlscript is designed to help easier integration with existing javascript system.
In the (nice) immutable map example on that page, a reference cell is used & updated each time something is added to the map. How would the JS output differ if you built the map using a fold or similar, without any refs?
Before that, I'd ask if the implementation of OCaml's map is at all similar to the implementation of Immutable.js's Map. Different internal data structures/algorithms are probably the most likely explanation for any difference. Still, it's nice to see a benchmark that shows how OCaml compiled to JS is competitive in performance, even when compared to a very popular library that is known for performing well.
Also a fair point, and I take your implication that the effect of a change to the outer loop syntax would probably be lost in the noise. It just struck me that using ref cells and loops feels like a concession to writing Javascript-style code in the first place (I grant in this particular case it makes the code more obvious) and I wondered how much difference it actually makes. Is the compiler able to flatten a fold into a similar loop? Or do you get a readable fold call in the JS code and, if so, is it much slower? Or neither?
List.fold_left is compiled into a while loop in ocamlscript. The main reason that I used for loop is that the generated code is the same as hand written js, so the comparison is more reasonable
Edit: I am a practical programmer, I am in favor of both for loop and recursion as long as it is readable
That is how you justify the project, and it makes sense, when you look at it with a microscope.
But my remark still stand: we now have two compilers to js, their API/FFI is not the same, so you can't factorize work, and the integration work could be made on js_of_ocaml too (see alain frisch's project).
actually we have great relationship with Lexifi, and we did exchange some ideas about ocamlscript before, I think mostly we are on the same page, there is enough room for two compilers :-)
Its good to see ocaml->js back to life. Have you considered supporting TCO or arbitrarily deep stack? I'm just curious how often could we reuse ocaml libraries with just tail call to loop conversion.
The compiler is compiled into JS and native code.
It generates highly readable JS code and easier FFI.
It compiles super fast (generally 10~100 faster than Scala) and generates optimized code.