<?xml version="1.0" encoding="UTF-8"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/"><channel><atom:link href="https://www.mikkokoski.com" rel="self" type="application/rss+xml"/><title>mikkokoski.com</title><link>https://www.mikkokoski.com</link><description>Mikko Koski's Blog</description><generator>clj-rss</generator><item><guid isPermaLink="false">https://www.mikkokoski.com/blog/jj-heart-standard-clj/index.html</guid><title>jj fix 🩷 standard-clj</title><link>https://www.mikkokoski.com/blog/jj-heart-standard-clj/index.html</link><pubDate>Tue, 26 May 2026 17:30:29 +0000</pubDate><description>&lt;div&gt;&lt;p&gt;For the past few weeks, I&amp;apos;ve been using &lt;a href="https://www.jj-vcs.dev/"&gt;Jujutsu&lt;/a&gt; (&lt;code&gt;jj&lt;/code&gt;) as my primary version control system instead of &lt;code&gt;git&lt;/code&gt;. I&amp;apos;m still learning, but I think I&amp;apos;m past the point of no return.&lt;/p&gt;&lt;p&gt;&lt;code&gt;jj&lt;/code&gt; has a bunch of cool things, but one of my favorite commands is &lt;code&gt;jj fix&lt;/code&gt;.&lt;/p&gt;&lt;p&gt;In this article, I&amp;apos;ll describe what &lt;code&gt;jj fix&lt;/code&gt; does and why it&amp;apos;s better than doing the same with &lt;code&gt;git rebase&lt;/code&gt; + &lt;code&gt;git amend&lt;/code&gt; workflow, and how it integrates with my Clojure formatter of choice, &lt;code&gt;standard-clj&lt;/code&gt;.&lt;/p&gt;&lt;h2&gt;&lt;code&gt;jj fix&lt;/code&gt; in a nutshell&lt;/h2&gt;&lt;p&gt;&lt;code&gt;jj fix&lt;/code&gt; updates files with formatting fixes. You can hook it up with a formatter of your liking, e.g. &lt;code&gt;prettier&lt;/code&gt; or &lt;code&gt;standard-clj&lt;/code&gt; or any tool that can take content from stdin and output it to stdout.&lt;/p&gt;&lt;p&gt;At &lt;a href="https://www.sharetribe.com"&gt;Sharetribe&lt;/a&gt;, we started using &lt;code&gt;standard-clj&lt;/code&gt; as the formatter for our Clojure code, and it works really well with &lt;code&gt;jj fix&lt;/code&gt;.&lt;/p&gt;&lt;p&gt;Let&amp;apos;s see how &lt;code&gt;jj fix&lt;/code&gt; works.&lt;/p&gt;&lt;h2&gt;So you forgot to run the formatter...&lt;/h2&gt;&lt;p&gt;Here&amp;apos;s the problem.&lt;/p&gt;&lt;p&gt;Let&amp;apos;s say you made a commit &amp;quot;Hello, world&amp;quot;, where you added the following piece of code:&lt;/p&gt;&lt;pre class="language-clojure"&gt;(ns myapp)

(defn hello []
     (println &amp;quot;Hello, world&amp;quot;)
    
    )
&lt;/pre&gt;&lt;p&gt;Notice the indentation, which is broken.&lt;/p&gt;&lt;p&gt;After that, you made a second commit &amp;quot;Bye bye&amp;quot;, where you changed the &amp;quot;Hello, world&amp;quot; to &amp;quot;Bye bye&amp;quot;:&lt;/p&gt;&lt;pre class="language-clojure"&gt;(ns myapp)

(defn hello []
     (println &amp;quot;Bye bye!&amp;quot;)
    
    )
&lt;/pre&gt;&lt;p&gt;You are now ready to commit your changes and open a PR. But then you realize: &lt;strong&gt;You forgot to run the code formatter&lt;/strong&gt;.&lt;/p&gt;&lt;p&gt;With Git, you have two options:&lt;/p&gt;&lt;ol&gt;&lt;li&gt;Run the formatter and add a third commit just for the formatting fixes.&lt;/li&gt;&lt;li&gt;Do a &lt;code&gt;git rebase --interactive&lt;/code&gt; and fix the formatting in the commits that introduced them.&lt;/li&gt;&lt;/ol&gt;&lt;p&gt;The professional in you thinks that of course option 2 is the right thing to do, but since the interactive rebase with Git is kinda cumbersome, it&amp;apos;s tempting to just be lazy and pick option 1.&lt;/p&gt;&lt;p&gt;The problem with the git rebase option is the following.&lt;/p&gt;&lt;p&gt;Let&amp;apos;s do &lt;code&gt;git rebase --interactive&lt;/code&gt; to edit the &amp;quot;Hello, world&amp;quot; commit&lt;/p&gt;&lt;pre class="language-bash"&gt;edit 36dc4f9 # Hello, world
pick 0057aed # Bye bye

# Rebase 0057aed onto 049568a (2 commands)
&lt;/pre&gt;&lt;p&gt;Now while rebase is ongoing and we are editing &lt;code&gt;36dc4f9&lt;/code&gt;, let&amp;apos;s run &lt;code&gt;standard-clj fix&lt;/code&gt;:&lt;/p&gt;&lt;pre class="language-bash"&gt;➜  git:(36dc4f9) ✗ standard-clj fix myapp.clj
standard-clj fix v0.28.0

F /myapp.clj [3.47ms]

1 file formatted with Standard Clojure Style 👍 [1067.49ms]
&lt;/pre&gt;&lt;p&gt;&lt;code&gt;standard-clj&lt;/code&gt; successfully fixed (&lt;code&gt;F&lt;/code&gt;) &lt;code&gt;/myapp.clj&lt;/code&gt;&lt;/p&gt;&lt;p&gt;Let&amp;apos;s amend the formatting fixes and continue rebase.&lt;/p&gt;&lt;pre class="language-bash"&gt;➜  git:(36dc4f9) ✗ git add myapp.clj
➜  git:(36dc4f9) ✗ git commit --amend
&lt;/pre&gt;&lt;p&gt;Now let&amp;apos;s continue the rebase:&lt;/p&gt;&lt;pre class="language-bash"&gt;➜  git:(b98ec2c) git rebase --continue
Auto-merging myapp.clj
CONFLICT (content): Merge conflict in myapp.clj
error: could not apply 0057aed... Bye bye
hint: Resolve all conflicts manually, mark them as resolved with
hint: &amp;quot;git add/rm &amp;lt;conflicted_files&amp;gt;&amp;quot;, then run &amp;quot;git rebase --continue&amp;quot;.
hint: You can instead skip this commit: run &amp;quot;git rebase --skip&amp;quot;.
hint: To abort and get back to the state before &amp;quot;git rebase&amp;quot;, run &amp;quot;git rebase --abort&amp;quot;.
hint: Disable this message with &amp;quot;git config set advice.mergeConflict false&amp;quot;
Could not apply 0057aed... # Bye bye
&lt;/pre&gt;&lt;p&gt;Oh no... as you might have expected, we have a conflict:&lt;/p&gt;&lt;pre class="language-clojure"&gt;(ns myapp)

(defn hello []
&amp;lt;&amp;lt;&amp;lt;&amp;lt;&amp;lt;&amp;lt;&amp;lt; HEAD
  (println &amp;quot;Hello, world&amp;quot;))
=======
     (println &amp;quot;Bye bye!&amp;quot;)
    
    )

&amp;gt;&amp;gt;&amp;gt;&amp;gt;&amp;gt;&amp;gt;&amp;gt; 0057aed (Bye bye)
&lt;/pre&gt;&lt;p&gt;What happened? We formatted the earlier &amp;quot;Hello, world&amp;quot; commit. Then we continued with the rebase. The &amp;quot;Bye bye&amp;quot; commit touches the same line that got formatted, so while git tries to apply the &amp;quot;Bye bye&amp;quot; commit diff, it realizes that the underlying &amp;quot;Hello, world&amp;quot; commit had changed, so it doesn&amp;apos;t know how to apply the diff.&lt;/p&gt;&lt;h2&gt;Enter &lt;code&gt;jj fix&lt;/code&gt;&lt;/h2&gt;&lt;p&gt;Let&amp;apos;s do the same with &lt;code&gt;jj fix&lt;/code&gt;.&lt;/p&gt;&lt;p&gt;I&amp;apos;ve initialized a new jj repo with the same two commits. Also, I&amp;apos;ve configured &lt;code&gt;jj fix&lt;/code&gt; to use &lt;code&gt;standard-clj&lt;/code&gt; for formatting (we&amp;apos;ll see soon how that is done).&lt;/p&gt;&lt;p&gt;Here&amp;apos;s how my &lt;code&gt;jj log&lt;/code&gt; looks:&lt;/p&gt;&lt;pre class="language-bash"&gt;➜  jj:(pwl) jj log
@  pwlomtxr mikko@sharetribe.com 2026-05-19 22:58:54 122deafe
│  Bye bye
○  qoxyznmy mikko@sharetribe.com 2026-05-19 22:57:33 9d8ce0ae
│  Hello, world
◆  zzzzzzzz root() 00000000
&lt;/pre&gt;&lt;p&gt;We have the same two commits: &amp;quot;Hello, world&amp;quot; (qox) and Bye bye (pwl).&lt;/p&gt;&lt;p&gt;Now, let&amp;apos;s run &lt;code&gt;jj fix&lt;/code&gt;:&lt;/p&gt;&lt;pre class="language-bash"&gt;➜  jj:(pwl) jj fix
Fixed 2 commits of 2 checked.
Working copy  (@) now at: pwlomtxr 8cb76981 Bye bye
Parent commit (@-)      : qoxyznmy 5fd8b557 Hello, world
Added 0 files, modified 1 files, removed 0 files
&lt;/pre&gt;&lt;p&gt;Boom! 💥 &lt;code&gt;jj fix&lt;/code&gt; found and fixed formatting issues in two commits, &lt;strong&gt;without any conflicts!&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;Let&amp;apos;s confirm it actually formatted the code as expected and included the formatting fixes in correct commits. Here&amp;apos;s the diff for &amp;quot;Hello, world&amp;quot; (&lt;code&gt;qox&lt;/code&gt;) commit:&lt;/p&gt;&lt;pre class="language-bash"&gt;➜  jj:(pwl) jj diff -r qox
Added regular file myapp.clj:
        1: (ns myapp)
        2:
        3: (defn hello []
        4:   (println &amp;quot;Hello, world&amp;quot;))
