First, I get tired of (my current) file managers

Like most people, I use file managers almost every day. And as some people, I tend to use different ones depending on OS I use at work or at home.

One day, I needed to quickly find and edit some file at work, and since I have Ubuntu installed on my workstation, I launched Nautilus and tried navigaing to some directory, but it didn't happened right away, it took a couple of seconds, so my brain performed a usual context-switch, and I was able to notice that:

Wow, that's pretty slow...

The thing is that it wasn't some kind of directory with lots of items or media files so that it might need to generate thumbs, no, just regular directory with project files, nothing special.

I don't remember what happened when I came home and used Total Commander to do some stuff, but I know it was something that reminded me about the case earlier that day. This got me thinking:

Are file managers that hard?

And right after:

Probably yes, but...

But maybe I can build one to learn why and also try out some new shiny stuff?

Eion in JavaScript

I had some experience with Node.js and Electron at that time already, so I thought that building cross-platform file manager with web technologies should be fun and educating.

I also just finished watching a video course on Redux earlier that week, so it seemed to fit nicely. Finally, I found this thread on the awesome-electron repo and submitted the idea there (it didn't get any huge traction though, but I didn't expected that to happen anyway, so I just moved on).

I just went ahead and set up basic Electron project with typical React, Redux and Webpack setup. Things were progressing good, I've got basic navigation working, I was able to launch files, I made it show logical drives on Windows (C:, D: drives, etc.) and user's home directories on Linux (Home, Pictures, Downloads and so on).

Last JavaScript version looked like so:

Eion JavaScript

Handling state

The next thing I needed was the ability to select items in directories. At first this seemed pretty easy to do: you just need a new key in your Redux store to keep track of currently selected items and update/reset on certain events. But since Redux requires you to return new state when something logically changes in your app, I noticed I started doing Object.assign too much.

This got me thinking:

Returning (really) new object on every state change? Sounds familliar!

Immutable.js to the rescue! ?

At this point, I knew that I need something like Map.setIn to set value in nested map structure. And using Immutable.js was actually the first thought I had. I heard many good things about Immutable in my Twitter feed, and in recorded conference talks, so it seemed like a clear win.

There was a catch though, at least for me.

From Immutable.js readme on GitHub:

While immutable is inspired by Clojure, Scala, Haskell and other functional programming environments, it's designed to bring these powerful concepts to JavaScript, and therefore has an Object-Oriented API that closely mirrors that of ES6 Array, Map, and Set.

What I got from this paragraph, is that while Immutable.js does the job, it is a product of other language's ideas and not first-class in JavaScript.

Don't get me wrong, Immutable.js is great in bringing ideas of immutability to mainstream in a very professional and polished form. It was just me who didn't want to deal with product of the ideas and not the actual language where they came from itself.

Second reason to get out of JavaScript was curiosity. I felt like I need to go out there and look for a new ways of solving problems, just increase my overall knowlege in general.

Clojure(Script) to the rescue!

immutable is inspired by Clojure, Scala, Haskell

Ok, cool, I heard about all these, but that was pretty much it.

Haskell. Haskell is (probably) an interesting language to learn, but it seemed too far from what I needed to do - write an Electron application.

Scala. Scala's positioning was not close enough to what I needed to do either. Yes, it targets Java platform, and yes, of course you can do GUI stuff with Java, but it was still not what I wanted.

Clojure. Clojure being (dialect of) LISP stand out from this list pretty significantly. From what I learned later, of these three languages, Clojure has the most significant emphasis on immutability and persisitent data structures so I decided to proceed with it.

I have to say that I was actually pre-biased to use Clojure because I heard more (good) things about it than about other ones.

I know, it is kind of unfair to decide upon things only because you "heard" of them, but I felt like this would be a win in any case because I'll learn things anyway and there is nothing in not using what you've learned right away.

Clojure itself targets JVM, so in that respect is same as Scala to me, so there has to be a catch. Yes, there is a catch and it's called ClojureScript.

Martin's talk

ClojureScript is a dialect of Clojure that has same immutable and persisitent data structures I was looking for, but it targets JavaScript language as its execution platform, so it can run in browsers, node and surely Electron being the mix of them.

Yes I know, there are Scala.js and experimental Haskell-to-JS compiler, but these are not as first-class to their base languages as ClojureScript to Clojure.

The next thing I needed (among actually learning ClojureScript) was to find a way to make ClojureScript application for Electron.

