Jekyll2024-01-02T20:30:58+00:00/feed.xml(:dev/notes vlaaad)First impressions of Morse and Replicant2023-04-30T00:00:00+00:002023-04-30T00:00:00+00:00/replicant<h1 id="release-of-morse">Release of Morse</h1>
<p>Awhile ago, the Clojure team <a href="https://clojure.org/news/2023/04/28/introducing-morse">announced</a> a new data inspection tool called Morse. Unfortunately, it was not clear what the tool is exactly, since the announcement post didn’t include any screenshots, only saying that it’s an evolution from REBL. Upon closer inspection it turned out that Morse <em>is</em> REBL, but rebranded and open sourced. I’m very grateful for all the hard and thoughtful work that Clojure team does to improve the ecosystem of my favorite programming language! I also hope they would spend a bit more time on communicating the work they do… Anyway, here is what Morse looks like:</p>
<p><img src="/assets/2023-04-30/morse.png" alt="" /></p>
<p>As you might know, I made <a href="/reveal/">Reveal</a> — a tool similar to Morse/REBL that aims to help with data inspection. One thing that made me particularly curious about the Morse announcement is a mention of new Replicant libraries designed to help with remote data inspection.</p>
<h1 id="replicant">Replicant</h1>
<p>Replicant comes in 2 parts: <a href="https://github.com/clojure/data.alpha.replicant-client">client</a> and <a href="https://github.com/clojure/data.alpha.replicant-server">server</a>. The idea is that you run replicant server in the process you want to inspect, and use replicant client in a tool like Morse or Reveal to interact with the server. Together, they allow inspecting remote objects as if they are local. The server library is JVM-only, but in principle there is a protocol on top of eval and edn that can be implemented in another Clojure dialect.</p>
<p>Replicant server is a prepl that “remotifes” objects when responding. For example, if I request <code class="language-plaintext highlighter-rouge">*ns*</code>, it will respond with a following EDN:</p>
<div class="language-clj highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="o">#</span><span class="n">r/object</span><span class="w"> </span><span class="p">{</span><span class="no">:klass</span><span class="w"> </span><span class="n">clojure.lang.Namespace,</span><span class="w">
</span><span class="no">:ref</span><span class="w"> </span><span class="o">#</span><span class="n">r/id</span><span class="w"> </span><span class="o">#</span><span class="n">uuid</span><span class="w"> </span><span class="s">"fd88d9ab-42ce-492a-a6ee-3b3ae2c1e152"</span><span class="p">}</span><span class="w">
</span></code></pre></div></div>
<p>Replicant client provides a set of data readers for tagged literals like <code class="language-plaintext highlighter-rouge">r/object</code>, <code class="language-plaintext highlighter-rouge">r/id</code> and others. The idea is that your prepl client uses replicant readers to construct remote objects — objects that ask the server on interactions in the client process. <a href="https://github.com/nubank/morse/blob/330345b9a06abe01bcbb1b6a54cee3f4ee7f891d/src/dev/nu/morse/ui.clj#L543-L565">Here is</a> the code that implements it in Morse.</p>
<h1 id="first-impressions-with-replicant-and-morse">First impressions with Replicant and Morse</h1>
<p>I tried Morse in a remote mode, and unfortunately it didn’t work due to a minor bug (<a href="https://github.com/nubank/morse/issues/2">reported here</a>). After I fixed the bug in a locally checked out verion of Morse repo, it started to work. When I evaluated with <code class="language-plaintext highlighter-rouge">*ns*</code>, it responded with a map that looked like this:</p>
<div class="language-clj highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">{</span><span class="no">:klass</span><span class="w"> </span><span class="n">clojure.lang.Namespace</span><span class="w">
</span><span class="no">:ref</span><span class="w"> </span><span class="n">user</span><span class="p">}</span><span class="w">
</span></code></pre></div></div>
<p>Here, <code class="language-plaintext highlighter-rouge">clojure.lang.Namespace</code> is a symbol, but <code class="language-plaintext highlighter-rouge">user</code> is deserialized as a “Relay” in replicant terms — a custom type that holds a reference to replicant client and a reference id. When Morse asks for <code class="language-plaintext highlighter-rouge">toString</code> of Relay, it performs a network request and fetches a string — <code class="language-plaintext highlighter-rouge">"user"</code> — for the id.</p>
<p>I also <a href="https://github.com/clojure/data.alpha.replicant-server/issues/1">reported an issue</a> where evaluating a map literal like <code class="language-plaintext highlighter-rouge">{:a 1}</code> serialized it as a <code class="language-plaintext highlighter-rouge">r/fn</code> (remote fn) instead of <code class="language-plaintext highlighter-rouge">r/map</code> (remote map), so it wasn’t possible to inspect maps at all — remote fns don’t even fetch <code class="language-plaintext highlighter-rouge">toString</code>s… I’m not sure if I’m doing something wrong here, but I launched the server as described in the docs:</p>
<div class="language-sh highlighter-rouge"><div class="highlight"><pre class="highlight"><code>clj <span class="se">\</span>
<span class="nt">-Sdeps</span> <span class="s1">'{:deps {io.github.clojure/data.alpha.replicant-server {:git/tag "v2023.04.25.01" :git/sha "039bea0"}}}'</span> <span class="se">\</span>
<span class="nt">-X</span> clojure.data.alpha.replicant.server.prepl/start :host <span class="s1">'"localhost"'</span> :port 7272
<span class="c"># Replicant server listening on 7272 ...</span>
</code></pre></div></div>
<p>And then:</p>
<div class="language-sh highlighter-rouge"><div class="highlight"><pre class="highlight"><code>nc localhost 7272
<span class="o">{}</span>
<span class="c"># out => {:tag :ret, :val "#r/fn {:id #uuid \"ac946192-666e-4da8-989c-395e9b10115f\"}", :ns "user", :ms 1, :form "{}"}</span>
</code></pre></div></div>
<h1 id="integrating-replicant-into-reveal">Integrating Replicant into Reveal</h1>
<p>I prototyped Replicant integration for Reveal. One roadblock I hit was that replicant client is distributed as a git dep only, while Reveal is distributed as a Maven dependency. This means I can’t release a version of Reveal that depends on Replicant. I reported the issue <a href="https://github.com/clojure/data.alpha.replicant-client/issues/1">here</a>. It didn’t stop me from prototyping the integration though. The main issue when implementing a replicant+reveal-flavored prepl was mixing user-submitted forms to <code class="language-plaintext highlighter-rouge">*in*</code> with replicant forms that load more data for remote objects. The problem here is that we can evaluate a form like <code class="language-plaintext highlighter-rouge">(read-line)</code>, and after input an unstructured text until the next newline. I ignored the problem for now and just read from <code class="language-plaintext highlighter-rouge">*in*</code> form by form and interleaved these forms with replicant forms. Now that I’m writing this blog post I realized that what I actually need is 2 connections to the replicant server — one for <code class="language-plaintext highlighter-rouge">*in*</code> that has to be piped to the server as is and another for replicant forms.</p>
<p>I also noticed that current implementation of the Replicant client issues a synchonous network request every time the <code class="language-plaintext highlighter-rouge">toString</code> on a remote object is called, which is, I think, unfortunate, but can be improved, but also maybe it’s intentional and completely fine, and Reveal should be more careful about calling <code class="language-plaintext highlighter-rouge">toString</code> on objects it inspects. When I received <code class="language-plaintext highlighter-rouge">RemoteFn</code> instance after evaluating <code class="language-plaintext highlighter-rouge">{:a 1}</code>, I could use it as a function, so invoking <code class="language-plaintext highlighter-rouge">(the-remote-fn :a)</code> resulted in <code class="language-plaintext highlighter-rouge">1</code> being loaded, which was pretty neat!</p>
<h1 id="conclusion">Conclusion</h1>
<p>Even though I can’t release any Replicant integration as of now, I’m looking forward to the evolution of Replicant libraries. I like Replicant because many Clojure data inspection tools can benefit from it, which helps the whole ecosystem. I’ve spent some time thinking about the problem space of inspecting the data from the remote process, and I’m happy to see there is some work in this area!</p>Release of MorseReveal now highlights illegal booleans in Clojure2023-02-10T00:00:00+00:002023-02-10T00:00:00+00:00/illegal-booleans<p>I made <a href="/reveal/">Reveal</a> — Read Eval Visualize Loop for Clojure — a set of visual tools aiming to improve the development experience by providing better access to the data in your JVM. One of the core ideas in Reveal is using value-aware syntax-highlighting to display the output to the user, so you can immediately see a difference between e.g. a symbol <code class="language-plaintext highlighter-rouge">clojure.lang.Ratio</code> and a class <code class="language-plaintext highlighter-rouge">clojure.lang.Ratio</code>. The newest version of Reveal (Free <code class="language-plaintext highlighter-rouge">1.3.280</code>, Pro <code class="language-plaintext highlighter-rouge">1.3.359</code>) introduces a new highlight.</p>
<h2 id="what-are-illegal-booleans">What are illegal booleans</h2>
<p>An illegal boolean is an instance of Boolean that is neither <code class="language-plaintext highlighter-rouge">Boolean/TRUE</code> nor <code class="language-plaintext highlighter-rouge">Boolean/FALSE</code>, e.g. a manually created instance of a Boolean like <code class="language-plaintext highlighter-rouge">(Boolean. true)</code>. They are problematic in Clojure because of its implementation of truthiness — while the semantics define truthiness as “everything is truthy except nil and false”, it is implemented as “everything is truthy except nil and <code class="language-plaintext highlighter-rouge">Boolean/FALSE</code>”. This means <code class="language-plaintext highlighter-rouge">(Boolean. false)</code> is truthy in Clojure. It’s bad. It’s a great time sink that can drive you wild if you don’t know about it and you see a <code class="language-plaintext highlighter-rouge">false</code> acting as <code class="language-plaintext highlighter-rouge">true</code> in your code.</p>
<p>And what’s worse, you can get those pesky little booleanses accidentally.</p>
<h2 id="when-can-you-get-illegal-booleans">When can you get illegal booleans</h2>
<p>Of course, no one ever writes <code class="language-plaintext highlighter-rouge">(Boolean. false)</code>, but new boolean instances will be created if you use reflection:</p>
<div class="language-clj highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">(</span><span class="k">let</span><span class="w"> </span><span class="p">[</span><span class="n">eq-meth</span><span class="w"> </span><span class="p">(</span><span class="nf">.getDeclaredMethod</span><span class="w"> </span><span class="n">Object</span><span class="w"> </span><span class="s">"equals"</span><span class="w"> </span><span class="p">(</span><span class="nb">into-array</span><span class="w"> </span><span class="n">Class</span><span class="w"> </span><span class="p">[</span><span class="n">Object</span><span class="p">]))]</span><span class="w">
</span><span class="p">(</span><span class="k">def</span><span class="w"> </span><span class="n">eq?</span><span class="w"> </span><span class="o">#</span><span class="p">(</span><span class="nf">.invoke</span><span class="w"> </span><span class="n">eq-meth</span><span class="w"> </span><span class="n">%1</span><span class="w"> </span><span class="p">(</span><span class="nb">into-array</span><span class="w"> </span><span class="n">Object</span><span class="w"> </span><span class="p">[</span><span class="n">%2</span><span class="p">]))))</span><span class="w">
</span><span class="p">(</span><span class="nf">eq?</span><span class="w"> </span><span class="mi">1</span><span class="w"> </span><span class="mi">2</span><span class="p">)</span><span class="w">
</span><span class="n">=></span><span class="w"> </span><span class="n">false</span><span class="w"> </span><span class="c1">;; So far so good...</span><span class="w">
</span><span class="p">(</span><span class="k">if</span><span class="w"> </span><span class="p">(</span><span class="nf">eq?</span><span class="w"> </span><span class="mi">1</span><span class="w"> </span><span class="mi">2</span><span class="p">)</span><span class="w"> </span><span class="no">:equal</span><span class="w"> </span><span class="no">:not-equal</span><span class="p">)</span><span class="w">
</span><span class="n">=></span><span class="w"> </span><span class="no">:equal</span><span class="w"> </span><span class="c1">;; What the fuuuu....</span><span class="w">
</span></code></pre></div></div>
<p>…Yep. The Boolean constructor is deprecated since Java 9, but it’s left for compatibility and JVM will create new booleans in cases such as this.</p>
<h2 id="how-reveal-helps-you-spot-the-problem">How Reveal helps you spot the problem</h2>
<p>In the newest release of Reveal, you will now be able to spot the problem earlier:</p>
<p><img src="/assets/2022-02-10/img.png" alt="img" /></p>
<p>Neat, isn’t it?</p>
<h2 id="how-to-fix-the-problem">How to fix the problem</h2>
<p>If you find illegal booleans in your code, you should find the place where they originate from and wrap it with <code class="language-plaintext highlighter-rouge">boolean</code> call like so:</p>
<div class="language-clj highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">(</span><span class="k">let</span><span class="w"> </span><span class="p">[</span><span class="n">eq-meth</span><span class="w"> </span><span class="p">(</span><span class="nf">.getDeclaredMethod</span><span class="w"> </span><span class="n">Object</span><span class="w"> </span><span class="s">"equals"</span><span class="w"> </span><span class="p">(</span><span class="nb">into-array</span><span class="w"> </span><span class="n">Class</span><span class="w"> </span><span class="p">[</span><span class="n">Object</span><span class="p">]))]</span><span class="w">
</span><span class="p">(</span><span class="k">def</span><span class="w"> </span><span class="n">eq?</span><span class="w"> </span><span class="o">#</span><span class="p">(</span><span class="nb">boolean</span><span class="w"> </span><span class="p">(</span><span class="nf">.invoke</span><span class="w"> </span><span class="n">eq-meth</span><span class="w"> </span><span class="n">%1</span><span class="w"> </span><span class="p">(</span><span class="nb">into-array</span><span class="w"> </span><span class="n">Object</span><span class="w"> </span><span class="p">[</span><span class="n">%2</span><span class="p">])))))</span><span class="w">
</span><span class="p">(</span><span class="nf">eq?</span><span class="w"> </span><span class="mi">1</span><span class="w"> </span><span class="mi">2</span><span class="p">)</span><span class="w">
</span><span class="n">=></span><span class="w"> </span><span class="n">false</span><span class="w"> </span><span class="c1">;; So far so good...</span><span class="w">
</span><span class="p">(</span><span class="k">if</span><span class="w"> </span><span class="p">(</span><span class="nf">eq?</span><span class="w"> </span><span class="mi">1</span><span class="w"> </span><span class="mi">2</span><span class="p">)</span><span class="w"> </span><span class="no">:equal</span><span class="w"> </span><span class="no">:not-equal</span><span class="p">)</span><span class="w">
</span><span class="n">=></span><span class="w"> </span><span class="no">:not-equal</span><span class="w"> </span><span class="c1">;; Whew!</span><span class="w">
</span></code></pre></div></div>
<p>Hope this helps!</p>I made Reveal — Read Eval Visualize Loop for Clojure — a set of visual tools aiming to improve the development experience by providing better access to the data in your JVM. One of the core ideas in Reveal is using value-aware syntax-highlighting to display the output to the user, so you can immediately see a difference between e.g. a symbol clojure.lang.Ratio and a class clojure.lang.Ratio. The newest version of Reveal (Free 1.3.280, Pro 1.3.359) introduces a new highlight.Reveal, Vega and data DSLs2022-01-29T00:00:00+00:002022-01-29T00:00:00+00:00/reveal-vega-and-data-dsls<p>I made <a href="/reveal/">Reveal</a> — Read Eval Visualize Loop for Clojure — a set of visual tools aiming to improve the development experience by providing better access to the data in your JVM.</p>
<h1 id="vega-view">Vega view</h1>
<p><a href="https://vega.github.io/">Vega(-Lite)</a> is a data visualization library that uses declarative syntax (JSON) to describe charts. Its data-driven API is a good match for Clojure, where everything revolves around simple data structures. While it was <a href="/vega-in-reveal">always possible</a> to view Vega(-Lite) visualizations in Reveal, it required some setup, and I’m pleased to announce that the newest Reveal release — <code class="language-plaintext highlighter-rouge">1.3.265</code> — now bundles a proper Vega viewer!</p>
<p>In addition to displaying Vega visualizations, new <a href="https://github.com/vlaaad/reveal/blob/aefa8e921175a06e0ee330bed090bcf7950cafc8/src/vlaaad/reveal.clj#L640">vega-view</a> includes:</p>
<ul>
<li>support for separating data from vega spec that results in a faster update performance for streaming visualizations;</li>
<li>styling that matches Reveal’s look-and-feel;</li>
<li>visualization auto-sizing by default;</li>
<li>2-way signal binding: both providing signals to the visualization and reacting to signal changes in the JVM;</li>
<li>support for <a href="https://github.com/vega/vega-embed">vega-embed</a> options.</li>
</ul>
<p>Here is what it looks like:</p>
<video controls=""><source src="/assets/reveal/vega-view.mp4" type="video/mp4" /></source></video>
<h1 id="on-data-dsls">On data DSLs</h1>
<p>Now that we saw some Vega in action, I’d like to discuss something bothering me about data DSLs. Let me start with what I mean by data DSL in Clojure — a pattern of using data structures in a bespoke format to drive some behavior. It’s a common and popular way to program in Clojure, and some examples of this approach include:</p>
<ul>
<li><a href="https://github.com/cljfx/css">cljfx/css</a> to define CSS;</li>
<li><a href="https://github.com/weavejester/hiccup">weavejester/hiccup</a> to define HTML;</li>
<li><a href="https://github.com/noprompt/garden">noprompt/garden</a> to define CSS;</li>
<li><a href="https://github.com/metosin/reitit">metosin/reitit</a> to define routing;</li>
<li><a href="https://github.com/seancorfield/honeysql/">seancorfield/honeysql</a> to build SQL queries;</li>
<li>Vega(-Lite) wrappers like Reveal’s vega view to define visualizations;</li>
<li><a href="https://github.com/cljfx/cljfx">cljfx/cljfx</a> to define dynamic JavaFX UI.</li>
</ul>
<p>Data structures are a fantastic building material — easily serialized and extensively supported by Clojure standard library — so data DSLs are rightfully widespread in the Clojure ecosystem. Isn’t it amazing to write something small and simple that defines a non-trivial visualization?</p>
<div class="language-clj highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">{</span><span class="no">:mark</span><span class="w"> </span><span class="s">"line"</span><span class="w">
</span><span class="no">:encoding</span><span class="w"> </span><span class="p">{</span><span class="no">:x</span><span class="w"> </span><span class="p">{</span><span class="no">:field</span><span class="w"> </span><span class="s">"date"</span><span class="w">
</span><span class="no">:type</span><span class="w"> </span><span class="s">"temporal"</span><span class="p">}</span><span class="w">
</span><span class="no">:y</span><span class="w"> </span><span class="p">{</span><span class="no">:field</span><span class="w"> </span><span class="s">"price"</span><span class="w">
</span><span class="no">:type</span><span class="w"> </span><span class="s">"quantitative"</span><span class="w">
</span><span class="no">:scale</span><span class="w"> </span><span class="p">{</span><span class="no">:type</span><span class="w"> </span><span class="s">"log"</span><span class="p">}}</span><span class="w">
</span><span class="no">:color</span><span class="w"> </span><span class="p">{</span><span class="no">:field</span><span class="w"> </span><span class="s">"ticker"</span><span class="p">}}}</span><span class="w">
</span></code></pre></div></div>
<p>I would say yes, but only if you know the Vega language. Not all data DSLs are created equal, and some DSLs (like Vega language) have high complexity in terms of behaviors they define and the options they support.</p>
<p>And here is the problem: data DSLs are a layer of indirection, and there is nothing my IDE can help me with when I assemble the data structures since they are context-free until executed by the engine.</p>
<h1 id="solutions">Solutions</h1>
<p>Depending on the difficulty level of data DSLs, there are different solutions for alleviating this problem:</p>
<ul>
<li>“easy” DSLs like hiccup don’t need anything since they have no learning curve;</li>
<li>“medium” DSLs rely on extensive documentation (reitit, honeysql) and helper functions (honeysql) whose purpose is to give the developer some autocomplete for building data structures;</li>
<li>“hard” DSLs like cljfx or Vega require looking at the source or searching for examples on the web.</li>
</ul>
<p>There also exist data structure specification libraries like <a href="https://clojure.org/guides/spec">Clojure Spec</a> that can be helpful in this area. They excel at validating data shapes and describing the errors, but they are not very helpful at suggesting available options when creating the data structures. Looking at specs to figure out expected data shapes is not necessarily easier than searching the documentation. While modern editors provide autocomplete support for JSON documents annotated with <a href="https://json-schema.org/">JSON Schema</a>, it is not always sufficient to get an overview of all available data shapes.</p>
<p>I’ve been thinking about this problem area for a while and eventually created <a href="/reveal-pro" class="buy-button">Reveal Pro</a> Forms: a tool that converts data structure specifications (like specs) to interactive UI elements that help with creating data structures satisfying the specifications and exploring possible data shapes.</p>
<p>I’m happy to announce that in addition to Clojure Spec, Forms in the newest release of Reveal Pro — <code class="language-plaintext highlighter-rouge">1.3.330</code> — now support JSON Schemas!</p>
<p>How is this related to Vega(-Lite)?</p>
<h1 id="vega-forms">Vega Forms</h1>
<p>Vega(-Lite) provides JSON schemas for their visualization specifications. It means it is now possible to use <a href="/reveal-pro" class="buy-button">Reveal Pro</a> for creating Vega visualizations with a UI that:</p>
<ul>
<li>allows exploring and learning available options in Vega that is more exhaustive than JSON Schema autocompletion of <a href="https://vega.github.io/editor/">Vega editor</a>;</li>
<li>creates Clojure data structures that satisfy Vega(-Lite) specification;</li>
<li>shows a live view of visualization.</li>
</ul>
<p>In addition to generic JSON Schema Forms, Reveal Pro provides Vega(-Lite) JSON schemas and their corresponding forms out of the box. Here is what it looks like in action:</p>
<video controls=""><source src="/assets/reveal-pro/vega-form-view.mp4" type="video/mp4" /></source></video>
<p>So there you have it. Reveal Free now supports Vega(-Lite) visualizations out of the box. Reveal Pro simplifies creating Vega(-Lite) visualizations with interactive forms. Give it a try and tell me what you think!</p>I made Reveal — Read Eval Visualize Loop for Clojure — a set of visual tools aiming to improve the development experience by providing better access to the data in your JVM.2021 wrapped2022-01-01T00:00:00+00:002022-01-01T00:00:00+00:00/2021<p>Here are my most memorable events and thoughts of 2021.</p>
<h1 id="gamestop">GameStop</h1>
<p>2021 started for me with buying $GME at ~$45 and watching it shoot up to $420.69 in a matter of a week. And then watching coordinated market manipulation bringing it to $40, congress hearings, endless FUD on the media, and price fluctuating around 150-250 dollars per share. I have a higher base price now after increasing my position over time, and it’s my best performing stock pick, and I’m continuing to hold. One hell of a rollercoaster though.</p>
<h1 id="investing-for-happiness">Investing for happiness</h1>
<p>One realization I had is that money is a tool for increasing happiness, not an end in itself, so I’m making sure to not put too much into savings. Having coffee to go and paying for apartment cleaning is more important because more free time and little pleasures are where the joy is. Maybe. I’m not sure. I think some suffering has to be there too to highlight the positive side…</p>
<h1 id="taking-fridays-off">Taking Fridays off</h1>
<p>Speaking of more free time. Last half a year I took Fridays off as parental leave to spend more time with my daughter. God bless Sweden for providing parental leave to fathers, and for normalizing taking time off work to have rest. Now that parental leave days are spent, I’ll continue taking Fridays for myself, so I’ll have more time for my projects. I’m also very grateful to all my sponsors — both GitHub sponsors of my FOSS and customers of Reveal Pro — for providing enough so I can reduce my working hours in favor of passion projects!</p>
<h1 id="starting-a-business">Starting a business</h1>
<p>I dreamed about it since I was a child! And now I have a business and paying customers! Nothing life-changing though. In fact, my GitHub sponsors bring me significantly more money than Reveal Pro customers. So I guess it’s a mixed bag. I didn’t know about things like addressable market before, and now I know.</p>
<p>Here is an interesting question — how many Clojure developers are there in the world? I should have tried to answer that before creating a paid version of Reveal. My estimate is 2500-3000 devs. It’s a very small number for a programming language community… I would love to be proven wrong, otherwise, I need every third Clojurist to buy Reveal Pro for me to be able to quit my day job, which I don’t see happening… Here is why I think there are only around 2750 Clojurists in the world:</p>
<ol>
<li>Clojurians Slack <a href="https://clojurians.slack.com/stats">weekly active users</a> is stuck at around 1600. It’s been around this number for years, which in itself is concerning — given the growth of software engineer numbers, community size has to be constantly growing just to stay at the same place;</li>
<li>Latest Clojurians yearly survey results show that 60% of developers talked to other Clojurians using Slack which provides an estimate of how many Clojurians use Slack.</li>
<li>60% of 2667 is 1600.</li>
</ol>
<h1 id="going-on-dates-with-my-wife">Going on dates with my wife</h1>
<p>I and my wife are Russians living in Sweden. With no other family members in Sweden, it was hard to spend time together when we have no choice but to spend our weekends and vacations with our daughter. We decided to take a day off every month and spend it together while our daughter is in preschool. I think this contributed to my happiness more than anything else this year…</p>
<h1 id="to-catch-a-thief">To catch a thief</h1>
<p>A guy we tried to sell our old MacBook to took it and ran away. I chased him and he returned the laptop when he ran himself into a dead end. I then immediately left, called the police, found him on Facebook, and asked his friends about his whereabouts and contact details. This was a very stressful experience even if there were no monetary losses. I would prefer this incident didn’t happen at all.</p>
<h1 id="finding-exercises-that-i-enjoy-doing">Finding exercises that I enjoy doing</h1>
<p>Ever since the pandemic started, I was trying various do-stuff-every-day challenges with exercises and yoga to keep myself physically healthy. I always got demotivated after some time until I tried kettlebells and dumbbells. They are fun. I also decided to exercise less frequently, and now I feel I’m actually enjoying this stuff instead of forcing myself into health.</p>
<h1 id="pen-and-paper-is-a-bicycle-for-the-mind">Pen and paper is a bicycle for the mind</h1>
<p>I find the idea that the phone is a bicycle for the mind preposterous. It’s some hard fucking drugs. Please how do I stop wasting my time on Reddit and HN? On the other hand, for me, pen and paper are the best media for thinking. This year I tried e-paper, and while it’s fine for writing, e-ink screen refresh rates make them worthless for looking stuff up. Which is important. So, for me, pen and paper still beat any computer solution for thinking.</p>
<h1 id="grammar-of-ui-interactions">Grammar of UI interactions</h1>
<p>I’ve been enjoying vega(-lite) visualizations. I like the idea of a grammar of graphics that allows expressing a lot with very few words. I hope someone will come up with something similar for interactive UIs because the complexity is huge, and react is not enough.</p>
<h1 id="tabletop-role-playing-games">Tabletop role-playing games</h1>
<p>I love me some TTRPGs. I play with friends on Discord due to geographical distribution. As much as I’m tired of online meetings, these are the best.</p>
<h1 id="two-sides-of-fatherchild-relationship">Two sides of father/child relationship</h1>
<p>My mom divorced my stepfather and I finally told him how much I fear and hate him. What I didn’t think of before the “coming out” is that I think my resentment towards him was affecting me the whole time — it’s all too easy for me to feel guilty if I’m tired to spend time with my daughter and want some time for myself. I also feel guilty if my family members are sad or mad about something, even when their feelings are not directed at me. Somehow my hate towards a father figure turned into guilt when I became a father. Hopefully, now that I’m aware of this it’ll become easier in the future. Maybe I should see a therapist.</p>
<h1 id="i-want-for-the-pandemic-to-be-over">I want for the pandemic to be over</h1>
<p>My year wasn’t “anything but normal”. I traveled for the last New Year to Göteborg and had summer vacation on Gotland. I got vaccinated and traveled to Europe — I’ve been in France and Netherlands. I’m writing this post from my mother-in-law’s apartment in Saint Petersburg. I go to bars to meet friends. I don’t get spooked by coughing people. It’s been enough time for covid to turn from “pandemic for everyone” to “pandemic for unvaccinated”. Omicron is an evolutionary step towards covid becoming flu.</p>
<p>And yet it’s not over. Sweden might be in a more comfortable situation now due to a low population density, but everyone else seems to be worse. Having to wear masks is extremely uncomfortable. Taking them off in cafes or on a plane to eat voids any usefulness. Other countries have lockdowns. Hospitals are overwhelmed with unvaccinated. Governments abusing safety concerns to increase their power over people. Please, end.</p>
<h1 id="there-is-no-summary">There is no summary</h1>
<p>Happy New Year!</p>Here are my most memorable events and thoughts of 2021.Reveal stickers2021-12-02T00:00:00+00:002021-12-02T00:00:00+00:00/reveal-stickers<p>The year is 2011. “It’s just a jar”, you say to your colleagues, adding Clojure to the classpath. They look at you with disbelief as you cackle diabolically, and open a real-time chat with Mrs. Eval, a witch of Java Vast Magicland. She gives you the power to debug and fix a running server without ever restarting it again.</p>
<p>Present times. “It’s just a jar”, you say to your colleagues, adding Reveal to the classpath. They look at you with disbelief as you cackle diabolically, and your screen opens a vortex directly to the JV Magicland. “I can finally see it with my own eyes” are the last words your colleagues hear from you as you jump into the vortex. Now you are no longer restricted to conjure JVM magic, as well as observe the spells’ effects, through a letter correspondence with Mrs. Eval.</p>
<p>Ahem.</p>
<p>I made <a href="/reveal/">Reveal</a> — Read Eval Visualize Loop for Clojure. One defining property of Reveal is that it runs in the JVM, which allows for some <a href="/reveal/#inspect-object-fields-and-properties">deep object exploration</a> capabilities and <a href="/reveal/#ref-watchers">live views</a>. This comes with a trade-off.</p>
<h1 id="the-reveal-trade-off">The Reveal trade-off</h1>
<p>The main trade-off when using Reveal is that it comes as an OS window separate from your IDE. This means that when you use it as a REPL, you have to arrange your desktop so your IDE takes roughly 3/4 of the screen, with the Reveal REPL window taking the other 1/4. You have to sacrifice some of your screen space for REPL output that you don’t even need 60% of the time — most REPL evaluation results are small and would be better off inlined in the code buffer. It sucks.</p>
<h1 id="a-new-old-hope">A new old hope</h1>
<p>When I was going through my notes about Reveal, the very first note with an idea of this project describes “a tool that pops up small popup windows (like postit notes/devcards) that are always on top, easily closable and don’t steal focus when created/shown”. This idea started Reveal, and I think there is value in this approach because it’s a non-intrusive overlay that can be easily integrated into existing IDE-based dev workflows. And now Reveal finally works this way! I’m proud to present Reveal stickers — a new approach to developing with Reveal that is released in Reveal Free v1.3.250 and <a href="/reveal-pro" class="buy-button">Reveal Pro</a> v1.3.293.</p>
<p>Reveal stickers are small always-on-top windows that you can:</p>
<ul>
<li>create with whatever view you want;</li>
<li>arrange on your screen in a place that will be remembered by the window the next time it’s shown;</li>
<li>maximize and restore to its designated place with a single keypress;</li>
<li>minimize all at once for moments when you need your full focus on IDE.</li>
</ul>
<p>This allows you to make your IDE take the whole screen while Reveal consumes as little screen space as you want, and you can more easily focus on data explorations that require more screen space. It also makes it more convenient to use Reveal without Reveal REPL or Reveal nREPL middleware, since you can create stickers in the REPL.</p>
<p>Let’s see what’s fresh out of the oven:</p>
<h1 id="built-in-stickers">Built-in stickers</h1>
<h2 id="tap-log">Tap log</h2>
<p>If you don’t use Reveal REPL or Reveal nREPL middleware, at least you should be using tap log, and you should be using <code class="language-plaintext highlighter-rouge">tap></code> instead of <code class="language-plaintext highlighter-rouge">println</code>. The difference in usefulness for debugging is immense!</p>
<p>To show a tap log, you need to invoke <code class="language-plaintext highlighter-rouge">vlaaad.reveal/tap-log</code> fn:</p>
<div class="language-clj highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">(</span><span class="nf">require</span><span class="w"> </span><span class="o">'</span><span class="p">[</span><span class="n">vlaaad.reveal</span><span class="w"> </span><span class="no">:as</span><span class="w"> </span><span class="n">r</span><span class="p">])</span><span class="w">
</span><span class="p">(</span><span class="nf">r/tap-log</span><span class="p">)</span><span class="w">
</span></code></pre></div></div>
<p>Then you can make it as small as you want and keep it around on top of your IDE. When you need to inspect some big tapped data structure, you can do so by focusing the window and pressing <kbd>F11</kbd> (<kbd>Cmd Shift M</kbd> on mac) to toggle maximized window state.</p>
<p>Here is an example showing tap log in a situation where I create an endpoint for a web server and use <code class="language-plaintext highlighter-rouge">tap></code> to inspect ring request map in search for a handler param that is supplied in a URI path:</p>
<video controls=""><source src="/assets/2021-12-02/tap-log.mp4" type="video/mp4" /></source></video>
<h2 id="inspect">Inspect</h2>
<p>Sometimes you need to inspect some value that is hard to explore as text. You can open a temporary sticker that can be closed by pressing <kbd>Escape</kbd>:</p>
<div class="language-clj highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">;; Use vlaaad.reveal/inspect fn to open inspector sticker</span><span class="w">
</span><span class="p">(</span><span class="nf">r/inspect</span><span class="w"> </span><span class="p">(</span><span class="nb">map</span><span class="w"> </span><span class="nb">bean</span><span class="w"> </span><span class="p">(</span><span class="nb">all-ns</span><span class="p">)))</span><span class="w">
</span><span class="c1">;; It might be easier to use reader tag if you </span><span class="w">
</span><span class="c1">;; don't have vlaaad.reveal ns required in current ns</span><span class="w">
</span><span class="o">#</span><span class="n">reveal/inspect</span><span class="w"> </span><span class="p">(</span><span class="nb">map</span><span class="w"> </span><span class="nb">bean</span><span class="w"> </span><span class="p">(</span><span class="nb">all-ns</span><span class="p">))</span><span class="w">
</span></code></pre></div></div>
<p>You can also open inspector popups by pressing <kbd>Shift Enter</kbd> instead of <kbd>Enter</kbd> when selecting and Reveal action to execute for some value.</p>
<video controls=""><source src="/assets/2021-12-02/inspect.mp4" type="video/mp4" /></source></video>
<h1 id="custom-stickers">Custom stickers</h1>
<p>Now, this is where things get interesting. You can use and compose various built-in views to create an overlay that matches the system you are developing. Is there some state you always have to mentally keep track of? Maybe an Integrant or Component or Mount system that is either running or not? Make it visible in a small sticker so you can always know (and control!) its current state.</p>
<p>Here is an <a href="https://github.com/vlaaad/reveal/blob/master/examples/e02_integrant_live_system_view.clj">example</a>:</p>
<video controls=""><source src="/assets/2021-12-02/sticker.mp4" type="video/mp4" /></source></video>
<p>By default, this window will remember its bounds per window title, so it’s easy to define a specialized place for a sticker just by giving it a distinct title.</p>
<h1 id="convert-reveal-repls-to-stickers">Convert Reveal REPLs to stickers</h1>
<p>In the tradition of keeping things as simple as possible, stickers are implemented as a bunch of independent options. While the default presentation for Reveal REPLs is a boring OS window, you can easily convert these to stickers by supplying <code class="language-plaintext highlighter-rouge">:always-on-top true</code> option, e.g.</p>
<div class="language-sh highlighter-rouge"><div class="highlight"><pre class="highlight"><code>clj <span class="se">\</span>
<span class="nt">-Sdeps</span> <span class="s1">'{:deps {vlaaad/reveal {:mvn/version "1.3.250"}}}'</span> <span class="se">\</span>
<span class="nt">-X</span> vlaaad.reveal/repl :always-on-top <span class="nb">true</span>
</code></pre></div></div>
<p>If you use Reveal as a REPL server, you can add this option to <code class="language-plaintext highlighter-rouge">:args</code> of a launcher alias, e.g.:</p>
<div class="language-clj highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="no">:reveal-server</span><span class="w"> </span><span class="p">{</span><span class="no">:exec-fn</span><span class="w"> </span><span class="n">clojure.core.server/start-server</span><span class="w">
</span><span class="no">:exec-args</span><span class="w"> </span><span class="p">{</span><span class="no">:name</span><span class="w"> </span><span class="s">"reveal-server"</span><span class="w">
</span><span class="no">:port</span><span class="w"> </span><span class="mi">5555</span><span class="w">
</span><span class="no">:accept</span><span class="w"> </span><span class="n">vlaaad.reveal/repl</span><span class="w">
</span><span class="no">:args</span><span class="w"> </span><span class="p">[</span><span class="no">:always-on-top</span><span class="w"> </span><span class="n">true</span><span class="p">]</span><span class="w">
</span><span class="no">:server-daemon</span><span class="w"> </span><span class="n">false</span><span class="p">}}</span><span class="w">
</span></code></pre></div></div>
<h1 id="editor-integration">Editor integration</h1>
<p>Now you can easily configure Reveal to play nicely with your IDE setup. What else might be needed? I think some light editor integration can still be useful: some predefined forms that you might want to send to your REPL bound to shortcuts — like the “REPL Command” feature in Cursive. In my experience with using stickers, I found 2 commands useful as a starting point:</p>
<h2 id="inspect-the-last-result">Inspect the last result</h2>
<p>Most of the time, the value you want to inspect is an evaluation result of the latest code form you submitted to REPL. For that, I use the following REPL command:</p>
<div class="language-clj highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="o">#</span><span class="n">reveal/inspect</span><span class="w"> </span><span class="n">*1</span><span class="w">
</span></code></pre></div></div>
<h2 id="minimizeunminimize-all-stickers-at-once">Minimize/unminimize all stickers at once</h2>
<p>Sometimes you need a full screen space of your IDE. Looking at you merge conflict resolution UI… For that case, I find it useful to have the following REPL command at the ready:</p>
<div class="language-clj highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">(</span><span class="nf">vlaaad.reveal/submit-command!</span><span class="w"> </span><span class="no">:always-on-top</span><span class="w"> </span><span class="p">(</span><span class="nf">vlaaad.reveal/toggle-minimized</span><span class="p">))</span><span class="w">
</span></code></pre></div></div>
<h1 id="the-end">The end</h1>
<p>Now you know how to use Reveal stickers to setup an overlay with live vortex into the JVM. You can configure it to represent the app you develop exactly as you want to see it. Give it a try and tell me what you think!</p>The year is 2011. “It’s just a jar”, you say to your colleagues, adding Clojure to the classpath. They look at you with disbelief as you cackle diabolically, and open a real-time chat with Mrs. Eval, a witch of Java Vast Magicland. She gives you the power to debug and fix a running server without ever restarting it again.REPL vs CLI: IDE wars2021-07-01T00:00:00+00:002021-07-01T00:00:00+00:00/clj-vs-cli<p>I’ve been thinking recently that Clojure REPL and CLI are both IDEs, and one might be better than another at being an IDE, so I decided to collect some scattered thoughts about the subject because I think this would be interesting to discuss.</p>
<p>The target audience of this post is software developers writing projects in Clojure.</p>
<h2 id="intro">Intro</h2>
<p>Let’s start with what I mean by abbreviations used throughout the post:</p>
<ul>
<li>REPL is a <a href="https://clojure.org/guides/repl/introduction#_what_is_a_repl">Clojure Read-Eval-Print Loop</a>, a programming environment which enables the programmer to interact with a running Clojure program and modify it, by evaluating one code expression at a time. In this post by REPL I mean Clojure Read-Eval-Print Loop used in conjunction with a <code class="language-plaintext highlighter-rouge">clj</code> command-line tool and a text editor capable of sending forms to this REPL, i.e. a standard REPL-aided development workflow;</li>
<li>CLI is a <a href="https://en.wikipedia.org/wiki/Command-line_interface">command line interface</a>, e.g. <a href="https://en.wikipedia.org/wiki/Unix_shell">Unix shell</a> — a command-line processor accepting commands one-at-a-time, a scripting language and a set of accompanying tools;</li>
<li>IDE is an <a href="https://en.wikipedia.org/wiki/Integrated_development_environment">integrated development environment</a> — a software that provides comprehensive facilities for software development, such as code editing, building executables, and debugging.</li>
</ul>
<p>I will not be the first to argue that <a href="https://blog.sanctum.geek.nz/series/unix-as-ide/">CLIs are IDEs</a>. You can use CLI for file management, text editing, building executables, debugging, version control, and more.</p>
<p>It might be a bit of a stretch to say that REPL is an IDE — especially when my definition of REPL for this post already includes some other text editor of choice 😄. In this post, I want to mostly focus on the “building executables” area of IDEs, e.g. helper tasks (like building/testing/deploying/configuring environment) that programmers usually use CLI for when developing Clojure projects. I would argue that even today REPL can be used as IDE, there is also seems to be a movement towards making more tooling a first-class citizen at the REPL, and REPL — when used as an IDE — might work much better than CLIs. Let’s compare them to see why I think so.</p>
<h2 id="tool-installation-and-project-setup">Tool installation and project setup</h2>
<p><code class="language-plaintext highlighter-rouge">brew install foo</code> or <code class="language-plaintext highlighter-rouge">apt install bar</code> are easy but complect dependency installation. If your project setup depends on <code class="language-plaintext highlighter-rouge">foo</code> and <code class="language-plaintext highlighter-rouge">bar</code> being in your IDE of choice, they should be automatically installed when developing the project instead of being mentioned in the readme. CLI dependency management is implicit and not reproducible: you can’t (probably?) make your shell auto-install some tools when you work in the context of your project.</p>
<p>Required tools will be forgotten to be mentioned in the readme. Installation instructions will differ for different operating systems. Default versions of tools provided by the OS will be incompatible with versions required by the project. All these issues happened to me while I was writing this post!</p>
<p>REPL tools are specified in <code class="language-plaintext highlighter-rouge">deps.edn</code> and automatically pulled by <code class="language-plaintext highlighter-rouge">clj</code>. Explicit, unforgotten, local to the project, reproducible.</p>
<p>For example, let’s have a look at a project that is available both on the command line and in the REPL: <a href="https://github.com/borkdude/jet">borkdude/jet</a>.</p>
<p>Here is how you install it for your project using CLI tooling:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>$ bash <(curl -s https://raw.githubusercontent.com/borkdude/jet/master/install)
</code></pre></div></div>
<p>or using <code class="language-plaintext highlighter-rouge">brew</code>:</p>
<div class="language-sh highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>brew <span class="nb">install </span>borkdude/brew/jet
</code></pre></div></div>
<p>Windows instructions will be different:</p>
<div class="language-sh highlighter-rouge"><div class="highlight"><pre class="highlight"><code>scoop bucket add scoop-clojure https://github.com/littleli/scoop-clojure
scoop bucket add extras
scoop <span class="nb">install </span>jet
</code></pre></div></div>
<p>If you use it in a project at work, your colleagues now have to repeat your <code class="language-plaintext highlighter-rouge">jet</code> installation steps. If there is a new version that your project requires, you now need to update it using another CLI command and remind everyone on your team to do the same.</p>
<p>Here is how you install it when your IDE is REPL:</p>
<div class="language-clj highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">;; deps.edn</span><span class="w">
</span><span class="p">{</span><span class="no">:deps</span><span class="w"> </span><span class="p">{</span><span class="n">borkdude/jet</span><span class="w"> </span><span class="p">{</span><span class="no">:mvn/version</span><span class="w"> </span><span class="s">"0.0.12"</span><span class="p">}}}</span><span class="w">
</span></code></pre></div></div>
<p>Your colleagues can start using this tool as soon as you commit it. If there is an upgrade, you commit the new version and your colleagues will get it automatically when they pull the latest changes from the repo. Unlike with command-line tools, installation and updates are the same on Windows, Linux, and macOS.</p>
<h2 id="dynamic-runtime">Dynamic runtime</h2>
<p>In the command line, I can install tools from the internet and start using them in the same shell session. When using Clojure REPL, I need to add the dependency to <code class="language-plaintext highlighter-rouge">deps.edn</code> and then restart the REPL.</p>
<p>Except I don’t: I use <a href="https://github.com/clojure/tools.deps.alpha/tree/add-lib3">add-lib</a> branch of <code class="language-plaintext highlighter-rouge">tools.deps.alpha</code> that allows me to add dependencies dynamically at the REPL and then start using them immediately, just like in the shell.</p>
<h2 id="scripting">Scripting</h2>
<p>Shell scripts are error-prone by default: any failure is ignored and the execution continues. Don’t forget the <code class="language-plaintext highlighter-rouge">set -euo pipefail</code> at the beginning of your script. What does <code class="language-plaintext highlighter-rouge">-euo</code> means? I don’t know, I copy-pasted it and <code class="language-plaintext highlighter-rouge">man set</code> said there is no manual entry for <code class="language-plaintext highlighter-rouge">set</code>.</p>
<p>REPL scripting is fantastic because Clojure is a fantastic language even when used for imperative do-this do-that pipelines. If you get an error, the execution stops by default and you get a stack trace. You have the power of the REPL to debug, profile and develop your script.</p>
<p>If I’m writing Clojure for the main program, why use different inferior languages for scripts around the main program? After all, it increases cognitive complexity and I’d rather spend my complexity budget on solving problems…</p>
<p>Is it because there are simply too few tools available in the REPL? With modern tooling embracing <a href="https://insideclojure.org/2020/07/28/clj-exec/">clj-exec</a> invocation that works both at the command line and in the REPL, there are more and more tasks I can do in the REPL. Running tests — <a href="https://github.com/cognitect-labs/test-runner#invoke-with-clojure--x-exec-style">io.github.cognitect-labs/test-runner</a>. Building jars — <a href="https://github.com/seancorfield/depstar">com.github.seancorfield/depstar</a>. If there is no tool for the JVM, you can always shell out from the REPL using <code class="language-plaintext highlighter-rouge">clojure.java.shell</code> ns.</p>
<p>Maybe it’s because of the inertia? Users of verbose heavy-weight programming languages like Java probably would never consider writing smaller scripts in the main program language, but Clojure is definitely as good for writing small programs as it is for writing big programs.</p>
<p>Or maybe it’s the startup time?</p>
<h2 id="the-startup-time">The startup time</h2>
<p>This is where shell scripts shine, right? Clojure tools take seconds to start while command line tools take millis. Except this is a false comparison: when I work in the REPL, command invocation is a function call, not starting up the whole JVM. Let’s compare how fast the startup time of a CLI jet vs REPL jet!</p>
<p>CLI:</p>
<div class="language-sh highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">time </span><span class="k">for </span>i <span class="k">in</span> <span class="o">{</span>1..1000<span class="o">}</span><span class="p">;</span> <span class="k">do </span><span class="nb">echo</span> <span class="s1">'{:a 1}'</span> | jet <span class="nt">--to</span> json <span class="o">></span> /dev/null <span class="p">;</span> <span class="k">done
</span>real 0m6.337s
user 0m4.107s
sys 0m2.838s
</code></pre></div></div>
<p>REPL:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>$ clj -Sdeps '{:deps {borkdude/jet {:mvn/version "0.0.12"}}}'
Clojure 1.10.3
user=> (require 'jet.formats)
nil
user=> (time (dotimes [_ 1000] (jet.formats/generate-json {:a 1} false)))
"Elapsed time: 37.1361 msecs"
nil
</code></pre></div></div>
<p>6 seconds vs 37 milliseconds! Function startup time and JIT FTW 🚀🚀🚀</p>
<h2 id="command-editing">Command editing</h2>
<p>When typing command-line invocations, the “editor”, a.k.a. the command line input is clunky: pressing up in multi-line command will go through history instead of moving the cursor up, most common modern shortcuts for editing and selecting don’t work or inconsistent. Ctrl+Left to jump words and holding Shift to extend selection is <em>shell-dependent</em> (i.e. it works in PowerShell and does not work in Bash).</p>
<p>In Clojure REPL, command “editor” is your favorite text editor that works as you want instead of working as compatible with terminals from the eighties. Completion, documentation, syntax highlighting is there. History — contextual! — is there if you use <a href="https://betweentwoparens.com/blog/rich-comment-blocks/">rich comment forms</a>.</p>
<h2 id="program-arguments">Program arguments</h2>
<p>CLI args accept only one data type as an argument: an array of strings. Each command-line tool parses this array in its own way. There are GNU Program Argument Syntax Guidelines, but they are not enforced in any way and modern command-line tools don’t always follow them either. Figuring out what are the expected arguments is not always trivial.</p>
<p>Clojure functions accept a big variety of data types, which is more expressive. Function invocation is regular — there is only one way to provide args to a function.</p>
<h2 id="command-composition">Command composition</h2>
<p>You can glue shell commands together with pipes and $(command interpolation).</p>
<p>Clojure has a function composition that is similar to both interpolation and piping. Another benefit in terms of composition at the REPL is that objects in the VM are more interactive than commands in the shell.</p>
<p>Let me illustrate this with another example: <a href="https://docs.datomic.com/cloud/operation/cli-tools.html">Datomic command line tools</a>. These tools are distributed as a command-line executable. When I’m developing an application that connects to the Datomic cloud, before starting the REPL I need to start a proxy that allows the Datomic client on my machine to connect to the Datomic cluster in the cloud.</p>
<p>If you look at the executable, you will see that the entire source of it is a <code class="language-plaintext highlighter-rouge">clj</code> main function invocation:</p>
<div class="language-sh highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c">#!/usr/bin/env bash</span>
clojure <span class="se">\</span>
<span class="nt">-Sdeps</span> <span class="s2">"{:deps {com.datomic/tools.ops {:mvn/version </span><span class="se">\"</span><span class="s2">0.10.82</span><span class="se">\"</span><span class="s2">}}}"</span> <span class="se">\</span>
<span class="nt">-m</span> datomic.tools.ops <span class="s2">"</span><span class="nv">$@</span><span class="s2">"</span>
</code></pre></div></div>
<p>So this shell script is also available as <a href="https://mvnrepository.com/artifact/com.datomic/tools.ops/0.10.89">Apache 2.0 licensed</a> Clojure library. When using it as a library, I can start the proxy from the REPL, e.g.</p>
<div class="language-clj highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">(</span><span class="nf">datomic.tools.ops.ssh/access</span><span class="w">
</span><span class="p">{</span><span class="no">:port</span><span class="w"> </span><span class="mi">8182</span><span class="w">
</span><span class="no">:ssho</span><span class="w"> </span><span class="p">[</span><span class="s">"IdentitiesOnly=yes"</span><span class="p">]</span><span class="w">
</span><span class="no">:type</span><span class="w"> </span><span class="no">:client</span><span class="w">
</span><span class="no">:system</span><span class="w"> </span><span class="s">"my-datomic-system"</span><span class="p">})</span><span class="w">
</span></code></pre></div></div>
<p>The current implementation will block the calling thread until the proxy disconnects, but with a small modification it will be able to return an instance of <a href="https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/lang/Process.html">java.lang.Process</a> — a process handle that allows me to query it, stop it, add exit callbacks — all from the REPL. This makes it possible to inject the proxy startup in a code that initializes the Datomic client so I won’t need to think about proxying at all — no more “run this command in a separate terminal before launching the REPL”!</p>
<p>Wouldn’t it be nice if <code class="language-plaintext highlighter-rouge">datomic.tools.ops.ssh/access</code> didn’t block by default? 😛</p>
<h2 id="instead-of-a-summary">Instead of a summary</h2>
<p>When I was writing this post, I considered converting all my Bash and PowerShell scripts to pure Clojure to prove my point, but I haven’t done it because they work just fine as they are. CLI tools are neither broken nor bad. I’m not proposing to Rewrite It In Clojure™ — there will always be CLI, and there will always be command-line tools that are required for the development, but minimizing the amount of these tools is good for the project and developer. Maybe one day <code class="language-plaintext highlighter-rouge">git</code>, <code class="language-plaintext highlighter-rouge">java</code>, <code class="language-plaintext highlighter-rouge">clj</code> and ssh will be enough for every Clojure project…</p>
<p>Right now using REPL as IDE might be rough around the edges. I need rich comment forms for history and convenience. I need an unofficial tools-deps branch to add dependencies dynamically. The amount of <code class="language-plaintext highlighter-rouge">clj-exec</code>-friendly tooling is small.</p>
<p>Bit it’s growing. Cognitect will soon release tools-build that expands the REPL toolbelt. There are rumors <code class="language-plaintext highlighter-rouge">add-lib</code> branch will end up in some future version of Clojure. When that happens, I think the local maxima for the project tooling will be much higher.</p>
<p>What do you think? Discuss here, on <a href="https://www.reddit.com/r/Clojure/comments/obm420/repl_vs_cli_ide_wars/">Reddit</a> or <a href="https://news.ycombinator.com/item?id=27698987">Hacker News</a>.</p>I’ve been thinking recently that Clojure REPL and CLI are both IDEs, and one might be better than another at being an IDE, so I decided to collect some scattered thoughts about the subject because I think this would be interesting to discuss.View Vega charts in Reveal2021-06-01T00:00:00+00:002021-06-01T00:00:00+00:00/vega-in-reveal<h2 id="reveal-charts">Reveal charts</h2>
<p>So I made this explorable REPL output pane called <a href="https://vlaaad.github.io/reveal/">Reveal</a>. It’s built on top of JavaFX, a UI framework for JVM that has built-in charts. Reveal <a href="https://vlaaad.github.io/reveal/#charts">provides access</a> to those charts, where data in a shape that fits the expected chart input shape can be viewed visually. Those charts have some interesting properties, specifically that they are not leaves in terms of exploration — you can select a data point and then access the object in the VM to explore it further. Here is an example:</p>
<p><img src="/assets/reveal/line-chart.gif" alt="Line chart demo" /></p>
<p>I think that’s pretty nice, but one downside of these JavaFX charts is that their visualization abilities are limited: axes don’t have scales other than linear, you can’t put labels on the chart, can’t mix lines with areas, etc.</p>
<h2 id="vega-charts">Vega charts</h2>
<p>I’m no data scientist, so you might disagree with me on that, but there is a pretty good visualization library for the web called <a href="https://vega.github.io/vega/">Vega</a>. It uses declarative syntax (json) to describe charts. There is also <a href="https://vega.github.io/vega-lite/">Vega-Lite</a> — easy API to Vega that makes describing those charts much more concise. Here is an example of such a chart along with the chart spec:</p>
<p><img src="/assets/2021-06-01/vega.png" alt="Vega-Lite example" /></p>
<h2 id="vega-in-reveal">Vega in Reveal</h2>
<p>Since JavaFX includes a browser capable of showing vega charts, and Vega can be fairly easy embedded into web pages, and it’s trivial to convert Clojure data structures to json, I created a simple view that shows Vega charts:</p>
<div class="language-clj highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">(</span><span class="nf">require</span><span class="w"> </span><span class="o">'</span><span class="p">[</span><span class="n">cljfx.ext.web-view</span><span class="w"> </span><span class="no">:as</span><span class="w"> </span><span class="n">fx.ext.web-view</span><span class="p">]</span><span class="w">
</span><span class="o">'</span><span class="p">[</span><span class="n">clojure.data.json</span><span class="w"> </span><span class="no">:as</span><span class="w"> </span><span class="n">json</span><span class="p">])</span><span class="w"> </span><span class="c1">;; the only external dependency</span><span class="w">
</span><span class="p">(</span><span class="k">defn</span><span class="w"> </span><span class="n">vega-view</span><span class="w"> </span><span class="p">[{</span><span class="no">:keys</span><span class="w"> </span><span class="p">[</span><span class="n">spec</span><span class="p">]}]</span><span class="w">
</span><span class="p">{</span><span class="no">:fx/type</span><span class="w"> </span><span class="n">fx.ext.web-view/with-engine-props</span><span class="w">
</span><span class="no">:props</span><span class="w"> </span><span class="p">{</span><span class="no">:content</span><span class="w"> </span><span class="p">(</span><span class="nb">str</span><span class="w"> </span><span class="s">"
<head>
<script src=\"https://cdn.jsdelivr.net/npm/vega@5\"></script>
<script src=\"https://cdn.jsdelivr.net/npm/vega-lite@4\"></script>
<script src=\"https://cdn.jsdelivr.net/npm/vega-embed@6\"></script>
</head>
<body>
<div id=\"view\"></div>
<script>vegaEmbed('#view', "</span><span class="w"> </span><span class="p">(</span><span class="nf">json/write-str</span><span class="w"> </span><span class="n">spec</span><span class="p">)</span><span class="w"> </span><span class="s">");</script>
</body>"</span><span class="p">)}</span><span class="w">
</span><span class="no">:desc</span><span class="w"> </span><span class="p">{</span><span class="no">:fx/type</span><span class="w"> </span><span class="no">:web-view</span><span class="p">}})</span><span class="w">
</span></code></pre></div></div>
<p>Now I can create a vega view in the REPL…</p>
<div class="language-clj highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">{</span><span class="no">:fx/type</span><span class="w"> </span><span class="n">vega-view</span><span class="w">
</span><span class="no">:spec</span><span class="w"> </span><span class="p">{</span><span class="no">:data</span><span class="w"> </span><span class="p">{</span><span class="no">:url</span><span class="w"> </span><span class="s">"https://vega.github.io/vega-lite/data/seattle-weather.csv"</span><span class="p">}</span><span class="w">
</span><span class="no">:mark</span><span class="w"> </span><span class="no">:bar</span><span class="w">
</span><span class="no">:encoding</span><span class="w"> </span><span class="p">{</span><span class="no">:x</span><span class="w"> </span><span class="p">{</span><span class="no">:timeUnit</span><span class="w"> </span><span class="no">:month</span><span class="w">
</span><span class="no">:field</span><span class="w"> </span><span class="no">:date</span><span class="w">
</span><span class="no">:type</span><span class="w"> </span><span class="no">:ordinal</span><span class="p">}</span><span class="w">
</span><span class="no">:y</span><span class="w"> </span><span class="p">{</span><span class="no">:aggregate</span><span class="w"> </span><span class="no">:mean</span><span class="w">
</span><span class="no">:field</span><span class="w"> </span><span class="no">:precipitation</span><span class="p">}}}}</span><span class="w">
</span></code></pre></div></div>
<p>…and view it in Reveal:</p>
<p><img src="/assets/2021-06-01/reveal.png" alt="Vega in Reveal" /></p>
<p>Yes, this view is a leaf, but it’s a very powerful leaf that can display a big variety of data visualizations.</p>
<h2 id="hacking-at-the-repl">Hacking at the REPL</h2>
<p>I use this <code class="language-plaintext highlighter-rouge">vega-view</code> for stock market-related explorations. I combine it with Reveal commands for automatically showing views so I don’t have to leave the editor — you can find an example <a href="https://github.com/vlaaad/reveal/blob/master/examples/e08_vega_view.clj">here</a>. This way I can iterate on vega visualizations in the REPL one eval at a time, and see the updated chart immediately. I find this setup fantastic — Clojure REPL is a productive and fun environment, and Reveal brings a powerful visualization aspect that integrates into REPL-aided development workflow very well. Wow, that didn’t sound unbiased at all :D</p>
<p>Anyway, if you haven’t already, I would suggest giving Reveal a try!</p>Reveal chartsReveal, REPLs and networking2021-01-02T00:00:00+00:002021-01-02T00:00:00+00:00/reveal-repls-and-networking<p>I recently got a question whether it’s possible to configure <a href="vlaaad.github.io/reveal/">Reveal</a> in such a way that it works across 3 machines:</p>
<ul>
<li>machine A runs editor;</li>
<li>machine B runs only Reveal;</li>
<li>machine C runs target server.</li>
</ul>
<p>It also reminded me of <a href="https://suvratapte.com/nREPL-middleware/">an article</a> I read awhile ago about nREPL middlewares that gives a good overview of how those work, but unfortunately contains a mistake in a section where it discusses Clojure REPL, where it states that:</p>
<blockquote>
<p>there is no easy way to start this REPL on a socket. So if you are using this REPL, you cannot connect to it from remote machines. So the default REPL clearly cannot be used as your daily development REPL. That is where the need for other types of REPLs comes in.</p>
</blockquote>
<p>I enjoy using simple tooling, and REPL is a wonderful example of such a concept (it’s not a single tool really) that enables a variety of non-trivial use-cases. In this post I’ll try to explain what makes it special as well as give an example of using the configurability of REPL.</p>
<h2 id="what-repl-is-what-nrepl-isnt">What REPL is, what nREPL isn’t</h2>
<p>REPL is Read-Eval-Print Loop, a programming environment that enables you to interact with a running Clojure program and modify it by evaluating one code expression at a time. An important characteristic of REPL is that every part of REP is independent and thus swappable:</p>
<ul>
<li><em>Read</em> is a protocol on character streams — one of the most widely available and simple transports. You can swap Read easily: consume from standart input, consume from network, consume from pre-recorded REPL interaction to replay it etc;</li>
<li><em>Eval</em> is the full power of Clojure, and you can augment it when necessary by e.g. starting new REPLs with <a href="https://github.com/TristeFigure/lexikon/blob/master/src/lexikon/core.clj#L129-L148">lexical scope</a> which might be seen as a break point on steroids;</li>
<li><em>Print</em> is a way to show the output of code evaluation to the user, by default it transforms values to text, but you can do much more than that with a tool like <a href="vlaaad.github.io/reveal/">Reveal</a> that acts as a REPL output panel that enables inspection and visualization super powers;</li>
</ul>
<p>nREPL, despite its name, is not a REPL, it’s a eval RPC server. It does not have independent Read Eval and Print concepts, instead its main building blocks are <a href="https://nrepl.org/nrepl/0.8/design/handlers.html">handlers</a>, <a href="https://nrepl.org/nrepl/0.8/design/transports.html">transport</a> and <a href="https://nrepl.org/nrepl/0.8/design/middleware.html">middlewares</a>. It’s a different model that, like REPL, gives some powers and takes some powers away. I personally find REPL model simpler, more powerful and more approachable, so I use it, but YMMV.</p>
<h2 id="starting-repl-socket-server">Starting REPL socket server</h2>
<p>It is trivial to start a REPL that can be reached from a remote machine, you won’t even need any external dependencies, it’s all there in <a href="https://clojure.github.io/clojure/clojure.core-api.html#clojure.core.server/start-server">clojure.core.server</a> namespace. The easiest way to start it is to specify a JVM property that <a href="https://clojure.org/reference/repl_and_main#_launching_a_socket_server">starts socket server</a> automatically on the JVM startup, but for simplicity we will call it directly as a function:</p>
<div class="language-sh highlighter-rouge"><div class="highlight"><pre class="highlight"><code>clj <span class="se">\</span>
<span class="nt">-X</span> clojure.core.server/start-server <span class="se">\</span>
:name <span class="s1">'"repl"'</span> <span class="se">\</span>
:port 5555 <span class="se">\</span>
:accept clojure.core.server/repl <span class="se">\</span>
:server-daemon <span class="nb">false</span>
</code></pre></div></div>
<p>This <a href="https://insideclojure.org/2020/09/04/clj-exec/">clj-exec</a> invocation supplies all required args to <code class="language-plaintext highlighter-rouge">clojure.core.server/start-server</code> fn:</p>
<ul>
<li><code class="language-plaintext highlighter-rouge">:name</code> is a server identifier that can be used to stop the server;</li>
<li><code class="language-plaintext highlighter-rouge">:port</code> is a socket REPL port;</li>
<li><code class="language-plaintext highlighter-rouge">:accept</code> is a symbol indicating a repl function.</li>
</ul>
<p>Remaining <code class="language-plaintext highlighter-rouge">:server-daemon</code> argument is needed to keep the JVM running while REPL server is active, you won’t need this argument if you are using <code class="language-plaintext highlighter-rouge">start-server</code> from the REPL.</p>
<p>If you are unfamiliar with clj-exec, this invocation is analogous to a following clojure form:</p>
<div class="language-clj highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">(</span><span class="nf">clojure.core.server/start-server</span><span class="w">
</span><span class="o">'</span><span class="p">{</span><span class="no">:name</span><span class="w"> </span><span class="s">"repl"</span><span class="w">
</span><span class="no">:port</span><span class="w"> </span><span class="mi">5555</span><span class="w">
</span><span class="no">:accept</span><span class="w"> </span><span class="n">clojure.core.server/repl</span><span class="w">
</span><span class="no">:server-daemon</span><span class="w"> </span><span class="n">false</span><span class="p">})</span><span class="w">
</span></code></pre></div></div>
<h2 id="connecting-to-remote-socket-repl">Connecting to remote socket REPL</h2>
<p>You can connect to it using <code class="language-plaintext highlighter-rouge">nc</code> and start sending forms:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>nc localhost 5555
user=> (+ 1 2 3)
6
</code></pre></div></div>
<p>How about nesting REPLs to connect to this REPL server from another clojure REPL? There is no built-in way to do it, but the implementation of <a href="https://github.com/vlaaad/remote-repl">REPL client</a> is less than 50 lines of code, thanks to the simplicity of REPL concept. Let’s try it out:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>clj \
-Sdeps '{:deps {vlaaad/remote-repl {:mvn/version "1.1"}}}'
Clojure 1.10.1
user=> (require '[vlaaad.remote-repl :as rr])
nil
user=> (rr/repl :port 5555)
;; at this point, forms sent to the repl are evaluated in the remote process
user=> clojure.core.server/*session*
{:server repl, :client "1"}
user=> :repl/quit
;; now we are back to evaluating in our local process.
nil
user=>
</code></pre></div></div>
<h2 id="repls-for-humans-prepls-for-tools">REPLs for humans, prepls for tools</h2>
<p>How about using Reveal to connect to this server? Reveal is a tool that needs structured REPL output to process it properly, it can’t really work on REPL prompts like <code class="language-plaintext highlighter-rouge">user=></code>. There is <a href="https://oli.me.uk/clojure-socket-prepl-cookbook/">prepl</a> (programmable REPL), which is a socket REPL with output structured as edn maps — bread and butter of Clojure. To connect Reveal to remote socket REPL server it needs to be a prepl, like that:</p>
<div class="language-sh highlighter-rouge"><div class="highlight"><pre class="highlight"><code>clj <span class="se">\</span>
<span class="nt">-X</span> clojure.core.server/start-server <span class="se">\</span>
:name <span class="s1">'"repl"'</span> <span class="se">\</span>
:port 5555 <span class="se">\</span>
:accept clojure.core.server/io-prepl <span class="se">\</span>
:server-daemon <span class="nb">false</span>
</code></pre></div></div>
<p>If you are curious how this REPL’s output looks like, here is an example at the command line:</p>
<div class="language-sh highlighter-rouge"><div class="highlight"><pre class="highlight"><code>clj <span class="se">\</span>
<span class="nt">-Sdeps</span> <span class="s1">'{:deps {vlaaad/remote-repl {:mvn/version "1.1"}}}'</span> <span class="se">\</span>
<span class="nt">-X</span> vlaaad.remote-repl/repl <span class="se">\</span>
:port 5555
<span class="o">(</span>+ 1 2 3<span class="o">)</span>
<span class="o">{</span>:tag :ret, :val <span class="s2">"6"</span>, :ns <span class="s2">"user"</span>, :ms 9, :form <span class="s2">"(+ 1 2 3)"</span><span class="o">}</span>
</code></pre></div></div>
<p>Reveal can talk to this prepl server out of the box with its <a href="https://vlaaad.github.io/reveal/#remote-prepl">remote-prepl</a>:</p>
<div class="language-sh highlighter-rouge"><div class="highlight"><pre class="highlight"><code>clj <span class="se">\</span>
<span class="nt">-Sdeps</span> <span class="s1">'{:deps {vlaaad/reveal {:mvn/version "1.2.188"}}}'</span> <span class="se">\</span>
<span class="nt">-X</span> vlaaad.reveal/remote-prepl <span class="se">\</span>
:port 5555
<span class="o">(</span>+ 1 2 3<span class="o">)</span>
<span class="o">{</span>:tag :ret, :val 6, :ns <span class="s2">"user"</span>, :ms 2, :form <span class="s2">"(+ 1 2 3)"</span><span class="o">}</span>
</code></pre></div></div>
<p>Console output is the same, but there is now a Reveal window that shows evaluations results:</p>
<p><img src="/assets/2021-01-02/remote-prepl.png" alt="" /></p>
<h2 id="reveal-clients-clients">Reveal client’s clients</h2>
<p>Now, lets get back to the original question of having REPL configuration where editor is on machine A, Reveal on machine B and target process on machine C. We already have most of the pieces laid out, the only missing part is how to setup reveal to run as a server that is itself a client, and that part is <code class="language-plaintext highlighter-rouge">:args</code> — additional arguments to a repl function specified by <code class="language-plaintext highlighter-rouge">:accept</code> symbol.</p>
<p>Lets setup it piece by piece. I’ll use everything on the same machine because I’m lazy, but the real world example will differ only in having to specify <code class="language-plaintext highlighter-rouge">:host</code> in addition to <code class="language-plaintext highlighter-rouge">:port</code> in clients. Here is machine C with ClojureScript prepl just for fun:</p>
<div class="language-sh highlighter-rouge"><div class="highlight"><pre class="highlight"><code>clj <span class="se">\</span>
<span class="nt">-Sdeps</span> <span class="s1">'{:deps {org.clojure/clojurescript {:mvn/version "1.10.764"}}}'</span> <span class="se">\</span>
<span class="nt">-X</span> clojure.core.server/start-server <span class="se">\</span>
:name <span class="s1">'"cljs"'</span> <span class="se">\</span>
:accept cljs.server.browser/prepl <span class="se">\</span>
:port 5555 <span class="se">\</span>
:server-daemon <span class="nb">false</span>
</code></pre></div></div>
<p>Machine B, that uses Reveal to connect to C while acting as a REPL server:</p>
<div class="language-sh highlighter-rouge"><div class="highlight"><pre class="highlight"><code>clj <span class="se">\</span>
<span class="nt">-Sdeps</span> <span class="s1">'{:deps {vlaaad/reveal {:mvn/version "1.2.188"}}}'</span> <span class="se">\</span>
<span class="nt">-X</span> clojure.core.server/start-server <span class="se">\</span>
:name <span class="s1">'"reveal"'</span> <span class="se">\</span>
:accept vlaaad.reveal/remote-prepl <span class="se">\</span>
:args <span class="s1">'[:port 5555]'</span> <span class="se">\</span>
:port 6666 <span class="se">\</span>
:server-daemon <span class="nb">false</span>
</code></pre></div></div>
<p>Finally, we can connect from machine A to machine B on port <code class="language-plaintext highlighter-rouge">6666</code>, and that will make it open a Reveal window with connection to machine C:</p>
<div class="language-sh highlighter-rouge"><div class="highlight"><pre class="highlight"><code>clj <span class="se">\</span>
<span class="nt">-Sdeps</span> <span class="s1">'{:deps {vlaaad/remote-repl {:mvn/version "1.1"}}}'</span> <span class="se">\</span>
<span class="nt">-X</span> vlaaad.remote-repl/repl <span class="se">\</span>
:port 6666
</code></pre></div></div>
<p>Evaluating code like <code class="language-plaintext highlighter-rouge">js/window</code> on machine A will make ClojureScript evaluate code in the browser on machine C and send it to machine B where Reveal will show the output:</p>
<p><img src="/assets/2021-01-02/cljs-prepl.png" alt="" /></p>
<h2 id="conclusion">Conclusion</h2>
<p>Once I’ve got the simplicity of REPL, I’ve got a lot more power at my disposal, with a significantly smaller cognitive footprint and improved understanding of underlying stack (e.g. Clojure evaluation semantics). If you don’t know it, you don’t know it; if you know it, you enjoy it.</p>
<p>What do you think?</p>I recently got a question whether it’s possible to configure Reveal in such a way that it works across 3 machines: machine A runs editor; machine B runs only Reveal; machine C runs target server.Alternative to tools.cli in 10 lines of code2020-07-27T00:00:00+00:002020-07-27T00:00:00+00:00/tools-cli-in-10-lines-of-code<p>Let me start with what I think command line interface to a program should do:</p>
<ul>
<li>it should teach its user how to use it by invoking it with some well-known argument such as <code class="language-plaintext highlighter-rouge">-h</code> or <code class="language-plaintext highlighter-rouge">--help</code>;</li>
<li>it should run a program, optionally with some arguments.</li>
</ul>
<h2 id="status-quo">Status quo</h2>
<p>For its purpose, writing command line entry point is too damn verbose: parsing args, performing validation, providing defaults — all this is a boring busywork. There are libraries such as <a href="https://github.com/clojure/tools.cli">tools.cli</a> that help with that, but if you look at it’s API, you will see yet another ad-hoc implementation of half of clojure spec: it’s a data-driven parser of a sequence of strings with schema, validations and defaults. Its benefit is conformance to GNU Program Argument Syntax Guidelines, but I would prefer to have an API that is simple and straightforward than having to remember what every letter means in the context of a particular command.</p>
<p>If only defining a command was as easy as defining a function… If only parsing arguments was as predictable as using clojure reader… Perhaps it’s possible? Without further ado, let’s jump straight into 10 lines of Clojure this post is about.</p>
<h2 id="10-lines-of-clojure-this-post-is-about">10 lines of Clojure this post is about</h2>
<div class="language-clojure highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">(</span><span class="k">defn</span><span class="w"> </span><span class="n">-main</span><span class="w"> </span><span class="p">[</span><span class="o">&</span><span class="w"> </span><span class="n">opts</span><span class="p">]</span><span class="w">
</span><span class="p">(</span><span class="k">let</span><span class="w"> </span><span class="p">[</span><span class="n">f</span><span class="w"> </span><span class="o">#</span><span class="p">(</span><span class="nf">try</span><span class="w">
</span><span class="p">(</span><span class="k">let</span><span class="w"> </span><span class="p">[</span><span class="n">form</span><span class="w"> </span><span class="p">(</span><span class="nf">read-string</span><span class="w"> </span><span class="n">%</span><span class="p">)]</span><span class="w">
</span><span class="p">(</span><span class="k">cond</span><span class="w">
</span><span class="p">(</span><span class="nf">qualified-symbol?</span><span class="w"> </span><span class="n">form</span><span class="p">)</span><span class="w"> </span><span class="o">@</span><span class="p">(</span><span class="nf">requiring-resolve</span><span class="w"> </span><span class="n">form</span><span class="p">)</span><span class="w">
</span><span class="p">(</span><span class="nb">symbol?</span><span class="w"> </span><span class="n">form</span><span class="p">)</span><span class="w"> </span><span class="o">@</span><span class="p">((</span><span class="nb">ns-publics</span><span class="w"> </span><span class="p">(</span><span class="nb">symbol</span><span class="w"> </span><span class="p">(</span><span class="nb">namespace</span><span class="w"> </span><span class="o">`</span><span class="n">-main</span><span class="p">)))</span><span class="w"> </span><span class="n">form</span><span class="p">)</span><span class="w">
</span><span class="no">:else</span><span class="w"> </span><span class="n">form</span><span class="p">))</span><span class="w">
</span><span class="p">(</span><span class="nf">catch</span><span class="w"> </span><span class="n">Exception</span><span class="w"> </span><span class="n">_</span><span class="w"> </span><span class="n">%</span><span class="p">))</span><span class="w">
</span><span class="p">[</span><span class="n">f</span><span class="w"> </span><span class="o">&</span><span class="w"> </span><span class="n">args</span><span class="p">]</span><span class="w"> </span><span class="p">(</span><span class="nb">map</span><span class="w"> </span><span class="n">f</span><span class="w"> </span><span class="n">opts</span><span class="p">)]</span><span class="w">
</span><span class="p">(</span><span class="nf">some-></span><span class="w"> </span><span class="p">(</span><span class="nb">apply</span><span class="w"> </span><span class="n">f</span><span class="w"> </span><span class="n">args</span><span class="p">)</span><span class="w"> </span><span class="nb">prn</span><span class="p">)))</span><span class="w">
</span></code></pre></div></div>
<p>This entry point establishes a convention for invoking a program and parsing arguments that is a bit vague, but simple, straightforward and powerful. The convention: it is a function call in main ns with parens omitted. The important implication of this convention is that you learn both clojure and command line APIs at the same time. Here is how invocation of this CLI might look like:</p>
<div class="language-sh highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>clj <span class="nt">-m</span> cli foo :x bar :y <span class="nb">true</span>
</code></pre></div></div>
<p>And here is how using this ns from clojure might look like:</p>
<div class="language-clj highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">(</span><span class="nf">cli/foo</span><span class="w"> </span><span class="no">:x</span><span class="w"> </span><span class="s">"bar"</span><span class="w"> </span><span class="no">:y</span><span class="w"> </span><span class="n">true</span><span class="p">)</span><span class="w">
</span></code></pre></div></div>
<p>Looks pretty similar, isn’t it? The actual implementation allows a bit more in one place and a bit less in another, but I think that’s fine. Let’s explore the possibilities and limitations!</p>
<h2 id="arguments-defaults-validation">Arguments, defaults, validation</h2>
<p>With this entry point creating tasks is done by defining new functions. Let’s start with a simple task that we will use to see how arguments are parsed:</p>
<div class="language-clojure highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">(</span><span class="nf">ns</span><span class="w"> </span><span class="n">cli</span><span class="p">)</span><span class="w">
</span><span class="p">(</span><span class="k">defn</span><span class="w"> </span><span class="n">echo</span><span class="w"> </span><span class="p">[</span><span class="o">&</span><span class="w"> </span><span class="n">args</span><span class="p">]</span><span class="w">
</span><span class="p">(</span><span class="nb">apply</span><span class="w"> </span><span class="nb">prn</span><span class="w"> </span><span class="n">args</span><span class="p">))</span><span class="w">
</span><span class="p">(</span><span class="k">defn</span><span class="w"> </span><span class="n">-main</span><span class="w"> </span><span class="p">[]</span><span class="w"> </span><span class="n">...</span><span class="p">)</span><span class="w">
</span></code></pre></div></div>
<p>Now lets invoke it:</p>
<div class="language-sh highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>clj <span class="nt">-m</span> cli <span class="nb">echo</span> :host 127.0.0.1 :port 8080 :async <span class="nb">true</span>
:host <span class="s2">"127.0.0.1"</span> :port 8080 :async <span class="nb">true</span>
</code></pre></div></div>
<p>As you can see, <code class="language-plaintext highlighter-rouge">:host</code>, <code class="language-plaintext highlighter-rouge">:port</code> and <code class="language-plaintext highlighter-rouge">:async</code> are keywords, <code class="language-plaintext highlighter-rouge">true</code> is a boolean, and <code class="language-plaintext highlighter-rouge">127.0.0.1</code> is a string. We can easily and consistently parse arguments to values that make sense and invoke a function! What about defaults and validation? Just use normal Clojure code to do both! Here is an example:</p>
<div class="language-clojure highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">(</span><span class="k">defn</span><span class="w"> </span><span class="n">ensure-connection</span><span class="w">
</span><span class="s">"Ensure a connection to a network resource is possible
Available options:
- :host (optional, defaults to localhost) - target host
- :port (required) - target port
- :timeout (ms, optional, defaults to 10000) - connection timeout"</span><span class="w">
</span><span class="p">[</span><span class="o">&</span><span class="w"> </span><span class="p">{</span><span class="no">:keys</span><span class="w"> </span><span class="p">[</span><span class="n">host</span><span class="w"> </span><span class="n">port</span><span class="w"> </span><span class="n">timeout</span><span class="p">]</span><span class="w">
</span><span class="no">:or</span><span class="w"> </span><span class="p">{</span><span class="n">host</span><span class="w"> </span><span class="s">"localhost"</span><span class="w">
</span><span class="n">timeout</span><span class="w"> </span><span class="mi">10000</span><span class="p">}}]</span><span class="w">
</span><span class="p">{</span><span class="no">:pre</span><span class="w"> </span><span class="p">[(</span><span class="nb">string?</span><span class="w"> </span><span class="n">host</span><span class="p">)</span><span class="w"> </span><span class="p">(</span><span class="nf">int?</span><span class="w"> </span><span class="n">port</span><span class="p">)</span><span class="w"> </span><span class="p">(</span><span class="nf">int?</span><span class="w"> </span><span class="n">timeout</span><span class="p">)]}</span><span class="w">
</span><span class="p">(</span><span class="nb">doto</span><span class="w"> </span><span class="p">(</span><span class="nf">java.net.Socket.</span><span class="p">)</span><span class="w">
</span><span class="p">(</span><span class="nf">.connect</span><span class="w"> </span><span class="p">(</span><span class="nf">java.net.InetSocketAddress.</span><span class="w"> </span><span class="o">^</span><span class="n">String</span><span class="w"> </span><span class="n">host</span><span class="w"> </span><span class="o">^</span><span class="nb">int</span><span class="w"> </span><span class="n">port</span><span class="p">)</span><span class="w"> </span><span class="n">timeout</span><span class="p">)</span><span class="w">
</span><span class="p">(</span><span class="nf">.close</span><span class="p">))</span><span class="w">
</span><span class="p">(</span><span class="nb">println</span><span class="w"> </span><span class="s">"Connection can be established"</span><span class="p">))</span><span class="w">
</span></code></pre></div></div>
<p>It certainly looks like a regular clojure function you might find in your code or someone’s library. Let’s try invoking it from the command line with required parameter missing:</p>
<div class="language-sh highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>clj <span class="nt">-m</span> cli ensure-connection
Execution error <span class="o">(</span>AssertionError<span class="o">)</span> at cli/ensure-connection <span class="o">(</span>cli.clj:16<span class="o">)</span><span class="nb">.</span>
Assert failed: <span class="o">(</span>int? port<span class="o">)</span>
Full report at:
/tmp/clojure-10136260048334273705.edn
</code></pre></div></div>
<p>Wonderful, we have validation! You can use spec or hand-written error messages to improve error reporting. Now let’s add missing parameter:</p>
<div class="language-sh highlighter-rouge"><div class="highlight"><pre class="highlight"><code>clj <span class="nt">-m</span> cli ensure-connection :port 443
Execution error <span class="o">(</span>ConnectException<span class="o">)</span> at sun.nio.ch.Net/pollConnect <span class="o">(</span>Net.java:-2<span class="o">)</span><span class="nb">.</span>
Connection refused
Full report at:
/tmp/clojure-1463050396010033872.edn
</code></pre></div></div>
<p>Exit code is 1, since I don’t have anything running on port 443. Overriding defaults is dead simple:</p>
<div class="language-sh highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>clj <span class="nt">-m</span> cli ensure-connection :port 443 :host google.com
Connection can be established
</code></pre></div></div>
<h2 id="more-power-to-the-user">More power to the user</h2>
<p>You may have noticed that this entry point resolves symbols. One unintended consequence is that this CLI allows invoking any function by specifying its fully qualified symbol:</p>
<div class="language-sh highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>clj <span class="nt">-m</span> cli clojure.core/prn :woot
:woot
</code></pre></div></div>
<p>While this particular behavior is certainly not intended and can be restricted with a bit more code, the ability to supply symbols is extremely useful for improving expressivity available to this CLI: it supports any def-ed value as an argument! Suppose we write a custom repl that in its first iteration behaves exactly like <code class="language-plaintext highlighter-rouge">clojure.main/repl</code>:</p>
<div class="language-clojure highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">(</span><span class="k">defn</span><span class="w"> </span><span class="n">repl</span><span class="w"> </span><span class="p">[</span><span class="o">&</span><span class="w"> </span><span class="n">options</span><span class="p">]</span><span class="w">
</span><span class="p">(</span><span class="nb">apply</span><span class="w"> </span><span class="n">clojure.main/repl</span><span class="w"> </span><span class="n">options</span><span class="p">))</span><span class="w">
</span></code></pre></div></div>
<p>Invoking it from the command line supports all options expecting functions, so we can e.g. configure its printing behavior from the command line:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>$ clj -m cli repl :print clojure.pprint/pprint
user=> (meta #'tap>)
{:arglists ([x]),
:doc "sends x to any taps. Will not block. Returns true if there was room in the queue,\n false if not (dropped).",
:added "1.10",
:line 7886,
:column 1,
:file "clojure/core.clj",
:name tap>,
:ns #object[clojure.lang.Namespace 0x30404dba "clojure.core"]}
</code></pre></div></div>
<h2 id="getting-help">Getting help</h2>
<p>What about learning the API? First of all, it’s easy to create <code class="language-plaintext highlighter-rouge">help</code> command that prints function’s docstrings:</p>
<div class="language-clojure highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">(</span><span class="k">defn</span><span class="w"> </span><span class="n">help</span><span class="w"> </span><span class="p">[</span><span class="n">f</span><span class="p">]</span><span class="w">
</span><span class="p">(</span><span class="nb">println</span><span class="w"> </span><span class="p">(</span><span class="no">:doc</span><span class="w"> </span><span class="p">(</span><span class="nb">meta</span><span class="w"> </span><span class="p">(</span><span class="nb">resolve</span><span class="w"> </span><span class="p">(</span><span class="nb">symbol</span><span class="w"> </span><span class="p">(</span><span class="nf">Compiler/demunge</span><span class="w"> </span><span class="p">(</span><span class="nf">.getName</span><span class="w"> </span><span class="p">(</span><span class="nb">class</span><span class="w"> </span><span class="n">f</span><span class="p">)))))))))</span><span class="w">
</span></code></pre></div></div>
<p>Your documentation in code is now available in the command line:</p>
<div class="language-sh highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>clj <span class="nt">-m</span> cli <span class="nb">help </span>ensure-connection
Tests a connection to a network resource.
Available options:
- :host <span class="o">(</span>optional, defaults to localhost<span class="o">)</span> - target host
- :port <span class="o">(</span>required<span class="o">)</span> - target port
- :timeout <span class="o">(</span>ms, optional, defaults to 10000<span class="o">)</span> - connection <span class="nb">timeout</span>
</code></pre></div></div>
<p>Now, what about well-known higher-level help using <code class="language-plaintext highlighter-rouge">--help</code> or <code class="language-plaintext highlighter-rouge">-h</code>? Wait, aren’t those valid clojure symbols? Lets try defining those!</p>
<div class="language-clojure highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">(</span><span class="k">defn</span><span class="w"> </span><span class="n">--help</span><span class="w"> </span><span class="p">[]</span><span class="w">
</span><span class="p">(</span><span class="nb">println</span><span class="w"> </span><span class="s">"Available commands:"</span><span class="p">)</span><span class="w">
</span><span class="p">(</span><span class="nb">doseq</span><span class="w"> </span><span class="p">[</span><span class="n">sym</span><span class="w"> </span><span class="p">(</span><span class="nb">sort</span><span class="w"> </span><span class="p">(</span><span class="nb">keys</span><span class="w"> </span><span class="p">(</span><span class="nb">dissoc</span><span class="w"> </span><span class="p">(</span><span class="nb">ns-publics</span><span class="w"> </span><span class="p">(</span><span class="nb">symbol</span><span class="w"> </span><span class="p">(</span><span class="nb">namespace</span><span class="w"> </span><span class="o">`</span><span class="n">--help</span><span class="p">)))</span><span class="w"> </span><span class="ss">'-main</span><span class="p">)))]</span><span class="w">
</span><span class="p">(</span><span class="nb">println</span><span class="w"> </span><span class="p">(</span><span class="nb">str</span><span class="w"> </span><span class="s">" "</span><span class="w"> </span><span class="n">sym</span><span class="p">)))</span><span class="w">
</span><span class="p">(</span><span class="nb">println</span><span class="w"> </span><span class="s">"Use help <command> to see description of that command"</span><span class="p">))</span><span class="w">
</span><span class="p">(</span><span class="k">def</span><span class="w"> </span><span class="n">-h</span><span class="w"> </span><span class="n">--help</span><span class="p">)</span><span class="w">
</span></code></pre></div></div>
<p>Would that work? Yes it would!</p>
<div class="language-sh highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>clj <span class="nt">-m</span> cli <span class="nt">--help</span>
Available commands:
<span class="nt">--help</span>
<span class="nt">-h</span>
<span class="nb">echo
</span>ensure-connection
<span class="nb">help
</span>repl
Use <span class="nb">help</span> <<span class="nb">command</span><span class="o">></span> to see description of that <span class="nb">command</span>
</code></pre></div></div>
<h2 id="limitations">Limitations</h2>
<p>This entry point does not evaluate S-expressions, instead forms are read as is:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>$ clj -m cli echo "(if 1 :foo :bar)"
(if 1 :foo :bar)
</code></pre></div></div>
<p>I think this is a good thing: if you want to start writing code for such command line invocation, it’s a sign you should write a proper program. But now that you learned the command line API, you also learned the library API!</p>
<h2 id="summary">Summary</h2>
<p>If you are willing to give up GNU Program Argument Syntax Guidelines for your command line entry point to a Clojure library, you might find yourself having a simple, concise, powerful and straightforward API that will make your code transparent to your users.</p>
<p>What do you think? Discuss <a href="https://www.reddit.com/r/Clojure/comments/hynnhy/alternative_to_toolscli_in_10_lines_of_code/">on reddit</a>.</p>Let me start with what I think command line interface to a program should do: it should teach its user how to use it by invoking it with some well-known argument such as -h or --help; it should run a program, optionally with some arguments.Year of Clojure on the Desktop2020-03-28T00:00:00+00:002020-03-28T00:00:00+00:00/year-of-clojure-on-the-desktop<p>What is Clojure’s use case? <a href="https://www.quora.com/What-are-the-best-use-cases-for-using-Clojure-for-new-development/answers/653524">In theory</a>, it’s any situation where JVM is a reasonable choice, but especially for high concurrency and processing large amounts of data. In practice, it’s mostly used on the backend for servers. Java is also widely used on Android, but Clojure is <a href="https://blog.ndk.io/state-of-coa.html">rarely a good choice there</a> due to it’s startup time.</p>
<h2 id="an-unexpected-journey">An unexpected journey</h2>
<p>Another area where JVM is useful is desktop applications. With web browsers eating the front-end world it’s a hard choice, especially with tools like Electron allowing to have the same code base for both web and desktop apps. Countless resources spent on making DOM and Javascript VMs
as efficient and approachable as possible make it a solid choice for many types of applications despite the historical baggage.</p>
<p>I think that even though the web is more popular then ever there are still use cases where JVM is a better choice as an application platform, and coincidentally, these use cases align perfectly with Clojure’s advantages over other JVM languages:</p>
<ul>
<li>browser-based technology struggles to utilize multiple cores in the same VM instance, while Clojure’s immutability by default, concurrency primitives and core.async make writing multi-threaded code a breeze;</li>
<li>javascript VMs choke on processing large amounts of data, which leaves them a better fit for advanced interactive forms than compilers or data processing pipelines;</li>
<li>Clojure’s REPL-aided development with fast feedback loop is perfect for tinkering with UI, as opposed to compile-and-restart-the-app-and-then-navigate-the-UI-to-see-your-change approach of other languages.</li>
</ul>
<p>Until recently there were 2 other huge selling points of the web over JVM that are finally getting addressed: react model and app distribution.</p>
<h2 id="react-model">React model</h2>
<p><a href="https://reactjs.org/">React</a> changed UI development for the better. It brought it’s complexity sure, but also it made creating consistent UI wonderfully simple. Funnily enough, Clojure’s <a href="https://clojure.org/guides/equality">equality semantics</a> play extremely well with react model: <a href="http://reagent-project.github.io/">reagent</a> — ClojureScript wrapper of React — outperforms plain React a lot of the time due to optimizations that re-render components only when their inputs change.</p>
<p><a href="https://openjfx.io/">JavaFX</a> is the largest <a href="https://www.oracle.com/technetwork/java/javafx/overview/faq-1446554.html#6">supported</a> desktop application platform for the JVM. Clojure has <a href="https://github.com/cljfx/cljfx">cljfx</a> — declarative, functional and extensible react-like wrapper of JavaFX (which I wrote). Here is an example of a cljfx component:</p>
<div class="language-clj highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">(</span><span class="k">defn</span><span class="w"> </span><span class="n">todo-view</span><span class="w"> </span><span class="p">[{</span><span class="no">:keys</span><span class="w"> </span><span class="p">[</span><span class="n">text</span><span class="w"> </span><span class="n">id</span><span class="w"> </span><span class="n">done</span><span class="p">]}]</span><span class="w">
</span><span class="p">{</span><span class="no">:fx/type</span><span class="w"> </span><span class="no">:h-box</span><span class="w">
</span><span class="no">:spacing</span><span class="w"> </span><span class="mi">5</span><span class="w">
</span><span class="no">:padding</span><span class="w"> </span><span class="mi">5</span><span class="w">
</span><span class="no">:children</span><span class="w"> </span><span class="p">[{</span><span class="no">:fx/type</span><span class="w"> </span><span class="no">:check-box</span><span class="w">
</span><span class="no">:selected</span><span class="w"> </span><span class="n">done</span><span class="w">
</span><span class="no">:on-selected-changed</span><span class="w"> </span><span class="p">{</span><span class="no">:event/type</span><span class="w"> </span><span class="no">::set-done</span><span class="w"> </span><span class="no">:id</span><span class="w"> </span><span class="n">id</span><span class="p">}}</span><span class="w">
</span><span class="p">{</span><span class="no">:fx/type</span><span class="w"> </span><span class="no">:label</span><span class="w">
</span><span class="no">:style</span><span class="w"> </span><span class="p">{</span><span class="no">:-fx-text-fill</span><span class="w"> </span><span class="p">(</span><span class="k">if</span><span class="w"> </span><span class="n">done</span><span class="w"> </span><span class="no">:grey</span><span class="w"> </span><span class="no">:black</span><span class="p">)}</span><span class="w">
</span><span class="no">:text</span><span class="w"> </span><span class="n">text</span><span class="p">}]})</span><span class="w">
</span></code></pre></div></div>
<p>As you can see, it’s a simple function that returns simple data structures: bread and butter of Clojure code. You may notice that it can use event maps (in addition to functions) for event listeners to decouple logic from representation. Another thing to notice is that JavaFX uses styling similar to how it’s done on the web: with inline styles or external CSS files. In addition to inline styles being composable data in cljfx, there is also <a href="https://github.com/cljfx/css">cljfx/css</a> library that allows configuring CSS using same data structures and code.</p>
<h2 id="app-distribution">App distribution</h2>
<p>The problem of compiling a javascript app with various dependencies has many different solutions, and once you bake that js file just right, all you need to do to deliver it to users is upload it to a server. Java compilation, on the other hand, produces classes and jar files that still require JVM to be executed. JVM also needs to be distributed. In addition to that, there are platform differences: apps for Linux are usually in <code class="language-plaintext highlighter-rouge">deb</code> or <code class="language-plaintext highlighter-rouge">rpm</code> format, for macOS — <code class="language-plaintext highlighter-rouge">pkg</code> or <code class="language-plaintext highlighter-rouge">dmg</code>, for Windows — <code class="language-plaintext highlighter-rouge">msi</code> or <code class="language-plaintext highlighter-rouge">exe</code>.</p>
<p>Thankfully, Java 14 that was released earlier this year includes a new tool called <a href="https://openjdk.java.net/jeps/343">jpackage</a> that deals with packaging self-contained applications. It does all the heavy lifting needed to produce a platform-specific application package. Using jpackage, it’s now easy to create an app from the jar, and all that’s left to do is upload it to a server to allow users to download and install it.</p>
<p>What can be a better example of what is possible with Clojure on the desktop than a simple sample application? I made <a href="https://github.com/cljfx/hn">Hacker News Reader</a> app that shows how cljfx, cljfx/css and jpackage can be used together to create an installable application. You can even <a href="https://github.com/cljfx/hn/releases">download it</a> and give it a try — packages are built using Github Actions. Please note that there are some build steps that were intentionally omitted to keep it simple:</p>
<ul>
<li>startup time can be improved significantly by AOT-compiling Clojure code;</li>
<li>application package size can be reduced: you can use <a href="https://docs.oracle.com/javase/9/tools/jlink.htm">jlink</a> to minify the JDK, and if your application does not need to use webkit (which is used in this example), you can exclude cljfx’s dependency on javafx-web.</li>
</ul>
<h2 id="closing-thoughts">Closing thoughts</h2>
<p>I think JVM has a sweet spot for desktop apps that lies between interactive forms / <a href="https://www.arp242.net/webui.html">typesetting</a> that web is good at and extremely resource-intensive apps like games / 3D modeling software that need to be written in something lower-level. In the middle of that sweet spot, there is Clojure and cljfx that allow developing UI interactively with instant feedback — in the live app, not some static UI builder. It’s not all rainbows: JavaFX has some issues here and there, but it’s nice nonetheless. <a href="https://defold.com/">Editors for game engines</a> can be written in Clojure. <a href="https://github.com/vlaaad/reveal">Advanced visual REPLs</a> can be written in Clojure. Give it a try.</p>
<p>What do you think? Discuss <a href="https://www.reddit.com/r/Clojure/comments/fqimas/year_of_clojure_on_the_desktop/">on reddit</a>.</p>What is Clojure’s use case? In theory, it’s any situation where JVM is a reasonable choice, but especially for high concurrency and processing large amounts of data. In practice, it’s mostly used on the backend for servers. Java is also widely used on Android, but Clojure is rarely a good choice there due to it’s startup time.