&lt;/pre&gt;&lt;p&gt;Yep, the &amp;quot;Hello, world&amp;quot; commit is now correctly formatted.&lt;/p&gt;&lt;p&gt;And let&amp;apos;s also see the diff for &amp;quot;Bye bye&amp;quot; (&lt;code&gt;pwl&lt;/code&gt;) commit:&lt;/p&gt;&lt;pre class="language-bash"&gt;➜  jj:(pwl) jj diff -r pwl
Modified regular file myapp.clj:
   1    1: (ns myapp)
   2    2:
   3    3: (defn hello []
-  4     :   (println &amp;quot;Hello, world&amp;quot;))
+       4:   (println &amp;quot;Bye bye!&amp;quot;))
&lt;/pre&gt;&lt;p&gt;Looks good! No formatting fixes in this commit. The only change is the text from &amp;quot;Hello, world&amp;quot; to &amp;quot;Bye bye&amp;quot;.&lt;/p&gt;&lt;p&gt;And of course, this works with any number of commits. Two commits are still easy to handle, but with 10+ commits where all of them have formatting issues, handling all this with &lt;code&gt;git rebase&lt;/code&gt; would be very cumbersome.&lt;/p&gt;&lt;h2&gt;Why no conflict with &lt;code&gt;jj fix&lt;/code&gt;?&lt;/h2&gt;&lt;p&gt;So you might wonder, how does &lt;code&gt;jj fix&lt;/code&gt; do this without introducing conflicts as git did?&lt;/p&gt;&lt;p&gt;Here&amp;apos;s what &lt;code&gt;jj fix --help&lt;/code&gt; says about this (emphasis mine):&lt;/p&gt;&lt;blockquote&gt;&lt;p&gt;The changed files in the given revisions will be updated with any fixes determined by passing their &lt;strong&gt;file content&lt;/strong&gt; through any external tools the user has configured for those files. &lt;strong&gt;Descendants will also be updated by passing their versions of the same files&lt;/strong&gt; through the same tools, which will ensure that the fixes are not lost. This will never result in new conflicts.&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;What the help text is saying is that &lt;code&gt;jj fix&lt;/code&gt; does not care about diffs. It edits a commit and formats the &lt;strong&gt;file content&lt;/strong&gt; of the changed files (the snapshots of the files, not the diffs). Then it repeats that for the descendant commits. And indeed, this never results in conflicts. Neat!&lt;/p&gt;&lt;h2&gt;And it&amp;apos;s &lt;i&gt;fast&lt;/i&gt; too!&lt;/h2&gt;&lt;p&gt;Running &lt;code&gt;jj fix&lt;/code&gt; with &lt;code&gt;standard-clj&lt;/code&gt; is also very fast! The reason for this is that since &lt;code&gt;jj&lt;/code&gt; is aware of which files changed, it doesn&amp;apos;t run the formatter for the whole codebase, just to the files that changed. Of course, if you want to run it for the whole codebase, you can use the &lt;code&gt;--include-unchanged-files&lt;/code&gt; flag.&lt;/p&gt;&lt;h2&gt;How to configure &lt;code&gt;jj fix&lt;/code&gt; to use &lt;code&gt;standard-clj&lt;/code&gt;&lt;/h2&gt;&lt;blockquote&gt;&lt;p&gt;⚠️ Make sure you&amp;apos;re on standard-clj v0.28.0 or newer. See the note at the end of the post for why.&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;&lt;code&gt;jj&lt;/code&gt; can be configured at three levels: user, repo and workspace.&lt;/p&gt;&lt;p&gt;The formatting tool configuration goes into the repo level. The repo configuration file is stored outside of the repository. The way to know the path is to run:&lt;/p&gt;&lt;pre class="language-bash"&gt;➜  jj:(ymq) jj config path --repo
/Users/mikko/.config/jj/repos/3c9fd428d14ad5b1a5e0/config.toml
&lt;/pre&gt;&lt;p&gt;So that&amp;apos;s where you should add the configuration.&lt;/p&gt;&lt;p&gt;&lt;code&gt;jj&lt;/code&gt; requires that the tool used for &lt;code&gt;jj fix&lt;/code&gt; is able to take the content in stdin and output to stdout. &lt;code&gt;standard-clj fix -&lt;/code&gt; does exactly that. In addition to the tool &lt;code&gt;command&lt;/code&gt;, you need to specify glob &lt;code&gt;patterns&lt;/code&gt;. Here&amp;apos;s an example config that you can add to your repo-specific &lt;code&gt;config.toml&lt;/code&gt; file:&lt;/p&gt;&lt;pre class="language-bash"&gt;[fix.tools.standard-clj]
command = [&amp;quot;standard-clj&amp;quot;, &amp;quot;fix&amp;quot;, &amp;quot;-&amp;quot;]
patterns = [&amp;quot;glob:&amp;apos;src/**/*.{clj,cljs,cljc,edn}&amp;apos;&amp;quot;,
            &amp;quot;glob:&amp;apos;test/**/*.{clj,cljs,cljc,edn}&amp;apos;&amp;quot;,
            &amp;quot;glob:&amp;apos;deps.edn&amp;apos;&amp;quot;]
&lt;/pre&gt;&lt;h2&gt;Important! Use &lt;code&gt;standard-clj --version&lt;/code&gt; v0.28.0 or newer&lt;/h2&gt;&lt;p&gt;When I first started using &lt;code&gt;standard-clj&lt;/code&gt; with &lt;code&gt;jj fix&lt;/code&gt;, I noticed a weird bug. Some of my files, especially the longer ones with 1000+ lines, were sometimes just cut off. And since &lt;code&gt;jj fix&lt;/code&gt; runs the formatting for each commit, and the files were cut off in some earlier commit, you can probably imagine that this got my repository in a super broken state 😄.&lt;/p&gt;&lt;p&gt;Luckily, I was able to &lt;a href="https://github.com/oakmac/standard-clojure-style-js/pull/214"&gt;find the bug and provide the fix&lt;/a&gt;. This fix was released in &lt;code&gt;standard-clj&lt;/code&gt; version v0.28.0, so make sure you use that version or newer.&lt;/p&gt;&lt;/div&gt;</description></item><item><guid isPermaLink="false">https://www.mikkokoski.com/blog/derived-vars/index.html</guid><title>How to catch derived Vars with a clj-kondo hook</title><link>https://www.mikkokoski.com/blog/derived-vars/index.html</link><pubDate>Sun, 04 Feb 2024 14:38:36 +0000</pubDate><description>&lt;div&gt;&lt;p&gt;I love Clojure&amp;apos;s REPL-driven development workflow.&lt;/p&gt;&lt;p&gt;The fast feedback loop I get when I evaluate the changed code and immediately see the result brings me joy every day.&lt;/p&gt;&lt;p&gt;But... I do have one gripe: &lt;a href="https://clojure.org/guides/repl/enhancing_your_repl_workflow#writing-repl-friendly-programs"&gt;derived Vars&lt;/a&gt;.&lt;/p&gt;&lt;p&gt;Let&amp;apos;s take an example:&lt;/p&gt;&lt;pre class="language-clojure"&gt;(defn welcome-page-handler []
  {:status 200
   :body &amp;quot;Welcome!&amp;quot;})

(def routes
  {&amp;quot;/&amp;quot; welcome-page-handler})
&lt;/pre&gt;&lt;p&gt;In the example above, we have a very simple web app skeleton with a handler function &lt;code&gt;welcome-page-handler&lt;/code&gt; and a &lt;code&gt;routes&lt;/code&gt; map from URLs to handler functions.&lt;/p&gt;&lt;p&gt;Let&amp;apos;s try it out:&lt;/p&gt;&lt;pre class="language-clojure"&gt;(let [handler-fn (get routes &amp;quot;/&amp;quot;)]
  (handler-fn))

;; #=&amp;gt; {:status 200, :body &amp;quot;Welcome!&amp;quot;}
&lt;/pre&gt;&lt;p&gt;We simulated an HTTP request to the homepage path &lt;code&gt;&amp;quot;/&amp;quot;&lt;/code&gt;. We did &amp;quot;routing&amp;quot; with &lt;code&gt;get&lt;/code&gt; and called the handler function for the &lt;code&gt;&amp;quot;/&amp;quot;&lt;/code&gt; route, and the result was as expected. All good!&lt;/p&gt;&lt;p&gt;Now, let&amp;apos;s make a small change to the &lt;code&gt;welcome-page-handler&lt;/code&gt; function and re-evaluate it.&lt;/p&gt;&lt;pre class="language-clojure"&gt;(defn welcome-page-handler []
  {:status 200
   :body &amp;quot;Welcome, stranger!&amp;quot;})

;; #=&amp;gt; #&amp;apos;user/welcome-page-hander
&lt;/pre&gt;&lt;p&gt;Let&amp;apos;s test the handler again:&lt;/p&gt;&lt;pre class="language-clojure"&gt;(let [handler-fn (get routes &amp;quot;/&amp;quot;)]
  (handler-fn))