I Googled:

clojurescript electron

And found this YouTube video by Martin Klepsch and this GitHub repo. These was a huge luck to find because now I don't have to solve the problem of figuring out how to run ClojureScript inside Electron.

No Lein. No Figwheel

As I was learning ClojureScript, learned that there is a tool called Leiningen that is kind of the same as npm for JavaScript, it installs your deps, compiles your app and stuff like that.

There was also a lot of mentions of Figwheel, and it turned out to be a live-reloading infrastrucure for ClojureScript apps.

So but there was a problem: in his talk, Martin talked about building ClojureScript apps for Electron with Boot - alternative build tooling solution for Clojure(Script) while Leiningen and Figwheel seemed to be more mainstream.

So I decided to try Leiningen and Figwheel first, because I thought that because of popularity I can get up and running faster.

That turned out not to be the case for me. While I could get Lein more-or-less do what I wanted, Figwheel was very hard for me to set up that time. Maybe today I could've set it up with no (major) issues, but I gave up on Figwheel back then.

Boot

So I had to back to Martin's Electron boilerplate project that used Boot. As I started building the actual app, I discovered that Boot actually had all I need from the beginning and even more.

I'm not sure whether it was only that template had very good initial set up, or Boot in general is a better tool, but it was really pleasant to discover that live-reloading works out-of-the-box, re-compilation time is very short, and there is support for most things you might need to build web applications.

core.async

One thing I was curious about is how Clojure(Script) solves problem of keeping asynchronous code sane. I even asked this on StackOverflow. The answer was kind of what I expected (yet still useful), but I wanted to confirm what my gut was telling.

Basic idea is that when you have to interact with some asynchronous JavaScript API, you just create a function that takes some arguments and a channel (or creates one), calls the API, and on callback or result event it puts the payload on that channel (sounds like FRP? Maybe it is kind of FRP, I don't really care).

That's it. You don't usually want to do anyting more than that in callback or event handlers anyway. That approach was kind of refreshing, but it is also a foundation to the other cool thing in core.async - go blocks.

In Clojure(Script), "go" is a macro from core.async library that re-writes block of synchronous-looking code into state machine that actually performs async operations and handles async flow inside. And because Clojure macros are just code transformations at compile-time, it is very cheap (not free I suppose, but cheap enough to not worry about it).

Eion in ClojureScript

I found this channels + go blocks idea interesting and useful. It is very easy to adapt existing APIs to just put any valuable information into channel and be done with it. I ended up using go blocks for creating mini event-loops that wait for input on some channel, handle it, and then wait again. Time will show if it is good enough for this kind of stuff, but it looks promising so far.

Another very cool idea in Clojure(Script) is that you can use same syntax you use for data structures for basically everything (they call it "code is data"). And it scales surprisingly good. For me, it means they Rich Hickey was right about (among other things) the fact that there is actually not that many data types in the nature of information.

Examples of using plain data for some web technologies:

  • CSS is a pair(vector) of selector string and map of rule definitions:

    Garden :

    [:body {:font-family "monospace"}]
        [:div.success {:color "green"}]
        

    and it nests as you want.

    Many people love CSS pre/post-processors because they can do loops, conditionals and variables, but here you have full-blown language where you can do any styles (read: data) processing you can think of.

  • HTML is just nested sets of tag names, attributes and child nodes:

    Reagent example:

    (defn simple-component []
          [:div
           [:p "I am a component!"]
           [:p.someclass
            "I have " [:strong "bold"]
            [:span {:style {:color "red"}} " and red "] "text."]])
        

    Reagent is a React wrapper that (like many others) has really nice look and it is actually a part of re-frame framework that kind of reminds React + Redux pattern, but actually much more powerful.

  • Config(s) use Clojure data structures directly, because (almost) by definition config is a map of config keys to values.

  • HTTP headers are by definition key-value pairs. I can hardly imagine a reason to not use a map for them.

And possibly other examples I can't think of right now.

Unlike JavaScript, where every client-side templating engine (or CSS pre/post-processors) have it's own meta-language for flow control, there is no extra stuff in Clojure(Script) and it's libraries.

I'm very impressed how much you can do using just very basic and familliar (to JavaScript developers) data structures.

So, I'm (re)writing Eion in ClojureScript now, even though it wasn't a big project in the initial JavaScript version, it is still a re-write, and it feels good so far.