Rewrite of a Flask Web App in Clojure

3 min read Original article ↗

The most beautiful thing about Clojure development is interactive development, which means we can leverage a REPL to quickly verify our thoughts, try out language features and libraries, etc. Here, not only does it mean that we can define a function or call a function in an interpreter shell of a programming language, but it also means we can, for example, evaluate any forms anywhere.

For example, we can send over the preceding form to the REPL and get the result immediately when the cursor is at anywhere in the following code snippet, be it position 1, position 2, or position 3:

(*
 (+ 1 2)                                ; position 1
 (- 5 4)                                ; position 2
 )                                      ; position 3

Another big advantage of using Clojure is that we can use Clojure(Script) for both the backend and the frontend; all the code can reside within the same project, and we can even share some logic in common files with the .cljc file extension.

With Clojure and ClojureScript existing in the same project, ideally, I can bring up two REPLs for Clojure and ClojureScript in one go with CIDER in Emacs using the command M-x cider-jack-in-clj&cljs, but it didn't work as expected, and I failed to figure it out in a short time. Instead, in order to accomplish my major task, migrating to the Clojure ecosystem as soon as possible, I suppressed my impulse to nail it down and just started up two REPLs separately, that is, running M-x cider-jack-in-clj and then M-x cider-jack-in-cljs.

In order to quickly convert HTMLs to the hiccup forms, I intended to use some help from conversion tools. There are a bunch of them out there, as listed on Converting html to hiccup · weavejester/hiccup Wiki.

Finally, I used https://html2hiccup.dev/. It's simple and intuitive to use and overall gets the task done, except there were a few corner cases relating to using them as Reagent components (It seems like these are differences between Reagent and hiccup):

  1. Incorrect capitalizations. Some noticeable examples are:

    • :autofocus "" should be :autoFocus true
    • :readonly "" -> :readOnly true
    • autocomplete "on" -> autoComplete "on"
  2. style's value is a string, which is incorrect for Reagent. E.g. {:style "display:none;"} -> {:style {:display :none}}
  3. Whitespaces around DOM elements are eliminated.

    [:h1
     "Generate"
     " " ; It should have a space before and after [:i]
     [:i "compile_commands.json"]
     " "
     "from GNU make output online"]

Update:

Besides, there are also some editor-based solutions:

  1. An Emacs package: https://github.com/kwrooijen/hiccup-cli
  2. A VSCode extension: https://calva.io/hiccup. Thanks to Peter Strömberg for telling me over Slack that, calva can handle the above style issue properly with custom config in settings.json:

    "calva.html2HiccupOptions": {
      "mapify-style?": true,
      "kebab-attrs?": true,
      "add-classes-to-tag-keyword?": true
    },

A libpython-clj Problem

The development process went smoothly until I needed to return a list of dictionaries in Python for Cheshire to consume on Clojure side.

This was my first time using libpython-clj, and I initially thought it would perfectly be able to return complex Python data structures to Clojure. After asking for help in the libpython-clj-dev channel on Zulip, James Tolton kindly pointed out to me it's better to encode the result as a JSON string, which is compatible with Clojure strings, and the performance is better.

For the record, here is the issue detailing the problem on GitHub, you can follow up there if you're interested.