The implementation really is not built on top of GCL, the bulk of the standard library is persistent datastructure implementation and functional apis. The few cases here and there like `goog.string` can be removed over time. But really what's the rush? Large portions of GCL have worked unchanged for nearly two decades. Does Google archiving GCL make perfectly good code stop working?
When we eventually remove the direct GCL dependency it will sadly be more for optics than anything else.
Right. Some of that "goog" code looks decent enough. I wouldn't remove it either simply because it was "archived". Maybe vendor the useful stuff back into the implementation and remove all the "goog"?
I felt that way at first, but after getting around to listening to a few of them you start to realize he repurposed a lot his favorite material from these cantatas into his instrumental works. For example, one of Bach's innovations was the keyboard concerto - where the keyboard played the central solo role. The profound Harpsichord Concerto in D minor BWV 1052, https://www.youtube.com/watch?v=9ZX_XCYokQo, is actually reworked from the cantatas BWV 146 & 148.
While there's obviously plenty to enjoy w/o dipping into the cantatas, you might miss out on making these kinds of connections which I think really enriches our understanding of the instrumental works.
Some scholars believe it to be an early work - possibly Bach was 19 years old. No copy in Bach's hand has ever been discovered, so there's no certainty. It is a wonderful, memorable piece of music but Bach was pretty good at that and not just at the keyboard.
Some believe(d) it to be written by someone else! No candidate was found, though. It's definitely an earlier work. It's much more in the style of (e.g.) Buxtehude than Bach's later works. If you like BWV 565, the Toccata and Fugue, you could check out e.g. BuxWv (a numbering like BWV) 149 and 156.
To be fair there were definitely still bugs in Om as well as om.next (and both were marked as being experimental and of alpha quality) - however Fulcro came along and more less took the ideas and took them further and far as I know is actively maintained. I don't use it - but I suspect there was/is probably some kind of migration path.
Some technical points about Krell. It leverages the ClojureScript `:bundle` target which was released last year https://clojurescript.org/news/2020-04-24-bundle-target. By simply producing output that is JS bundler friendly we can just piggieback on Facebook's Metro just like we piggieback on Webpack etc. when targeting the web.
We simply reuse the debug loader provided by Google Closure and load ClojureScript and Google Closure JavaScript files through the Metro server. But this is the core of ClojureScript's hot-reloading capabilities without caveats. In Google Closure namespaces can be represented as nested JavaScript objects which delivers pervasive late-binding - which simply cannot be done with ES6 modules because imports will be captured (early bound).
The REPL bit (which is an independent piece from the hot-reloader) just runs on top of react-native-tcp-socket.
The only tricky part is that we need to be able to require Node libraries and assets into ClojureScript during development. This is done by a compiler pass - first we start at the entry point of the ClojureScript React Native project and follow the dep graph collecting all libraries required from `node_modules`. This is dumped to a file that is required transitively by `index.js`.
Asset handling is done as a simple compiler pass over every AST node searching for JavaScript `require` statements in the ClojureScript.
The end result is that we have an extremely rapid development workflow that simply is not possible with other existing technologies - not React Native, not Flutter, not SwiftUI. All of our apps are built via live-coding from our text editor of choice + REPL (either embedded in IDE or via shell).
> which simply cannot be done with ES6 modules because imports will be captured
I'm probably misunderstanding the problem here, but perhaps it's worth noting that ES6 module exports declared with let/var can be changed from within the module and the changes reflect to importing modules, even to named imports.
Mostly a maintenance release, but the and/or compiler pass thing is somewhat neat. First, it might seem that such an optimization would not be that useful, but actually it's one of the most critical ones - we want to write the persistent data structure code in a language idiomatic way yet know that performance sensitive JavaScript code will be generated.
Here's the problem.
In Clojure `and` and `or` are just macros that expand to `let` + `if`, simple and elegant:
... except when you target JavaScript - which is not an expression oriented language. So `let` will be wrapped in an immediately invoked function expression (IIFE). Google Closure does elide these when it can, but it will give up after only a few levels of nesting. For an IIFE to appear in the middle of a conditional is a performance killer.
Another complication is that it's not safe to just use `&&` and `||` blindly because of `0` and the empty string and the other cases which are not false-y in Clojure(Script). Checks for JavaScript false-y values are a performance killer.
So years ago we implemented a simple form of type inference which annotates the AST with type information as a compiler pass on every node when possible. As long we know the JavaScript `if` will receive a boolean value we can elide the JS false-y value check.
But this is also exactly the information we need to safely run a following pass that looks for the above and/or syntactical patterns and optimize it. As long as each nested `if` is guaranteed to return a boolean, we can remove the local and use `&&` or `||` instead.
The and/or optimization pass ended up being ~120 lines of code with no actual dependencies on anything else in the ClojureScript analyzer or compiler because the ClojureScript AST is just plain EDN - https://github.com/clojure/clojurescript/blob/master/src/mai...
Happy to answer any further questions about this!
UPDATE: Also ClojureScript turned 10 years old this month :)
Honestly not sure, I'm not a Webpack expert and have no wish to be :) But again, as I've said elsewhere, there is no specific integration with Webpack. The bundle feature makes no choices so it's easier for users to fully control the integration with any JS build tool.
Thank you for your response! ClojureScript is by far the best target-to-browser solution I have used. The REPL experience is stunning, macros work as expected, and Cljc compilation is seamless. Working with it is pure joy.
Thanks again for everything you did for ClojureScript, and your talks are also really inspiring!
Normally Webpack loader transforms a single file to another file, like CoffeeScript to JavaScript, mapping one to one. So when it's processing a single file, the loader probably has no knowledge on other files. When it's importing another module, it's Webpack detecting there's a `require` or `import` and revolsing that file.
This solution is not working for ClojureScript. I thought it was thheller told me that ClojureScript has macros, it's not just transforming files one by one, which is not how I was taught to write a loader...
There might be some solution but could be tricky. At least we still need a ClojureScript provided by JVM, which is already a lot more sophistached than a normal Webpack loader.
Yep. But having this feature only in shadow-cljs doesn't do much for the ecosystem as a whole since non-portable. With this change, many tools, cljsbuild, Figwheel, etc. have the same capabilities. It also opens the door for creating ClojureScript libraries that depend on node_modules and these can again be consumed with any tool.
Thank you so much for all of your work on ClojureScript, @swannodette!
I love the work that's been done on shadow-cljs (and the maintainer is amazing) -- I've had a vastly easier and more stable time than with JS bundlers.
That said, in building a business on top of Clojure(Script), it's incredibly reassuring to know that shadow isn't a single point of failure.
This feature will make integrating/selling ClojureScript into existing projects easier too -- I only wish that I'd had this when moving from company to company a few years ago -- I'd have brought ClojureScript to a number of places. :)
@swannodette
In the library case does the cljs built library need a clojure runtime bundled? Or does banning eval and running it through Closure allow for only the bare minimum to be pulled through?
1. NPM is analogous to Maven for Java. Yes the ClojureScript bundler target lets you use anything you've installed from NPM into your project's `node_modules` directory. Not many limitations because we wash our hands of dealing with this stuff - use whatever popular bundler solution you prefer.
2. Not for creating NPM packages, just consuming them. Webpack lets you use dependencies from NPM and package them up into a single JS (or code-split) asset to be used on a webpage. Note, we have no integration to any particular bundler. You could just as easily run Metro for React Native on the output and build a mobile app.
When we eventually remove the direct GCL dependency it will sadly be more for optics than anything else.