;; #=&amp;gt; {:status 200, :body &amp;quot;Welcome!&amp;quot;}
&lt;/pre&gt;&lt;p&gt;Dang! That&amp;apos;s not what we wanted to see, right? We were expecting the new &lt;code&gt;&amp;quot;Welcome, stranger!&amp;quot;&lt;/code&gt; greeting.&lt;/p&gt;&lt;p&gt;&lt;strong&gt;Why did this happen?&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;The reason is that the &lt;code&gt;routes&lt;/code&gt; map still points to the old version of the &lt;code&gt;welcome-page-handler&lt;/code&gt; function. To make the &lt;code&gt;routes&lt;/code&gt; point to the new version, we must also re-evaluate the &lt;code&gt;routes&lt;/code&gt; map too.&lt;/p&gt;&lt;p&gt;So what&amp;apos;s the big deal? Just re-evaluate &lt;code&gt;routes&lt;/code&gt;!&lt;/p&gt;&lt;p&gt;In a simple example like this, re-evaluating the &lt;code&gt;routes&lt;/code&gt; after we change the &lt;code&gt;welcome-page-handler&lt;/code&gt; (or any other function handler in the &lt;code&gt;routes&lt;/code&gt; map) is a viable option. However, in a larger codebase, the handler function might be in different namespaces where the routes map is. It is cumbersome to re-evaluate &lt;code&gt;routes&lt;/code&gt; every time you make a change to a handler. Changes are high that you will forget to do so.&lt;/p&gt;&lt;p&gt;Ok, so this is clearly a problem. Is there a solution?&lt;/p&gt;&lt;p&gt;Well, good news! &lt;strong&gt;Yes, there is a solution!&lt;/strong&gt; Three solutions, actually:&lt;/p&gt;&lt;ol&gt;&lt;li&gt;Use reloaded workflow&lt;/li&gt;&lt;li&gt;Var quoting with the &lt;code&gt;#&amp;apos;&lt;/code&gt; syntax&lt;/li&gt;&lt;li&gt;Function wrapping&lt;/li&gt;&lt;/ol&gt;&lt;p&gt;Let&amp;apos;s go through these options.&lt;/p&gt;&lt;h2&gt;Option 1: Use reloaded workflow&lt;/h2&gt;&lt;p&gt;At &lt;a href="https://www.sharetribe.com"&gt;Sharetribe&lt;/a&gt;, we&amp;apos;ve been developing our Clojure app for over nine years. Since the beginning, we&amp;apos;ve been using the &lt;a href="https://cognitect.com/blog/2013/06/04/clojure-workflow-reloaded"&gt;reloaded workflow&lt;/a&gt; and the &lt;a href="https://github.com/weavejester/reloaded.repl"&gt;reloaded.repl&lt;/a&gt; library to solve the derived Var issue. It has served us well.&lt;/p&gt;&lt;p&gt;The idea in a nutshell is to have a &lt;code&gt;(user/reset)&lt;/code&gt; function that utilizes &lt;a href="https://github.com/clojure/tools.namespace"&gt;&lt;code&gt;tools.namespace&lt;/code&gt;&lt;/a&gt; to reload the namespaces that have changed and the namespaces that depend on the changed namespaces.&lt;/p&gt;&lt;p&gt;So if you have a &lt;code&gt;routes&lt;/code&gt; namespace that requires &lt;code&gt;handlers&lt;/code&gt; namespace and the &lt;code&gt;handlers&lt;/code&gt; namespace changes, &lt;code&gt;reloaded.repl&lt;/code&gt; will reload both namespaces. This solves the issue with derived Vars.&lt;/p&gt;&lt;p&gt;This used to work well for us. But after nine years of development and 100k+ LOC of clj/c/s code, things started to get slow.&lt;/p&gt;&lt;p&gt;In the &lt;a href="https://cognitect.com/blog/2013/06/04/clojure-workflow-reloaded"&gt;Clojure Workflow Reloaded article&lt;/a&gt;, Stuart Sierra writes the following (emphasis mine):&lt;/p&gt;&lt;blockquote&gt;&lt;p&gt;Therefore, after every significant code change, I want to restart the application from scratch. But I don&amp;apos;t want to restart the JVM and reload all my Clojure code in order to do it: that takes too long and is too disruptive to my workflow. Instead, I want to design my application in such a way that I can quickly shut it down, discard any transient state it might have built up, start it again, and return to a similar state. And when I say quickly, I mean that &lt;strong&gt;the whole process should take less than a second.&lt;/strong&gt;&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;This is something that can&amp;apos;t be achieved with a large code base. Let me demonstrate.&lt;/p&gt;&lt;p&gt;If I&amp;apos;m in Sharetribe&amp;apos;s codebase and I make a change to one of the endpoint handler functions and call &lt;code&gt;refresh&lt;/code&gt; in our &lt;code&gt;user&lt;/code&gt; namespace, it usually takes 3-4 seconds:&lt;/p&gt;&lt;pre class="language-clojure"&gt;user&amp;gt; (time
       (clojure.tools.namespace.repl/refresh))
