This post describes a simple pattern for arranging ClojureScript namespaces, especially useful for those exposing macros delegating to runtime functionality. Let’s say we want a macro that will do a reverse lookup in a map. For example, the following would yield 2 : reverse-lookup zipmap 1 2 3 :a :b :c :b Furthermore, if the second argument evaluates to nil we will avoid evaluating the map…
When optimizing code, measurement is critical. This post details a technique I've found useful when optimizing ClojureScript code. Unfortunately, ClojureScript doesn't have a nice tool like Criterium . But, it does have facilities to fundamentally time how long code takes to execute. The easiest to use is the time macro. (This also exists in Clojure, by the way.) It is really easy to use directly…
When optimizing performance-critical ClojureScript code, function call overhead might be important to you. By default, when compiling ClojureScript in :advanced mode, the :static-fns compiler option is enabled. This post describes an additional option, :fn-invoke-direct , which is an extension to :static-fns for higher-order functions. As background, it might be worth reviewing this post , which…
Frequently, when writing ClojureScript that interoperates with JavaScript APIs that accept or return JavaScript objects, it is tempting to reach for clj->js or js->clj and rely on Clojure's rich standard library, operating on persistent values for your data manipulation needs. Depending on the access patterns involved, this may not be necessary. Oftentimes simpler, more efficient solutions can be…
Planck is now built using Closure Compiler optimizations. In addition, it is now trivial to apply Closure Compiler to scripts executed by Planck in order to optimize them. To date, Planck (as well as Replete ) have been using :none for compiler optimizations . This is ideal for a REPL because it avoids symbol renaming avoids DCE (so the entire runtime library remains available) But, these…
A change landed in the ClojureScript 1.9.854 release which supresses the printing of JavaScript associated with function bodies. Previously, if you printed a function, you would see the associated JavaScript implementation. Here is an example: cljs.user=> fn inc 3 #object ret 6185 auto "function { return 3 + 1 ; }" Now, in ClojureScript 1.9.854, the default is to print a much more compact…
A change landed in the ClojureScript 1.9.854 release which improves JavaScript object literal printing, using string keys when property names cannot be represented as keywords. ClojureScript supports #js for object and array literals. For example, the JavaScript object { foo: 1, bar: "a" } can be represented in ClojureScript as #js {:foo 1, :bar "a"} where the property names are represented as…
Exciting changes have landed in ClojureScript master and an unexpected side-benefit of ClojureScript is not an Island: Integrating Node Modules is improved interop between ClojureScript and React Native. Credit to António Monteiro for much of the hard work behind this feature, and discovering its applicability to React Native! Let's take a look at how we can take advantage of this new capability…
The ClojureScript 1.9.655 release introduced :const Var inlining. This is an exciting new feature—upgrade your compiler and give it a spin! This post will provide some detail on this new feature, covering some of its more subtler aspects. Review and Definition Previously, adding :const metadata to a Var, as in def :const foo 3 would have two effects in ClojureScript: It prevents re-definition…
An update in the ClojureScript 1.9.655 compiler release eliminates a difference with respect to Clojure for code making a recur to a defrecord method head. Hopefully this post is useful in the event that you encounter this situation! Let's look at some example code to make things concrete. Let's say you have a protocol: defprotocol ISearch search this coll And, let's say you are making an instance…
A release of test.check is now available which supports self-hosted ClojureScript! All you need to obtain is version 0.10.0-alpha1 . Here is the test.check ClojureScript example working in Planck (the same works in Lumo): $ planck -qc test.check-0.10.0-alpha1.jar cljs.user=> ns cljs.user # => :require clojure.test.check :as tc # => clojure.test.check.generators :as gen # =>…
In his talk entitled The Next Five Years of ClojureScript , David Nolen spoke about the general maturity of ClojureScript. Given this, if you were to choose anything to focus on, one appropriate prioritization would be in the area of compiler performance. To me, this is just icing on the cake! If you watch this bit , David predicts there is at least a 2× perf gain to be had. Well… I think we've…
An update to the ClojureScript REPL has arrived, supporting multiple forms on a line. Previously, only the first readable form would be evaluated: cljs.user=> + 1 2 str "a" "b" :hi 3 In the Clojure REPL, each is evaluated in sequence: user=> + 1 2 str "a" "b" :hi 3 "ab" :hi The difference has been addressed with the latest ClojureScript release, 1.9.542. This fixes an issue where text after the…
Ejecta is a fast browserless implementation of HTML canvas for iOS and tvOS. It's very easy to set things up so that you can drive Ejecta using ClojureScript. First, download Ejecta , which ships as an Xcode project. Also, ensure that you have CocoaPods installed. We'll make a few minor modifications to the Ejecta project which will allow us to establish a ClojureScript REPL into iOS and drive…
For the past 1½ years, we've been able to use brew to install Planck on macOS. I love this low-friction way of installing software and keeping it up to date. Well, now you can do the same using apt-get on Ubuntu! Image composition: Caitlin Fikes To do this, you first need to set up the ppa:mfikes/planck PPA , update , and you are good to go: $ sudo add-apt-repository ppa:mfikes/planck $…
Planck now supports Linux! This capability owes its roots to Lucas Stadler who a started things off with a truly massive contribution which converted the bulk of the Objective C in Planck to portable C. With this effort, Lucas essentially got Planck running for the first time on Linux. Incredible! Others who have helped with contributions to Planck 2 include Jonathan Leonard , Belaf , Ivan…
Planck 2 is currently in beta, and is closing in on a release. The main feature in Planck 2 will be support for Linux and there will be a future blog post about that aspect. In the meanwhile, I wanted to describe a few of the new features in Planck 2 relative to Planck 1.17. Async Shell Thanks to contributions by Jonathan Leonard , the planck.shell namespace gains the ability to execute…
Update March 21, 2018 : Content from this post has been incorporated into the ClojureScript site Guide: ns Forms . Some namespaces—like clojure.string and clojure.set —are available for use in ClojureScript, even though the first segment in those namespaces is clojure . But then others—like cljs.pprint , cljs.test , and now cljs.spec —live under cljs . Why the difference? Ideally, there'd be none.…
The new clojure.spec capability was released as cljs.spec in ClojureScript 1.9.14 . In particular, the use of cljs.spec with bootstrap ClojureScript was supported on day one. It ships with Planck 1.14 and, via the App Store, with Replete 1.6 . The cljs.spec namespace should work with any self-hosted environment. Porting When spec was announced on May 23rd, my immediate concern was whether it would…
Once exciting new feature in Planck 1.12 is support for HTTP. Planck has long had support for interacting with the local file system via planck.io and also interacting with local programs via planck.shell . But now there is a new namespace, planck.http , thanks to a generous contribution by Erik Assum . Here is an example of it in action: cljs.user=> require ' planck.http :refer get nil…
A new feature in Planck 1.12 is the use of Fipp to format REPL results. To be honest, I stole this idea from Cursive . And it's definitely worth stealing! In short, what this gives you is formatted output for structured data. You always have cljs.pprint available. But for me, the main selling point is the "F" in Fipp's name. It is freakishly fast . Thanks to Brandon Bloom for this awesome library.…
It seems that, for the longest time , there has been interest in core.async support in self-hosted ClojureScript. And, try as we might, for one reason or another, it has eluded us. Certainly, the most complex macro with the shortest name I've ever encountered is go . Now, more than half a year later, it's finally working! Here's a small demo, using it from Planck: cljs.user=> require '…
In Clojure, you can disable asserts by setting the assert var to false . Once set, this affects any newly compiled code which uses the assert macro. For example, normally macroexpand ' assert = 1 2 "hi" yields (reformatted for whitespace): if = 1 2 nil do throw new java.lang.AssertionError clojure.core/str "Assert failed: " "hi" "\n" clojure.core/pr-str quote = 1 2 But if you first set assert to…
This is a short technical note on a new capability that landed in ClojureScript. Currently in ClojureScript, if you use syntax-quote on a symbol that is qualified with an alias, resolution does not occur: cljs.user=> require ' clojure.string :as string nil cljs.user=> `string/reverse string/reverse But, with the change for CLJS-1612 , this properly reflects the behavior you'd see in Clojure:…
About nine months ago, bootstrap ClojureScript was born, with David Nolen's popular and apt characterization : ClojureScript can compile itself. Now, this being a new capability, it started off with its own share of bugs that needed to be eradicated. But—surprising to me at least—the known bug count was brought to zero within a time span of only half a year or so. Now, we are at the point where…
The general advice is to leave :static-fns turned off for development with the ClojureScript REPL. If you are curious and turn it on, most things will work anyway. This post gives a concrete example illustrating what can go wrong if you do turn it on. Let's say you have a multi-arity function f defn f a prn a a b prn a b and you define another function g that calls it: defn g f 1 2 If you try g at…
The ClojureScript compiler propagates type hints. This means that type information can flow and be used in locations downstream from where it is originally hinted or inferred. This is a very good thing , but there may be some rare cases where you'd like to stop the compiler from doing this. Here is an interesting bit of code to consider: defn f boolean b loop x b if x recur 0 :done What do you…
In a previous post , I had written about a consequence of the ClojureScript macro compilation model, and how it requires a tower of macro namespaces when self-hosted. Well, in the case that a macro expands into a macro call, the staging rules are satisfied and no tower is needed. Here's more detail on the subject. The macros in the previous post satisfy this constraint and the bar.core namespace…
Update March 21, 2018 : Content from this post has been incorporated into the ClojureScript site Guide: ns Forms . In ClojureScript, macros are handled a bit differently than in Clojure. In particular, the ns form supports :require-macros , along with some simplifying sugar and implicit loading behavior that this post will cover. Primitives For the sake of an example, let's assume that we have the…
The cljs.test namespace has been ported for use in Planck. In short, this means you can require this namespace and make use of it, just like you can do in regular JVM ClojureScript. What's the big deal? Why did it need to be ported ? A lot of cljs.test is macros, like deftest and is , and one key aspect is that these macros call other macros in the cljs.test namespace. While this is cool in…
The cljs.test library provides a mechanism for writing custom asserts that can be used with the is macro—in the form of an assert-expr defmulti . In JVM ClojureScript, any assert-expr defmethod must be written in Clojure, and you must arrange to have this Clojure code executed when running tests. This is a fallout of the fact that is is a macro written in Clojure. But, in the bootstrapped…
Improvements to ClojureScript are very much community driven . One easy way to help is to try out proposed ClojureScript patches. By doing this, you provide needed coverage relative to diverse environments and codebases. This produces immense value—relative to the effort required—and greatly reduces the burden in ascertaining whether a given patch is valid, won't cause regressions, etc. How easy…
A recent change to Planck has landed making use of Parinfer to improve the form entry experience, especially when entering multi-line forms. When you enter a multi-line form into Planck, it currently doesn't automatically provide indentation for newly entered lines. For example, say you entered the form map inc 1 + 2 3 4 , but where each number is entered on its own line. What you get (unless you…
This is a fun one: Planck now has colors! Here's what it looks like: I got the idea after having tried out the Boot REPL. In fact, the underlying Planck implementation is based on the same ANSI utilities in Pretty that Boot uses, but ported for use in the self-hosted ClojureScript environment of Planck. This ends up having evaluation results in blue, compiler warnings and other err output in red,…
Planck ships with a few macro namespaces which are now AOT compiled using Planck. A little background: Planck ships with its requisite namespaces bundled in its binary. For minimizing loading latency, most of this code is AOT compiled down to JavaScript. But, this has not been the case for the macro namespaces that ship with Planck. One example of such a namespace is the planck.core macro…
If you look closely at the Clojure logo, you'll notice that it employs symbology—popularized by SICP—reflecting something that doesn't exist in ClojureScript. Since bootstrapped ClojureScript provides a cljs.js/eval capability—making this trivial—I thought I'd go ahead and land support for eval in Planck that acts like clojure.core/eval . To use it, you simply need to refer it. require '…
Clojure and ClojureScript handle the optimization of self calls slightly differently and this post explores consquences of the difference. I don't claim to fully appreciate the underlying tradeoffs, but hopefully this exposition gets you thinking. Let's say you would like to optimize this definition of fib : defn fib n if < n 2 n + fib dec n fib dec dec n Currently, it calculates fib 41 in 5.6…
Sometimes you need to perform a side effect for each element in a sequence. For the sake of argument, let's say we want to prn each of the items in 0 1 2 3 4 . The existing core function doseq can be used to accomplish this: doseq x 0 1 2 3 4 prn x Since doseq is modeled after for , lots of sophisticated things can be done using its mini-language, with :let , :while , and :when . But, for applying…
When working with lazy sequences in Clojure, one bit of advice for avoiding excessive memory use is to avoid unintentionally holding onto the head of a lazy sequence. Let's mess around with this idea in ClojureScript. In particular, let's work with a gigantic sequence of numbers: repeatedly 100000000 rand . This sequence is lazy, and just calling repeatedly will not cause it to be generated. With…
Let's say you are writing a complex macro and decide to delegate some of its implementation to a function. When in bootstrapped ClojureScript, this can be fairly clean to do. But first, let's back up and review how this can be done in regular ClojureScript—where macros are written in Clojure. Regular ClojureScript Consider src/foo/macros.clj , with ns foo.macros defn add a b + a b defmacro add-now…
One of the goals of Planck has been to minimize startup latency. It's pretty fast by making use of bootstrapped ClojureScript in JavaScriptCore . It also supports caching compiled namespaces and static function dispatch . But, a significant portion of Planck initialization is time spent reading in the analysis cache for the ClojureScript runtime. What is the analysis cache? It is essentially…
Support for using realized? with lazy sequences has landed in ClojureScript. What kind of new fun can we have with this capability? Fun First, let's say we have a lazy sequence. Here's one representing the factors for each natural number: def factors map fn n filter fn x zero? rem n x range 1 inc n rest range This sequence looks like 1 1 2 1 3 1 2 4 1 5 ... Even though the def defines an infinite…
Previously I had written about some experimental hacking I had done in Ambly to make it so that foreign libs processing could occur when launching the Ambly REPL. Now, via recently landed CLJS-1313 , this capability will be available for all ClojureScript REPLs. Here is a detailed example illustrating the capability. Let's say you have a libs dir containing a couple of CommonJS modules: german.js…
In ClojureScript, unlike in Clojure, you can't intermix macros and their use in the same compilation stage . In pragmatic terms, this often means you end up defining macros in files that are separate from the files that consume them. There are two interesting corner cases that this post explores. I'll call them the tower and the loop . Tower Update April 15, 2016 —See Collapsing Macro Tower . In…
With some minor tweaks, Ethan Sherbondy has sorted out how to get a ClojureScript environment running on the Apple TV with a non-trivial app! Ethan put together a prototype game that makes use of the Apple TV remote for input and Ejecta for rendering. (Previously, Ambly was revised to help when using it with libraries like Ejecta.) What's cool is that all of the normal ClojureScript dev tooling…
In ClojureScript—unlike Clojure—a macro and a function can have the same name. Here is an example. Lets say you have src/foo/core.clj : ns foo.core defmacro add a b ` + a b and src/foo/core.cljs : ns foo.core defn add a b + a b 0.001 In the function definition above, I added an additional 0.001 to make it easier to see whether the macro or function is being invoked in different contexts. Let's try…
In ClojureScript, you can annotate a function's parameters or return type using boolean . This post describes the what and why. First off, its worth saying that you normally don’t need to do any of this . But, if you are working with some performance-critical code, understanding what's going on may help. Let’s look at what gets generated for this form: defn foo x if x 1 2 To easily see what is…
Let's say you have some namespaces that you would like to have automatically required and available when you launch your ClojureScript REPL. Perhaps these are utility namespaces that you tend to use only at the REPL, or maybe you'd just like to automatically require your app's main namespace. Support for this exists in ClojureScript REPLs via an option named :repl-requires . For our example, let's…
Planck now has support for script compilation caching. Planck executes scripts by compiling the ClojureScript to JavaScript for execution in JavaScriptCore. This is done dynamically and is usually very fast. But, if you have scripts that don't change frequently, and they're expensive to compile, it may make sense to save the resulting JavaScript so that subsequent script execution can bypass…
Replete makes for a convenient ClojureScript REPL that you can carry around with you in your iOS device. But, man is it hard to enter forms on the iOS keyboard. We've been considering extending the keyboard to add an extra row with parentheses keys, brackets and other frequently used characters, but that hasn't happened yet. In the meanwhile, Shaun LeBron has come out with Parinfer . My simplified…