:reloading (...)
&amp;quot;Elapsed time: 4680.926875 msecs&amp;quot;
;; =&amp;gt; :ok
&lt;/pre&gt;&lt;p&gt;That&amp;apos;s too long, in my opinion.&lt;/p&gt;&lt;h2&gt;Options 2: Var quoting&lt;/h2&gt;&lt;p&gt;&lt;strong&gt;Var quoting&lt;/strong&gt; means that you use a &lt;a href="https://clojure.org/reference/reader#_dispatch"&gt;special reader syntax&lt;/a&gt; &lt;code&gt;#&amp;apos;&lt;/code&gt; so that the &lt;code&gt;routes&lt;/code&gt; map would look like this:&lt;/p&gt;&lt;pre class="language-clojure"&gt;(def routes
  ;; see the #&amp;apos; in front of the welcome-page-handler
  {&amp;quot;/&amp;quot; #&amp;apos;welcome-page-handler})
&lt;/pre&gt;&lt;p&gt;We can use the &lt;code&gt;routes&lt;/code&gt; map precisely the same way we did earlier, with the exception that changes in the &lt;code&gt;welcome-page-handler&lt;/code&gt; are reflected without re-evaluating the &lt;code&gt;routes&lt;/code&gt; map:&lt;/p&gt;&lt;pre class="language-clojure"&gt;(let [handler-fn (get routes &amp;quot;/&amp;quot;)]
  (handler-fn))

;; #=&amp;gt; {:status 200, :body &amp;quot;Welcome, stranger!&amp;quot;}
&lt;/pre&gt;&lt;p&gt;What happens there is that the value of the &lt;code&gt;routes&lt;/code&gt; map isn&amp;apos;t a function anymore; it&amp;apos;s a &lt;strong&gt;Var&lt;/strong&gt;. Those two aren&amp;apos;t equal unless the Var is dereferenced:&lt;/p&gt;&lt;pre class="language-clojure"&gt;welcome-page-handler
;; #=&amp;gt; #function[user/welcome-page-handler]

#&amp;apos;welcome-page-handler
;; #=&amp;gt; #&amp;apos;user/welcome-page-handler

(= welcome-page-handler #&amp;apos;welcome-page-handler)
;; #=&amp;gt; false

(= welcome-page-handler @#&amp;apos;welcome-page-handler)
;; #=&amp;gt; true
&lt;/pre&gt;&lt;p&gt;Now, if the handler function isn&amp;apos;t a function anymore, but a &lt;strong&gt;Var&lt;/strong&gt;, how can we invoke the function without first deref&amp;apos;ing the Var? The reason is that when &lt;strong&gt;Var&lt;/strong&gt; is used in the function position (the first element in the list), it is deref&amp;apos;d automatically. &lt;strong&gt;Var&lt;/strong&gt; implements &lt;code&gt;IFn&lt;/code&gt; interface.&lt;/p&gt;&lt;p&gt;Because of this, the two examples below behave the same:&lt;/p&gt;&lt;pre class="language-clojure"&gt;(#&amp;apos;welcome-page-handler)
;; #=&amp;gt; {:status 200, :body &amp;quot;Welcome, stranger!&amp;quot;}

(@#&amp;apos;welcome-page-handler)
;; #=&amp;gt; {:status 200, :body &amp;quot;Welcome, stranger!&amp;quot;}
&lt;/pre&gt;&lt;h2&gt;Option 3: Function wrapping&lt;/h2&gt;&lt;p&gt;&lt;strong&gt;Function wrapping&lt;/strong&gt; means that you wrap the handler function with another function, like this:&lt;/p&gt;&lt;pre class="language-clojure"&gt;(def routes
  ;; see the anonymous function #( ) around the welcome-page-handler
  {&amp;quot;/&amp;quot; #(welcome-page-handler)})
&lt;/pre&gt;&lt;p&gt;Out of these two options, Var quoting and function wrapping, the &lt;code&gt;#&amp;apos;&lt;/code&gt; approach is the one I&amp;apos;d prefer in &lt;i&gt;most cases&lt;/i&gt;. It is, in my opinion, less noisy syntax.&lt;/p&gt;&lt;p&gt;However, &lt;code&gt;#&amp;apos;&lt;/code&gt; should be avoided in &lt;a href="https://clojureverse.org/t/our-codebase-has-hundreds-of-derived-vars-how-to-make-it-repl-friendly/8937/4"&gt;CLJS&lt;/a&gt; (and thus also in CLJC).&lt;/p&gt;&lt;h2&gt;How to find all derived Vars?&lt;/h2&gt;&lt;p&gt;Ok, so, now we know what are our options to fix the derived Vars.&lt;/p&gt;&lt;p&gt;But there are still two open questions:&lt;/p&gt;&lt;p&gt;&lt;strong&gt;How can I find all the derived Vars in my codebase?&lt;/strong&gt; Derived Vars are kinda subtle, aren&amp;apos;t they?&lt;/p&gt;&lt;p&gt;We&amp;apos;ve been building our app without worrying about derived Vars for nine years because the reloaded workflow solved the issue. The result is that there are derived Vars here and there in the code base. We have many maps that do some kind of dispatching from a name or a keyword to an appropriate handler function. Derived Vars are sprinkled everywhere.&lt;/p&gt;&lt;p&gt;In addition to finding all the derived Vars, I want solve this issue once and for all. If I now go through our codebase and fix all the derived Vars, I don&amp;apos;t want to do it again later. &lt;strong&gt;How can I prevent anyone from introducing derived Vars in the future?&lt;/strong&gt; As I already mentioned, they are subtle and easily introduced by accident.&lt;/p&gt;&lt;p&gt;I once asked about this in the &lt;a href="https://clojureverse.org/t/our-codebase-has-hundreds-of-derived-vars-how-to-make-it-repl-friendly/8937"&gt;ClojureVerse&lt;/a&gt; discussion forum. The conclusion was that there isn&amp;apos;t an existing solution out there to fix the issue with derived Vars once and for all, but clj-kondo might be able to get me somewhere.&lt;/p&gt;&lt;p&gt;clj-kondo has a nice feature, &lt;a href="https://cljdoc.org/d/clj-kondo/clj-kondo/2023.12.15/doc/hooks"&gt;hooks&lt;/a&gt;, that allows enhancing linting via user-provided code.&lt;/p&gt;&lt;p&gt;And it turned out that it&amp;apos;s possible to write a clj-kondo hook that will catch the derived Vars. Here&amp;apos;s how.&lt;/p&gt;&lt;h2&gt;The hook 🪝&lt;/h2&gt;&lt;p&gt;Ok, let&amp;apos;s get to it. Let&amp;apos;s build the hook, piece by piece.&lt;/p&gt;&lt;p&gt;Let&amp;apos;s start by editing the &lt;code&gt;.clj-kondo/config.edn&lt;/code&gt; file. We need to define the hook and add the linter:&lt;/p&gt;&lt;pre class="language-clojure"&gt;;; .clj-kondo/config.edn

;; &amp;amp;#9312; Analyze both CLJ and CLJS `def`s with `hooks.def/analyze`.
;; This hook will emit `:fn-sym-in-def` findings.
{:hooks {:analyze-call {clojure.core/def hooks.def/analyze
                        cljs.core/def hooks.def/analyze}}

 :linters {
           ;; &amp;amp;#9313; clj-kondo-config linter will complain about 
           ;; unknown linter `fn-sym-in-def`, unless we ignore it.
           #_{:clj-kondo/ignore [:clj-kondo-config]}
 
           ;; &amp;amp;#9314; Set `fn-sym-in-def` level to warning
           :fn-sym-in-def {:level :warning}}}
&lt;/pre&gt;&lt;p&gt;That&amp;apos;s all we need in the config file for now.&lt;/p&gt;&lt;p&gt;In the config file, we&amp;apos;re pointing to &lt;code&gt;hooks.def&lt;/code&gt; namespace. Next, we&amp;apos;ll create it.&lt;/p&gt;&lt;p&gt;Add a new CLJ file in &lt;code&gt;.clj-kondo/hooks/def.clj&lt;/code&gt;, with the following content:&lt;/p&gt;&lt;pre class="language-clojure"&gt;(ns hooks.def
  (:require [clj-kondo.hooks-api :as api]))
  
(defn analyze
  [{:keys [node lang]}]

  ;; &amp;amp;#9312; Print the `node`, just to try things out.
  (println node)

  ;; &amp;amp;#9313; Return the `node` without transforming it.
  node)
&lt;/pre&gt;&lt;p&gt;That&amp;apos;s the skeleton for our hook. You can now try to run &lt;code&gt;clj-kondo&lt;/code&gt; and see the &lt;code&gt;def&lt;/code&gt;s in your project to be printed out:&lt;/p&gt;&lt;pre class="language-bash"&gt;✗ clj-kondo --lint src
&amp;amp;lt;list: (def routes {&amp;quot;/&amp;quot; welcome-page-handler})&amp;amp;gt;
linting took 28ms, errors: 0, warnings: 0
&lt;/pre&gt;&lt;p&gt;Since we are about to analyze the form &lt;code&gt;def&lt;/code&gt;, it&amp;apos;s good to keep in mind that &lt;code&gt;def&lt;/code&gt; can have multiple arities:&lt;/p&gt;&lt;pre class="language-clojure"&gt;;; no initial value
(def foo)

;; initial value
(def bar :bar-value)

;; docstring and initial value
(def quax &amp;quot;Quax docstring&amp;quot; :quax-value)
&lt;/pre&gt;&lt;p&gt;We&amp;apos;re not interested in the first form; we can ignore it. Also, we are not interested in the docstring. We only want to inspect the last child of the node.&lt;/p&gt;&lt;p&gt;Let&amp;apos;s add the logic to the &lt;code&gt;analyze&lt;/code&gt; function to call an auxiliary function &lt;code&gt;analyze*&lt;/code&gt;:&lt;/p&gt;&lt;pre class="language-clojure"&gt;(defn analyze
  [{:keys [node lang]}]

  ;; &amp;amp;#9312; Call `analyze*` with the last child node
  (when (&amp;lt; 1 (count (:children node)))
    (analyze* {:node (last (:children node))
               :lang lang}))

  ;; &amp;amp;#9313; Return the `node` without transforming it.
  node)
&lt;/pre&gt;&lt;p&gt;Now, let&amp;apos;s implement the &lt;code&gt;analyze*&lt;/code&gt; function.&lt;/p&gt;&lt;p&gt;The &lt;code&gt;analyze*&lt;/code&gt; will have a &lt;code&gt;cond&lt;/code&gt; with two branches. Let&amp;apos;s start with the first branch in which we call a &lt;code&gt;fn-token-node?&lt;/code&gt; predicate, and if the node indeed is a token node pointing to a function, we record the finding and recommend a fix based on the language.&lt;/p&gt;&lt;pre class="language-clojure"&gt;(defn- analyze* [{:keys [node lang]}]
  (cond
    
    ;; &amp;amp;#9312; Call predicate function that returns true for symbols pointing to functions
    (fn-token-node? {:node node 
                     :lang lang})
                     
    ;; &amp;amp;#9313; Record finding if predicate returns truthy
    (api/reg-finding! (assoc (meta node)
                             :type :fn-sym-in-def
                             :message (str &amp;quot;fn-sym-in-def: &amp;quot;
                                           node
                                           (if (= :cljs lang)
                                             &amp;quot; - use function wrapping&amp;quot;
                                             &amp;quot; - use var quoting&amp;quot;))))))
&lt;/pre&gt;&lt;p&gt;And now, let&amp;apos;s implement the &lt;code&gt;fn-token-node?&lt;/code&gt; predicate, which is the beef of the hook.&lt;/p&gt;&lt;pre class="language-clojure"&gt;(defn- fn-token-node? [{:keys [node lang]}]
  ;; &amp;amp;#9312; Check that the node is a symbol token node
  (when (and (api/token-node? node)
             (symbol? (:value node)))
             
    ;; &amp;amp;#9313; Resolve the symbol to `ns` and `name`
    (let [{:keys [ns name]} (api/resolve {:name (:value node)})
          
          ;; &amp;amp;#9314; Get the _cached_ analysis data
          analysis (get-in (api/ns-analysis ns {:lang lang}) [lang name])]
          
      ;; &amp;amp;#9315; Ignore `clojure.core` and `cljs.core`
      (when-not (#{&amp;apos;clojure.core
                   &amp;apos;cljs.core} ns)
                   
        ;; &amp;amp;#9316; Return truthy if the analysis data looks like it&amp;apos;s a function
        (some (set (keys analysis)) [:fixed-arities :varargs-min-arity])))))
&lt;/pre&gt;&lt;p&gt;First, we check if the node is a symbol token node.&lt;/p&gt;&lt;p&gt;After that, we use clj-kondo&amp;apos;s &lt;code&gt;api/resolve&lt;/code&gt; function to resolve the namespace and the name of the symbol.&lt;/p&gt;&lt;p&gt;Then, we use the resolved namespace and name to get the &lt;strong&gt;cached&lt;/strong&gt; analysis data from clj-kondo.&lt;/p&gt;&lt;p&gt;Next, we filter out &lt;code&gt;clojure.core&lt;/code&gt; and &lt;code&gt;cljs.core&lt;/code&gt; namespaces.&lt;/p&gt;&lt;p&gt;If the returned analysis data looks like a function (i.e. it has either &lt;code&gt;:fixed-arities&lt;/code&gt; or &lt;code&gt;:varargs-min-arity&lt;/code&gt; key), we return truthy value from the predicate.&lt;/p&gt;&lt;p&gt;Now, we do want to do a bit of traversing in case the value is a Clojure collection. Only then we can catch the example case shown earlier we have a routing map from keys to handler functions. So let&amp;apos;s add another predicate function and another branch to the &lt;code&gt;cond&lt;/code&gt; and resursively traverse:&lt;/p&gt;&lt;pre class="language-clojure"&gt;(defn- traverse? [{:keys [node]}]
  (or
   (api/vector-node? node)
   (api/map-node? node)
   (api/set-node? node)))

(defn- analyze* [{:keys [node lang]}]
  (cond
    (fn-token-node? {:node node 
                     :lang lang})
    (api/reg-finding! (assoc (meta node)
                             :type :fn-sym-in-def
                             :message (str &amp;quot;fn-sym-in-def: &amp;quot;
                                           node
                                           (if (= :cljs lang)
                                             &amp;quot; - use function wrapping&amp;quot;
                                             &amp;quot; - use var quoting&amp;quot;))))
                          
    ;; &amp;amp;#9312; Check if we should recursively traverse
    (traverse? {:node node}) 
    
    ;; &amp;amp;#9313; Traverse all child nodes.
    ;; Note the `doall` which is needed because `reg-finding!` is
    ;; a side-effecting function inside `map`
    (doall (map #(analyze* % lang) (:children node)))))
&lt;/pre&gt;&lt;p&gt;Alright, that&amp;apos;s pretty much all we need! Now we can try it out to see if it catches the derived Vars in a router map.&lt;/p&gt;&lt;pre class="language-bash"&gt;➜  clj-kondo --lint src
src/core.clj:8:8: warning: fn-sym-in-def: welcome-page-handler - use var quoting
linting took 27ms, errors: 0, warnings: 1
&lt;/pre&gt;&lt;p&gt;That&amp;apos;s exactly what we wanted to see! We got a warning saying that &lt;code&gt;welcome-page-handler&lt;/code&gt; needs to be Var quoted.&lt;/p&gt;&lt;p&gt;To see the whole thing, have a look at the example repository at &lt;a href="https://github.com/rap1ds/derived-vars-clj-kondo-hook"&gt;https://github.com/rap1ds/derived-vars-clj-kondo-hook&lt;/a&gt;&lt;/p&gt;&lt;h2&gt;Caveats&lt;/h2&gt;&lt;p&gt;There are some caveats in the hook:&lt;/p&gt;&lt;p&gt;First, the hook uses &lt;code&gt;clj-kondo.hooks-api/ns-analysis&lt;/code&gt;, which utilizes clj-kondo cache. The problem is that if the cache is empty, you get false negative results, i.e. you don&amp;apos;t get warnings of derived Vars.&lt;/p&gt;&lt;p&gt;When you&amp;apos;re developing on your local environment, you likely have a populated clj-kondo cache. However, in a CI environment, you will probably start from a clean slate with an empty cache. The best workaround that I&amp;apos;m aware of is to run clj-kondo twice. The first time to populate the cache, and then the second time to do the actual linting. Obviously, this doubles the time it takes to lint the project. Luckily, clj-kondo is pretty fast.&lt;/p&gt;&lt;p&gt;The second caveat is that the traversing part is rather naive and only supports the most common Clojure data types. If you wrap the Var in e.g. &lt;code&gt;when&lt;/code&gt; or &lt;code&gt;partial&lt;/code&gt; etc, you won&amp;apos;t get a linter warning. However, I feel that the current implementation is good enough and catches the most common cases.&lt;/p&gt;&lt;h2&gt;Conclusion&lt;/h2&gt;&lt;p&gt;Derived Vars are, in my opinion, one of the most biggest annoyance in the REPL-driven development workflow.&lt;/p&gt;&lt;p&gt;We can use Var quoting or function wrapping to make derived Vars REPL friendly in a large codebase where namespace reloading with &lt;code&gt;tools.namespace&lt;/code&gt; is too slow.&lt;/p&gt;&lt;p&gt;It&amp;apos;s quite easy to introduce derived Vars by accident, so a solution like linter check needs to be implemented to make sure we don&amp;apos;t accidentally introduce them.&lt;/p&gt;&lt;p&gt;clj-kondo hooks are extremely powerful and can be used to analyze &lt;code&gt;def&lt;/code&gt; forms, register findings, and report warnings for derived Vars.&lt;/p&gt;&lt;p&gt;With the help of the clj-kondo hook, I was able to identify and fix over 300 derived Vars from our codebase. The codebase is now a bit more friendly for REPL-driven development!&lt;/p&gt;&lt;p&gt;--&lt;/p&gt;&lt;p&gt;If you have any questions or comments, I&amp;apos;d love to hear! &lt;a href="https://mastodon.social/@rap1ds/111875074026277313"&gt;Please leave a comment on Mastodon&lt;/a&gt;.&lt;/p&gt;&lt;/div&gt;</description></item><item><guid isPermaLink="false">https://www.mikkokoski.com/blog/set-custom-equality/index.html</guid><title>Clojure set with custom equality semantics</title><link>https://www.mikkokoski.com/blog/set-custom-equality/index.html</link><pubDate>Thu, 07 Sep 2023 19:34:47 +0000</pubDate><description>&lt;div&gt;&lt;p&gt;&lt;a href="https://clojure.org/reference/data_structures#Sets"&gt;Sets are collections of unique values.&lt;/a&gt;&lt;/p&gt;&lt;p&gt;To be able to eliminate the multiplicates, the &lt;a href="https://clojuredocs.org/clojure.core/set"&gt;&lt;code&gt;set&lt;/code&gt;&lt;/a&gt; needs to know when two values are equal.&lt;/p&gt;&lt;p&gt;Sets use the standard &lt;a href="https://clojure.org/guides/equality"&gt;Clojure equality semantics&lt;/a&gt;, meaning that if two values are &lt;code&gt;=&lt;/code&gt;, then it removes the multiplicates.&lt;/p&gt;&lt;p&gt;Example:&lt;/p&gt;&lt;pre class="language-clojure"&gt;(= :foo :foo)
;; #=&amp;gt; true

#{:foo :bar :foo}
;; #=&amp;gt; #{:foo :bar}
;; One of the two :foo values is removed because they are equal (`=`)
&lt;/pre&gt;&lt;p&gt;&lt;strong&gt;But what if you want to have a set with custom equality semantics?&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;Say you have a list of person entities, where each person has a &lt;code&gt;:person/id&lt;/code&gt; attribute that identifies the person. When you put the person entities in a set, you want entities with unique &lt;code&gt;:person/id&lt;/code&gt;s, no matter what other data the entity contains.&lt;/p&gt;&lt;p&gt;Example:&lt;/p&gt;&lt;pre class="language-clojure"&gt;#{{:person/id 1
   :person/firstName &amp;quot;Mikko&amp;quot;}
  {:person/id 2
   :person/firstName &amp;quot;John&amp;quot;
   :person/lastName &amp;quot;Doe&amp;quot;}
  {:person/id 1
   :person/lastName &amp;quot;Koski&amp;quot;}} 
;; #=&amp;gt; #{#:person{:id 1, :firstName &amp;quot;Mikko&amp;quot;}
;;       #:person{:id 1, :lastName &amp;quot;Koski&amp;quot;}
;;       #:person{:id 2, :firstName &amp;quot;John&amp;quot;, :lastName &amp;quot;Doe&amp;quot;}}

;; Note that there are two entities with `:person/id` 1, which is not what we want.
&lt;/pre&gt;&lt;p&gt;How could we create a &lt;a href="https://clojuredocs.org/clojure.core/set"&gt;&lt;code&gt;set&lt;/code&gt;&lt;/a&gt; that uses the &lt;code&gt;:person/id&lt;/code&gt; attribute to decide if two values are equal?&lt;/p&gt;&lt;h2&gt;Custom equality with &lt;a href="https://clojuredocs.org/clojure.core/sorted-set-by"&gt;&lt;code&gt;sorted-set-by&lt;/code&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;Sets are unordered data structures, but you can create ordered sets with &lt;a href="https://clojuredocs.org/clojure.core/sorted-set"&gt;&lt;code&gt;sorted-set&lt;/code&gt;&lt;/a&gt;. Using &lt;a href="https://clojuredocs.org/clojure.core/sorted-set-by"&gt;&lt;code&gt;sorted-set-by&lt;/code&gt;&lt;/a&gt;, you can have a sorted set with your own comparator for sorting.&lt;/p&gt;&lt;p&gt;The &lt;a href="https://clojuredocs.org/clojure.core/sorted-set"&gt;&lt;code&gt;sorted-set&lt;/code&gt;&lt;/a&gt; has another interesting property: In addition to the sort order, the comparator also defines the equality semantics, meaning that if the comparator thinks that two values are equal (i.e., the comparator returns &lt;code&gt;0&lt;/code&gt;), then the set threats them as equal values, that is, it removes multiplicates.&lt;/p&gt;&lt;p&gt;So for example, we can make a comparator that compares only the &lt;code&gt;:person/id&lt;/code&gt; attribute and return &lt;code&gt;0&lt;/code&gt; for two maps with the same &lt;code&gt;:person/id&lt;/code&gt;, regarless of the other keys and values in the map:&lt;/p&gt;&lt;pre class="language-clojure"&gt;(defn compare-id [x y]
  (compare (:person/id x) (:person/id y)))
  
(compare-id
 {:person/id 1
  :person/firstName &amp;quot;Mikko&amp;quot;}
 {:person/id 1
  :person/lastName &amp;quot;Koski&amp;quot;})
;; #=&amp;gt; 0
;; Zero means the values are equal from comparator point-of-view
&lt;/pre&gt;&lt;p&gt;And if we use &lt;code&gt;compare-id&lt;/code&gt; as the comparator with &lt;a href="https://clojuredocs.org/clojure.core/sorted-set-by"&gt;&lt;code&gt;sorted-set-by&lt;/code&gt;&lt;/a&gt;, we get the result we wanted:&lt;/p&gt;&lt;pre class="language-clojure"&gt;(def s
  (sorted-set-by
   compare-id
   {:person/id 1
    :person/firstName &amp;quot;Mikko&amp;quot;}
   {:person/id 2
    :person/firstName &amp;quot;John&amp;quot;}
   {:person/id 1
    :person/lastName &amp;quot;Koski&amp;quot;}))

s
;; #=&amp;gt; #{#:person{:id 1, :firstName &amp;quot;Mikko&amp;quot;} 
         #:person{:id 2, :firstName &amp;quot;John&amp;quot;}}
&lt;/pre&gt;&lt;h2&gt;Do sets with custom equality work with &lt;a href="https://clojuredocs.org/clojure.core/disj"&gt;&lt;code&gt;disj&lt;/code&gt;&lt;/a&gt;, &lt;a href="https://clojuredocs.org/clojure.core/contains_q"&gt;&lt;code&gt;contains?&lt;/code&gt;&lt;/a&gt; and &lt;a href="https://clojuredocs.org/clojure.core/contains_q"&gt;&lt;code&gt;get&lt;/code&gt;&lt;/a&gt;?&lt;/h2&gt;&lt;p&gt;Yes, they do.&lt;/p&gt;&lt;p&gt;These functions are documented as follows in Clojure reference:&lt;/p&gt;&lt;blockquote&gt;&lt;p&gt;Sets support &amp;apos;removal&amp;apos; with disj, as well as contains? and get, the latter returning the object that is held in the set which compares equal to the key, if found:&lt;/p&gt;&lt;p&gt;-- &lt;a href="https://clojure.org/reference/data_structures#Sets"&gt;Clojure - Data Structures&lt;/a&gt;&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;Here&amp;apos;s an example that demonstrates that everything works as documented:&lt;/p&gt;&lt;pre class="language-clojure"&gt;(disj s {:person/id 1 :person/lastName &amp;quot;Koski&amp;quot;})
;; #=&amp;gt; #{#:person{:id 2, :firstName &amp;quot;John&amp;quot;}}

(contains? s {:person/id 1 :person/lastName &amp;quot;Koski&amp;quot;})
;; #=&amp;gt; true

(get s {:person/id 1 :person/lastName &amp;quot;Koski&amp;quot;})
;; #=&amp;gt; #:person{:id 1, :firstName &amp;quot;Mikko&amp;quot;}
&lt;/pre&gt;&lt;h2&gt;What about &lt;a href="https://clojuredocs.org/clojure.set"&gt;&lt;code&gt;clojure.set&lt;/code&gt;&lt;/a&gt; operators?&lt;/h2&gt;&lt;p&gt;So now that we&amp;apos;ve set up our set of person entities with custom equality by &lt;code&gt;:person/id&lt;/code&gt; and tested that the essential functions &lt;a href="https://clojuredocs.org/clojure.core/disj"&gt;&lt;code&gt;disj&lt;/code&gt;&lt;/a&gt;, &lt;a href="https://clojuredocs.org/clojure.core/contains_q"&gt;&lt;code&gt;contains?&lt;/code&gt;&lt;/a&gt; and &lt;a href="https://clojuredocs.org/clojure.core/contains_q"&gt;&lt;code&gt;get&lt;/code&gt;&lt;/a&gt; work as expected, the next question is, do the set operations provided by the &lt;a href="https://clojuredocs.org/clojure.set"&gt;&lt;code&gt;clojure.set&lt;/code&gt;&lt;/a&gt; namespace work with sets with custom equality?&lt;/p&gt;&lt;p&gt;The answer is &lt;strong&gt;yes&lt;/strong&gt;, but with one caveat:&lt;/p&gt;&lt;p&gt;&lt;strong&gt;Do not mix different set types.&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;If we look at the &lt;a href="https://github.com/clojure/clojure/blob/6975553804b0f8da9e196e6fb97838ea4e153564/src/clj/clojure/set.clj"&gt;source code of clojure.set&lt;/a&gt;, we can see that the functions don&amp;apos;t create new sets for the return values; instead, they reuse the ones given as parameters. This is good news because we don&amp;apos;t want to lose our custom equality semantics after using the &lt;a href="https://clojuredocs.org/clojure.set"&gt;&lt;code&gt;clojure.set&lt;/code&gt;&lt;/a&gt; operations.&lt;/p&gt;&lt;p&gt;However, the functions in &lt;a href="https://clojuredocs.org/clojure.set"&gt;&lt;code&gt;clojure.set&lt;/code&gt;&lt;/a&gt; use &lt;a href="https://clojuredocs.org/clojure.core/count"&gt;&lt;code&gt;count&lt;/code&gt;&lt;/a&gt; of the given sets to determine which one will be used as a base for the return value. You shouldn&amp;apos;t rely on this logic, so it&amp;apos;s safest to use two sets of the same type.&lt;/p&gt;&lt;p&gt;Let&amp;apos;s take an example.&lt;/p&gt;&lt;p&gt;First, let&amp;apos;s call &lt;a href="https://clojuredocs.org/clojure.set/intersection"&gt;&lt;code&gt;clojure.set/intersection&lt;/code&gt;&lt;/a&gt; with a sorted set of two values and a normal unsorted set of one value:&lt;/p&gt;&lt;pre class="language-clojure"&gt;(def result-when-sorted-set-has-more-values
  (clojure.set/intersection
   (sorted-set-by
    compare-id
    {:person/id 1
     :person/firstName &amp;quot;Mikko&amp;quot;}
    {:person/id 2
     :person/firstName &amp;quot;John&amp;quot;})
   #{{:person/id 2
      :person/lastName &amp;quot;Doe&amp;quot;}}))

result-when-sorted-set-has-more-values
;; #=&amp;gt; #{#:person{:id 2, :lastName &amp;quot;Doe&amp;quot;}}

(sorted? result-when-sorted-set-has-more-values)
;; #=&amp;gt; false

(contains? result-when-sorted-set-has-more-values {:person/id 2})
;; #=&amp;gt; false
&lt;/pre&gt;&lt;p&gt;In the example above, the returned set has the value we expected but is not sorted, nor does it use the equality by &lt;code&gt;:person/id&lt;/code&gt;.&lt;/p&gt;&lt;p&gt;Next, let&amp;apos;s have a sorted set of one value and an unsorted set of two values:&lt;/p&gt;&lt;pre class="language-clojure"&gt;(def result-when-unsorted-set-has-more-values
  (clojure.set/intersection
   (sorted-set-by
    compare-id
    {:person/id 2
     :person/firstName &amp;quot;John&amp;quot;})
   #{{:person/id 1
      :person/firstName &amp;quot;Mikko&amp;quot;}
     {:person/id 2
      :person/lastName &amp;quot;Doe&amp;quot;}}))

result-when-unsorted-set-has-more-values
;; #=&amp;gt; #{}

(sorted? result-when-unsorted-set-has-more-values)
;; #=&amp;gt; true

(contains? result-when-unsorted-set-has-more-values {:person/id 2})
;; #=&amp;gt; false
&lt;/pre&gt;&lt;p&gt;Now we did got back a sorted set, but the set didn&amp;apos;t have any values, which is not what we wanted.&lt;/p&gt;&lt;p&gt;To get the correct result and correct set type, we have to use the same set type for both arguments:&lt;/p&gt;&lt;pre class="language-clojure"&gt;(def result-when-both-sets-are-sorted
  (clojure.set/intersection
   (sorted-set-by
    compare-id
    {:person/id 2
     :person/firstName &amp;quot;John&amp;quot;})
   (sorted-set-by
    compare-id
    {:person/id 1
     :person/firstName &amp;quot;Mikko&amp;quot;}
    {:person/id 2
     :person/lastName &amp;quot;Doe&amp;quot;})))

result-when-both-sets-are-sorted
;; #=&amp;gt; #{#:person{:id 2, :firstName &amp;quot;John&amp;quot;}}

(sorted? result-when-both-sets-are-sorted)
;; #=&amp;gt; true

(contains? result-when-both-sets-are-sorted {:person/id 2})
;; #=&amp;gt; true
&lt;/pre&gt;&lt;h2&gt;Conclusion&lt;/h2&gt;&lt;p&gt;Using the &lt;a href="https://clojuredocs.org/clojure.core/sorted-set-by"&gt;&lt;code&gt;sorted-set-by&lt;/code&gt;&lt;/a&gt; with custom equality semantics was a fun thing to learn, but I&amp;apos;m still unsure if there are any real world use-cases out there where this would be &lt;i&gt;actually&lt;/i&gt; useful.&lt;/p&gt;&lt;p&gt;In the examples above, I had two sets of person entities with slightly different attributes, and the aim was to eliminate the duplicates. In reality, I&amp;apos;d probably do that with something like &lt;a href="https://ask.clojure.org/index.php/11090/add-distinct-by-to-clojure-core"&gt;&lt;code&gt;distinct-by&lt;/code&gt; (if it existed)&lt;/a&gt;, or I&amp;apos;d use &lt;a href="https://clojuredocs.org/clojure.core/group-by"&gt;&lt;code&gt;group-by&lt;/code&gt;&lt;/a&gt; and then map the &lt;a href="https://clojuredocs.org/clojure.core/vals"&gt;&lt;code&gt;vals&lt;/code&gt;&lt;/a&gt; with &lt;a href="https://clojuredocs.org/clojure.core/merge"&gt;&lt;code&gt;merge&lt;/code&gt;&lt;/a&gt; to get distinct entities with the attributes merged, which is probably what I&amp;apos;d want anyway:&lt;/p&gt;&lt;pre class="language-clojure"&gt;(-&amp;gt;&amp;gt; 
 [{:person/id 1
  :person/firstName &amp;quot;Mikko&amp;quot;}
  {:person/id 2
   :person/firstName &amp;quot;John&amp;quot;}
  {:person/id 1
   :person/lastName &amp;quot;Koski&amp;quot;}]
 (group-by :person/id)
 vals
 (map #(apply merge %)))
;; #=&amp;gt; (#:person{:id 1, :firstName &amp;quot;Mikko&amp;quot;, :lastName &amp;quot;Koski&amp;quot;}
;;      #:person{:id 2, :firstName &amp;quot;John&amp;quot;})
&lt;/pre&gt;&lt;p&gt;But maybe if I&amp;apos;d need to do actual set operations (&lt;a href="https://clojuredocs.org/clojure.set/difference"&gt;&lt;code&gt;difference&lt;/code&gt;&lt;/a&gt;, &lt;a href="https://clojuredocs.org/clojure.set/intersection"&gt;&lt;code&gt;intersection&lt;/code&gt;&lt;/a&gt; etc.) with the two sets of entities, then I&amp;apos;d maybe use the &lt;a href="https://clojuredocs.org/clojure.core/sorted-set-by"&gt;&lt;code&gt;sorted-set-by&lt;/code&gt;&lt;/a&gt; with custom equality. Maybe.&lt;/p&gt;&lt;p&gt;What do you think? Is there any real-world use for this?&lt;/p&gt;&lt;p&gt;Please let me know and &lt;a href="https://mastodon.social/@rap1ds/111025587359537436"&gt;comment on Mastodon!&lt;/a&gt;&lt;/p&gt;&lt;/div&gt;</description></item><item><guid isPermaLink="false">https://www.mikkokoski.com/blog/8-tips-for-advent-of-code-2022/index.html</guid><title>8 tips for Advent of Code 2022 🎄</title><link>https://www.mikkokoski.com/blog/8-tips-for-advent-of-code-2022/index.html</link><pubDate>Wed, 23 Nov 2022 17:46:00 +0000</pubDate><description>&lt;p&gt;Advent of Code 2022 starts in 8 days!&lt;/p&gt;&lt;p&gt;I&amp;apos;ve been participating in AoC since 2017. My highlight was in 2020 when
   I &lt;a href="https://github.com/rap1ds/advent-of-code-2020"&gt;solved all
   the puzzles&lt;/a&gt; and received 50/50 stars ⭐️ (though that didn&amp;apos;t happen in
   December 2020 but only much later in 2021).&lt;/p&gt;&lt;p&gt;Since 2017, I&amp;apos;ve learned a lot. I&amp;apos;m sharing some tips/notes to self for
      the upcoming AoC 2022.&lt;/p&gt;&lt;p&gt;Here are eight tips for AoC 2022:&lt;/p&gt;&lt;h2&gt;1. Keep it fun!&lt;/h2&gt;&lt;p&gt;Let&amp;apos;s start with the most boring yet important tip.&lt;/p&gt;&lt;p&gt;Remember to keep it fun! Don&amp;apos;t stress if you can&amp;apos;t solve a puzzle in a
      day. Don&amp;apos;t let it ruin your Christmas! Christmas time can be stressful enough
      even without AoC.&lt;/p&gt;&lt;p&gt;Also, don&amp;apos;t stress about the leaderboard. People who are on the
      leaderboard have practiced these kinds of coding puzzles for years.&lt;/p&gt;&lt;h2&gt;2. Use the right data structure&lt;/h2&gt;&lt;p&gt;First &amp;quot;real&amp;quot; coding tip:&lt;/p&gt;&lt;p&gt;Using the right data structure might make solving a challenging puzzle a
      piece of cake.&lt;/p&gt;&lt;p&gt;Let&amp;apos;s take an example: &lt;/p&gt;&lt;ul&gt;&lt;li&gt;You need to have an XY-coordinate system, a grid&lt;/li&gt;&lt;li&gt;The grid has an infinite size&lt;/li&gt;&lt;li&gt;Initial value for point &lt;code&gt;[0, 0]&lt;/code&gt; is &lt;code&gt;1&lt;/code&gt;&lt;/li&gt;&lt;li&gt;Initial value for everything else is &lt;code&gt;0&lt;/code&gt;&lt;/li&gt;&lt;li&gt;You need to have fast random access (e.g., quickly access a value of any given XY-point)&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;What would be a proper data structure for this?&lt;/p&gt;&lt;p&gt;At first, by intuition, you might think about vectors in a vector (like a
                                                                               2D array) for an XY-coordinate system. But things get tricky due to the other
      requirements, i.e., infinite size.&lt;/p&gt;&lt;p&gt;A perfect data structure for this is a map from XY-points to values.&lt;/p&gt;&lt;pre class="language-clojure"&gt;;; Initial value for 0,0 is 1
      (def grid {[0 0] 1})

      ;; Fast random access and an initial value for everything else than 0,0 is 0
      (get grid [0 0] 0) ;; #=&amp;gt; 1
      (get grid [12 34] 0) ;; #=&amp;gt; 0
      (get grid [-56 78] 0) ;; #=&amp;gt; 0

      ;; Infinite size
      (def new-grid (assoc [100 100] grid 100))
      ;; #=&amp;gt; {[0 0] 1, [100 100] 100}&lt;/pre&gt;&lt;p&gt;Another useful data structure is a &lt;code&gt;set&lt;/code&gt;. You&amp;apos;ll find plenty of
   opportunities to use sets in AoC puzzles.&lt;/p&gt;&lt;h2&gt;3. How to make your solution fast? Tip 1/2: &lt;code&gt;memoize&lt;/code&gt;&lt;/h2&gt;&lt;p&gt;AoC puzzles have two parts.&lt;/p&gt;&lt;p&gt;A typical puzzle pattern is this: The first part is easy to solve. You
   can easily craft a naive, brute-force implementation that does the trick.&lt;/p&gt;&lt;p&gt;The second part is such that you need to change the first part only a
   bit. It looks effortless until you run it and notice that it takes forever to
   finish.&lt;/p&gt;&lt;p&gt;For example, in the first part, you need to define a step function (a
   function that takes as an input the output of the previous step function
   execution) and run the step 100 times for a given input. Running the step
   function 100 times takes couple of seconds.&lt;/p&gt;&lt;p&gt;In the second part, you need to run it 1 000 000 times. You notice that
   this will take hours to complete.&lt;/p&gt;&lt;p&gt;How to make your solution run faster for the second part?&lt;/p&gt;&lt;p&gt;One helpful strategy is memoization: Make your function pure (as we
   default in Clojure) and throw some memoize in the mix. That itself might make
   the function fast enough.&lt;/p&gt;&lt;h2&gt;4. How to make your solution faster? Tip 2/2: Find a repeating pattern&lt;/h2&gt;&lt;p&gt;Not all solutions can be memoized. In those cases, you need to figure out
   something else.&lt;/p&gt;&lt;p&gt;One thing you can look for is to find a repeating pattern.&lt;/p&gt;&lt;p&gt;Say you have a step function, and you&amp;apos;ve been asked what the result of
   running that step function 549027529283424023592234980235235 times (or some
   other ridiculously high number) is. You can&amp;apos;t just repeatedly run the
   function that many times, or it will take years to execute.&lt;/p&gt;&lt;p&gt;It&amp;apos;s possible that you can find a repeating pattern where the output of,
   say, the 12514th execution is the same as the output of the first run. You
   can now calculate 549027529283424023592234980235235 modulo 12514 and figure
   out that it is enough to run the step function only 8633 times to get the
   result.&lt;/p&gt;&lt;h2&gt;5. Make heavy use of the examples&lt;/h2&gt;&lt;p&gt;Each AoC puzzle will provide examples that are way smaller than the
   actual puzzle input.&lt;/p&gt;&lt;p&gt;Make sure you test your implementation with &lt;i&gt;_all_&lt;/i&gt; of the
   provided examples before even trying to run your solution with the actual
   input.&lt;/p&gt;&lt;h2&gt;6. Put inputs and examples in &lt;code&gt;resources/&lt;/code&gt;&lt;/h2&gt;&lt;p&gt;&lt;a href="https://www.reddit.com/r/adventofcode/comments/e7khy8/comment/fa13hb9"&gt;The
   organizers of AoC recommend that people refrain from pushing their puzzle
   inputs to public version control like Github.&lt;/a&gt;&lt;/p&gt;&lt;p&gt;I organized my AoC project so that I put the inputs and examples in
   the &lt;code&gt;resources/&lt;/code&gt; directory and &lt;code&gt;.gitignore&lt;/code&gt; it.&lt;/p&gt;&lt;p&gt;I think it&amp;apos;s helpful to have the examples also in
   the &lt;code&gt;resources/&lt;/code&gt; so they are easy to use from there.&lt;/p&gt;&lt;p&gt;Also, it helps if you have &lt;a href="https://github.com/rap1ds/advent-of-code-2020/blob/main/src/util.clj"&gt;a
   helper function to read the file lines from the &lt;code&gt;resources/&lt;/code&gt;&lt;/a&gt;.&lt;/p&gt;&lt;h2&gt;7. Keep your code modular&lt;/h2&gt;&lt;p&gt;Because AoC puzzles have two parts, it&amp;apos;s a good idea to keep your
   solution of the first part modular and split it into small functions that you
   can replace for the second part.&lt;/p&gt;&lt;p&gt;I&amp;apos;ve noticed that I often use threading macro &lt;code&gt;-&amp;gt;&lt;/code&gt;. A typical
   puzzle solution might look something like this:&lt;/p&gt;&lt;pre class="language-clojure"&gt;(-&amp;gt; (read-input-from-resources &amp;quot;name-of-the-file&amp;quot;)
    (parse-input)
    (calculate-one-thing)
    (calculate-another-thing)
    (calculate-the-final-checksum))&lt;/pre&gt;&lt;p&gt;Many times the requirements for part 2 are such that you need to change
   only one of these functions. A typical solution for part 2 looks something
   like this:&lt;/p&gt;&lt;pre class="language-clojure"&gt;(-&amp;gt; (read-input-from-resources &amp;quot;name-of-the-file&amp;quot;)
    (parse-input)
    (calculate-one-thing)
    (calculate-another-thing-a-bit-differently-for-part-2)
    (calculate-the-final-checksum))&lt;/pre&gt;&lt;h2&gt;8. If you can&amp;apos;t solve a puzzle, it doesn&amp;apos;t mean that you&amp;apos;re a lousy
   coder&lt;/h2&gt;&lt;p&gt;Final yet crucial tip (repeating the tip #1 a bit)&lt;/p&gt;&lt;p&gt;Don&amp;apos;t feel bad if you can&amp;apos;t solve a puzzle. It doesn&amp;apos;t mean that you&amp;apos;re a
   terrible coder. More likely, it means that you need to practice solving
   puzzles.&lt;/p&gt;&lt;p&gt;When we do our work as software engineers, we aren&amp;apos;t solving puzzles on a
   daily basis. We are solving real business problems. Solving puzzles is very
   different from solving business problems and requires different skills. And
   you&amp;apos;ll learn those skills by solving more puzzles.&lt;/p&gt;&lt;p&gt;I&amp;apos;ve witnessed this myself. When I started doing AoC, I managed to do
   only a few days and then got stuck. The puzzles just felt way too tricky.&lt;/p&gt;&lt;p&gt;And this felt bad. I thought I knew how to code!&lt;/p&gt;&lt;p&gt;But in 2020, when I completed all the puzzles, many of the puzzles&amp;apos;
   problems felt familiar and easy. The same patterns repeat, year after year,
   with only slight changes. I recognized how to solve a problem because I&amp;apos;d
   seen that same problem in earlier years.&lt;/p&gt;&lt;p&gt;So, I&amp;apos;m repeating myself, but this is important: &lt;strong&gt;Keep it fun,
   and don&amp;apos;t feel bad if you can&amp;apos;t solve all of them.&lt;/strong&gt;&lt;/p&gt;</description></item><item><guid isPermaLink="false">https://www.mikkokoski.com/blog/clojurescript-repl-workflow-with-figwheel/index.html</guid><title>ClojureScript REPL Workflow with Figwheel</title><link>https://www.mikkokoski.com/blog/clojurescript-repl-workflow-with-figwheel/index.html</link><pubDate>Tue, 15 Nov 2022 17:50:00 +0000</pubDate><description>&lt;p&gt;Roman Liutikov wrote a great piece on bringing &lt;a href="https://dev.to/romanliutikov/clojurescript-repl-workflow-427a"&gt;REPL
   workflow to ClojureScript.&lt;/a&gt;&lt;/p&gt;&lt;p&gt;In the article, Roman shows a neat trick to override ClojureScript&amp;apos;s built-in Browser REPL function for evaluating JavaScript code. The overridden version calls a &lt;code&gt;render&lt;/code&gt; function, which will rerender the app after each REPL evaluation. You&amp;apos;ll see the changes in your UI component &lt;strong&gt;immediately&lt;/strong&gt; after you&amp;apos;ve evaluated them in REPL.&lt;/p&gt;&lt;p&gt;But hey, what about Figwheel? I&amp;apos;m not using Browser REPL. I&amp;apos;m using Figwheel! How could I do the same with Figwheel?&lt;/p&gt;&lt;h2&gt;Rerendering after eval in Figwheel&lt;/h2&gt;&lt;p&gt;The function that needs to be overridden in Figwheel is &lt;a href="https://github.com/bhauman/figwheel-repl/blob/1571a6374d9b38ffdc072fbc850ec0110368f64d/src/figwheel/repl.cljc#L346"&gt;&lt;code&gt;figwheel.repl.eval-javascript**&lt;/code&gt;&lt;/a&gt;&lt;/p&gt;&lt;p&gt;If we take Roman&amp;apos;s snippet as a base and modify it a bit for Figwheel, we
   get this:&lt;/p&gt;&lt;pre class="language-clojure"&gt;(comment
  (let [eval-js (.. js/figwheel -repl -eval_javascript_STAR__STAR_)]
    (set! (.. js/figwheel -repl -evaluate_javascript_STAR__STAR_)
          (fn [&amp;amp; args]
            (let [ret (apply eval-js args)]
              (when (= :success (:status ret))
                (render)) ;; your render function
              ret)))))&lt;/pre&gt;&lt;p&gt;Call this function once to set it up and start evaluating changes in the REPL. You&amp;apos;ll see your app
   rerendering and reflecting all UI changes immediately.&lt;/p&gt;&lt;h2&gt;Async rerendering&lt;/h2&gt;&lt;p&gt;If you notice, like I did, that Figwheel REPL starts to time out quite often, your rendering function execution is taking too long.&lt;/p&gt;&lt;p&gt;I was able to fix this by making the rerendering async.&lt;/p&gt;&lt;p&gt;Here&amp;apos;s an improved version:&lt;/p&gt;&lt;pre class="language-clojure"&gt;(comment
  (let [eval-js (.. figwheel -repl -eval_javascript_STAR__STAR_)]
    (set! (.. js/figwheel -repl -evaluate_javascript_STAR__STAR_)
          (fn [&amp;amp; args]
            (let [ret (apply eval-js args)]
              (when (= :success (:status ret))
                (js/setTimeout ;; async rerender
                 (fn [] (render)))) ;; your render function
              ret))))))&lt;/pre&gt;&lt;h2&gt;But why?&lt;/h2&gt;&lt;p&gt;Figwheel already implements code hot-loading, so why is this needed?&lt;/p&gt;&lt;p&gt;When the project size grows, Figwheel code hot-loading start to take some time. It&amp;apos;s not terrible, but it&amp;apos;s not fast, either. It takes seconds for Figwheel to catch up on the newest changes after the file is saved.&lt;/p&gt;&lt;p&gt;The REPL method instead is &lt;strong&gt;instant&lt;/strong&gt;, and that feels pretty great.&lt;/p&gt;&lt;p&gt;My experience has been that rerendering after REPL eval works seamlessly with Figwheel&amp;apos;s own hot-loading. You might first eval the change in REPL, see it rerendered, then save the file and soon see the ClojureScript logo on the corner indicating that Figwheel has also caught up on the latest changes.&lt;/p&gt;</description></item><item><guid isPermaLink="false">https://www.mikkokoski.com/blog/clojurescript-update-from-1-10-439-to-1-11-60/index.html</guid><title>ClojureScript update from 1.10.439 to 1.11.60</title><link>https://www.mikkokoski.com/blog/clojurescript-update-from-1-10-439-to-1-11-60/index.html</link><pubDate>Thu, 25 Aug 2022 19:34:00 +0000</pubDate><description>&lt;p&gt;We recently upgraded our ClojureScript version from 1.10.439 to 1.11.60.
   This is a rather big jump. 1.10.439 was released on Nov 2, 2018, while 1.11.60
   was released on Jun 23, 2022. That&amp;apos;s almost three and a half years! Although
   the update went well, there were few things that needed special attention.&lt;/p&gt;&lt;h2&gt;Long.fromString instead of Long/fromString&lt;/h2&gt;&lt;p&gt;The way how we called the &lt;code&gt;fromString&lt;/code&gt; function
   from &lt;code&gt;goog.math.Long&lt;/code&gt; stopped working. We got a compilation error
   saying that &lt;code&gt;fromString&lt;/code&gt; didn&amp;apos;t exist.&lt;/p&gt;&lt;p&gt;Here&amp;apos;s how we used to require and call the function:&lt;/p&gt;&lt;pre class="language-clojure"&gt;
(ns &amp;quot;my.namespace&amp;quot;
  (:require [goog.math.Long]))

(goog.math.Long/fromString &amp;quot;123&amp;quot;)&lt;/pre&gt;&lt;p&gt;After CLJS upgrade, the compiler threw an error saying that &lt;code&gt;fromString&lt;/code&gt; doesn&amp;apos;t exist.&lt;/p&gt;&lt;p&gt;We changed the code to this:&lt;/p&gt;&lt;pre class="language-clojure"&gt;
(ns &amp;quot;my.namespace&amp;quot;
  (:import [goog.math Long]))

(goog.math.Long.fromString &amp;quot;123&amp;quot;)&lt;/pre&gt;&lt;p&gt;&lt;code&gt;import&lt;/code&gt; + dot works well, and it seems to be also the
   recommended way, according to the &lt;a href="https://cljs.github.io/api/cljs.core/import"&gt;&lt;code&gt;import&lt;/code&gt;
   function examples&lt;/a&gt; in the documentation.&lt;/p&gt;&lt;h2&gt;language-in: es-next&lt;/h2&gt;&lt;p&gt;New Google Closure Library seems to contains JavaScript code that
   utilizes &amp;quot;Optional catch binding&amp;quot;. What this means is that usually, when
   you catch an error in JavaScript, you bind the error to a variable, like
   this:&lt;/p&gt;&lt;pre class="language-javascript"&gt;
try {
  // something
} catch (error) {
  // the exception is bound to &amp;apos;error&amp;apos;
}
&lt;/pre&gt;&lt;p&gt;In the newer JavaScript version (ES10) it&amp;apos;s optional to bind the
   exception to a variable. The following works just fine:&lt;/p&gt;&lt;pre class="language-javascript"&gt;
try {
  // something
} catch {
  // no binding
}
&lt;/pre&gt;&lt;p&gt;Google Closure Library contains JavaScript code that utilizes optional
   catch binding. Because of that, we got an error &amp;quot;ERROR
   - [JSC_LANGUAGE_FEATURE] This language feature is only supported for
   ECMASCRIPT_2019 mode or better: Optional catch binding.

&amp;quot;.&lt;/p&gt;&lt;p&gt;The solution is to change the &lt;code&gt;:language-in&lt;/code&gt; compiler option
   to &lt;code&gt;:ecmascript-next&lt;/code&gt;. And AFAIK, there&amp;apos;s no need to change
   the &lt;code&gt;:language-out&lt;/code&gt; parameter.&lt;/p&gt;&lt;p&gt;The issue has also been discussed in &lt;a href="https://ask.clojure.org/index.php/11426/optional-catch-binding-problem-in-recent-versions"&gt;Ask
   Clojure forum.&lt;/a&gt;&lt;/p&gt;&lt;h2&gt;Upgrade devtools&lt;/h2&gt;&lt;p&gt;The compiler showed a warning about &lt;a href="https://github.com/binaryage/cljs-devtools"&gt;devtools&lt;/a&gt;using an
   undeclared var. This seems to be a &lt;a href="https://clojure.atlassian.net/browse/CLJS-3344"&gt; bug in
   ClojureScript.&lt;/a&gt;&lt;/p&gt;&lt;p&gt;The warning went away by upgrading devtools to 1.0.6&lt;/p&gt;&lt;h2&gt;Change the order in which JS files are required&lt;/h2&gt;&lt;p&gt;Okay, now that we fixed all the errors and warnings we saw in development
   mode, it was time to compile the JS bundle with advanced
   compilation. This revealed couple more issues.&lt;/p&gt;&lt;p&gt;We use &lt;a href="https://codemirror.net/"&gt;CodeMirror&lt;/a&gt; from &lt;a href="http://cljsjs.github.io/"&gt;CLJSJS&lt;/a&gt; with XML and Handlebars modes.
   Apparently, something has changed in the order in which the modules are
   required.&lt;/p&gt;&lt;p&gt;What we earlier had was this:&lt;/p&gt;&lt;pre class="language-clojure"&gt;
(ns code-editor-namespace
  (:require
   [codemirror :as codemirror]
   [cljsjs.codemirror.mode.xml]
   [cljsjs.codemirror.mode.handlebars]
   [cljsjs.codemirror.addon.mode.simple]
   [cljsjs.codemirror.addon.mode.multiplex]&lt;/pre&gt;&lt;p&gt;However, this didn&amp;apos;t work anymore. The reason was that Handlebars mode
   requires simple mode, thus, I needed to change the order in which they are
   required:&lt;/p&gt;&lt;pre class="language-clojure"&gt;
(ns code-editor-namespace
  (:require
   [codemirror :as codemirror]

   ;; Load addons first before language modes
   [cljsjs.codemirror.addon.mode.simple]
   [cljsjs.codemirror.addon.mode.multiplex]

   [cljsjs.codemirror.mode.xml]
   [cljsjs.codemirror.mode.handlebars]&lt;/pre&gt;&lt;p&gt;The change makes sense. However, what I don&amp;apos;t know is why this was
   working earlier. My guess is that because this appeared in advanced
   compilation, maybe something has changed in Google Closure Compiler.&lt;/p&gt;&lt;h2&gt;goog.object needs to be required before use&lt;/h2&gt;&lt;p&gt;Next issue with advanced compilation: &lt;code&gt;goog.object&lt;/code&gt; needs to
   be required in the namespace that uses it. This wasn&amp;apos;t the case with the
   earlier version, but was changed in the &lt;a href="https://clojurescript.org/news/2021-11-04-release"&gt;ClojureScript
   1.10.891 release.&lt;/a&gt;&lt;/p&gt;&lt;p&gt;Fix was easy, I just added the requires in the namespaces that didn&amp;apos;t
   have those.&lt;/p&gt;&lt;h2&gt;Conclusion&lt;/h2&gt;&lt;p&gt;Although a few things needed special attention, I&amp;apos;m
   suprised that there weren&amp;apos;t more of those. As mentioned, the version from
   which I updated was released more than three and a half years ago!&lt;/p&gt;&lt;p&gt;ClojureScript is heavily using Google Closure Compiler and Google Closure
   Library. I didn&amp;apos;t go through all the changelogs, but my assumption is that
   many of there &amp;quot;breaking changes&amp;quot; were actually something that were changes
   in Google Closure instead of changes in ClojureScript itself. &lt;/p&gt;</description></item></channel></rss>