
Clojure on the Desktop - vlaaad
https://vlaaad.github.io/year-of-clojure-on-the-desktop
======
dimitar
Most of the comments are about the startup RAM use and distributable size,
which is a criticism shared with Electron. The demo app is not attempting to
address these particular weaknesses.

The point of the article is there are some applications where processing speed
is important where Clojure's and JVMs concurrency and long running and large
dataset performance is very important. And the ecosystem appears to be ready
to make it possible to create apps where these strengths vis-a-vis Electron
are combined with the other strengths of Clojure compared to low level
languages (expressive syntax, interactive development).

~~~
Scarbutt
IME, regular JS is much faster than regular Clojure. You can make Clojure run
faster than JS by writing Java with parenthesis, but then you defeat all the
purpose of using Clojure in the first place.

~~~
piranha
That is a weird claim to make and completely not true in my experience.
Idiomatic Clojure is faster than any JS except maybe asm.js-like stuff.

------
wildermuthn
I once led a mid-sized team in building a desktop application with Clojure. It
might sound nuts, but we used both the JVM and Electron, using both Clojure
and ClojureScript. We solved all the performance issues we’d experienced with
Electron by restricting it to serve as a desktop GUI API. Literally nothing
was in Electron except a small package for handling these API requests: set a
menu, open a window, load a page, etc. Even inside an open window, the
ClojureScript code was loaded from and communicated with the JVM, rather than
being bundled into Electron. You want your desktop app to both look great and
perform great. Restricting Electron to its basics while moving everything else
into another system is definitely one option to consider.

~~~
kickopotomus
I am actually currently working on something similar but instead we opted to
use JxBrowser[1]. It's basically what Electron used to be but has Chromium
embedded into the JVM instead of Node.

[1]: [https://www.teamdev.com/jxbrowser](https://www.teamdev.com/jxbrowser)

~~~
sdegutis
Super cool. If someone wraps this with Clojure for the backend, ClojureScript
for the front-end, and hides jpackage and NPM (or whatever ClojureScript uses)
details, this would be a great Electron alternative.

------
rvz
Hmmm... Lets see the HN Reader example then...

95MB for the macOS DMG.

177MB for the actual example app.

For a typical hello world application like the HN example, it appears that it
would be no better than running an electron-made version given that they both
include their own versions of their respective runtimes. In this case, this
includes the entire Java runtime which Clojure needs to run.

I think I would be better off with using either C++ with Qt5 or even
FreePascal with the Lazarus IDE for this instead.

~~~
mister_hn
As suggested in the article, the usage of jpackage comes with a lot of
drawbacks:

\- still a JRE is shipped (you MUST tailor it to the bare minimum with the
flags) -> can be + ~30-40MB

\- OpenJFX/JavaFX relies on Qt (especially for the WebView) -> can grow up to
+ ~70MB

\- all the legal documents and docs are shipped together (you have to remove
them with a post-process script) -> \+ ~5-10MB

Using JVM for desktop apps is expensive in terms of space. Time can be reduced
consistently thanks to the graal native compiler, but still a C++ project
outperforms Java GUI applications by 500%

~~~
pjmlp
OpenJFX/JavaFX uses WebKit for the WebView, Java never had any dependency to
Qt.

[https://github.com/openjdk/jfx/blob/master/modules/javafx.we...](https://github.com/openjdk/jfx/blob/master/modules/javafx.web/src/main/native/CMakeLists.txt)

I wonder where that 500% number came from, and yes C++ does blazing fast
against Java in micro-benchmarks, except that on project delivery, developer
salaries, time to market, additional costs for bug fixes (including memory
corruption ones), available libraries, hardware costs for build performance
also play a very big role.

~~~
mister_hn
Until Java 9, openjdk was requiring Qt 5 for OpenJFX because of the WebView, I
clearly remember that. Still, shipping WebKit is pretty consuming

~~~
pjmlp
[https://docs.oracle.com/javase/8/javafx/get-started-
tutorial...](https://docs.oracle.com/javase/8/javafx/get-started-tutorial/jfx-
overview.htm)

> A web component that uses WebKitHTML technology to make it possible to embed
> web pages within a JavaFX application.

Qt was indeed there, just like Gtk, EFL and every other plaform that is part
of the WebKit source code vendoring inside the Java source code tree.

[http://hg.openjdk.java.net/openjfx/8/master/rt/file/f89b7dc9...](http://hg.openjdk.java.net/openjfx/8/master/rt/file/f89b7dc932af/modules/web/src/main/native)

Which doesn't imply that the native code shipped with Java has any trace of Qt
inside of it.

[https://www.oracle.com/java/technologies/javase-
jdk8-downloa...](https://www.oracle.com/java/technologies/javase-
jdk8-downloads.html)

------
piokoch
I've installed the application, it needs 840 MB of the memory. Firefox with
more than 100 tabs opend consumes 1.2 GB, IntelliJ with a huge java project
consumes 1.6 GB. I understand that this is proof of concept application, but
what eats so much RAM? Is that the data that was read or the framework itself?

~~~
imtringued
It doesn't actually need 840MB of RAM. This is an inherent limitation of the
way the JVM works. It will reserve a configured amount of memory upfront and
grow the heap if memory is full until it reaches the configured maximum heap
size. One especially annoying thing is that the JVM will not garbage collect
until the heap is actually full. This means if you allocate a little bit here
and there (like every single Java program in existence) over time you are
going to reach the maximum configured heap. Once the JVM has allocated heap
space it will never let go of it. Therefore you might as well assume that the
minimum amount of memory a JVM process consumes is the maximum that it has
been configured for. In this case the maximum was probably set to 840MB. The
application itself probably only uses something like 50MB.

Remember those stupid arguments that a competent programmer can write well
performing code [0] in any language? Here is the thing... You actually can't.
Especially not with the JVM. Let's say you use a super efficient framework
that is purely optimized for startup performance and low memory like Micronaut
or Quarkus. When you look at your metrics you see that the Java program is
consuming 10MB of heap space and pat yourself smugly on the shoulder thinking
how well written these frameworks are. Tough luck. Even if you configure the
heap size to something absurdly low like 32MB your JVM process will still use
120MB. Why? Because the JVM has a minimum footprint. It has lots of parked GC
threads in the background that need 2MB stacks. It has to keep the class files
in memory, keep profiling data, the generated code and lots of other things.
If I need something that needs to consume as little memory as possible I just
stick with C++ or Lua. With those two languages staying under 1 - 2 MB of RAM
is trivial.

Also... it is possible your coworkers do not even set a maximum heap and just
let it stay on the default values. That's how a tiny little CRUD app as a
dashboard ended up allocating 10GB of RAM to itself on a server with 128GB
RAM.

[0] well performing = low memory usage in this case

~~~
hedora
It is possible to write well-performing Java code. I’ve done it, and know
others that have.

From looking at multiple projects over many years (as both languages have
improved), my rule of thumb is that writing performant Java is about 10x
harder than writing memory-safe C++.

One particularly insidious, and related problem is that getting the first
working Java program is slightly faster than doing it in C++. There are people
that have built a career by producing a series of 90% Java solutions, and
rapidly moving on.

They leave a wake of human suffering behind them in the form of large teams
that perform heroic efforts in order to clean up the mess. This certainly
wastes many billions of dollars a year.

~~~
hota_mazi
> my rule of thumb is that writing performant Java is about 10x harder than
> writing memory-safe C++.

This is a bold claim.

The JVM is incredibly good at generating fast code, to the point that even
naively written Java is guaranteed to run in the ballpark of C/C++.

Writing memory safe C++... good luck with that one, especially as soon as you
start using external libraries where memory ownership becomes very blurred.

------
slifin
I wonder if graalvm can be applied to the output of proton native which allows
you to use ClojureScript to compile react code into QT but still in the
context of JavaScript if graalvm can get a native binary out of that it'd be
cool

[https://gist.github.com/polymeris/0112389243890709108c4b8b0a...](https://gist.github.com/polymeris/0112389243890709108c4b8b0a186f46)
[https://github.com/roman01la/proton-native-
cljs](https://github.com/roman01la/proton-native-cljs)

------
vikeri
Very interesting to use a redux (re-frame) like programming model for building
native desktop UI's. I have done some basic stuff with it and it sure seems
powerful.

For anyone interested there will be a virtual meetup 16:th of April 6PM CEST:
[https://www.meetup.com/sthlm-
clj/events/269204900](https://www.meetup.com/sthlm-clj/events/269204900)

~~~
phronmophobic
If you're interested in desktop alternatives for clojure, check out
[https://github.com/phronmophobic/membrane](https://github.com/phronmophobic/membrane).
It's different from javafx in that all the event handling code is pure
clojure. For graphics, it uses skia, [https://skia.org/](https://skia.org/).

~~~
capableweb
Love the example with the spacer, guess we're back to building UIs with tables
soon as well!

    
    
      (defn counter [num]
        (horizontal-layout
         (on :mouse-down (fn [[mouse-x mouse-y]]
                           (swap! counter-state inc)
                           nil)
             (button "more!"))
         (spacer 5 0)
         (label num (ui/font nil 19))))
    
    

Otherwise, haven't heard about Skia before, so thanks for sharing that, looks
really nifty.

~~~
phronmophobic
Thanks! More complicated layout strategies are easy to integrate, but spacer
works really well for examples since most people can intuit what the result
will look like.

Skia has worked well so far. Since it's used by Chrome, Android, and Firefox
under the hood, it means that I can reach a lot of platforms "for free".

------
lenkite
Please note that the Gluon Client Plugin can be used to create native JavaFX
desktop apps which take far less memory.

[https://docs.gluonhq.com/client](https://docs.gluonhq.com/client)

When Clojure resolves all outstanding Graal issues, one could use Clojure for
native apps too, but there are several bug tickets open on Clojure wrt Graal.

~~~
geokon
I wonder if anyone has tried cljfx->Android with Gluon's stuff

~~~
capableweb
Last time I tried, cljfx (actually JavaFX in general) doesn't play too nice
with GraalVM, I'm not sure Gluon actually addresses that, so it'll be hard to
add another layer (Android) when the basic layer doesn't work.

~~~
geokon
is it part of the same pipeline? Graal is compiling to native while Gluon on
Android I assume is running JFX on Dalvik instead of JVM (it possible I don't
get how Gluon gets JFX to run on Android)

------
jiofih
> browser-based technology struggles to utilize multiple cores in the same VM
> instance

> 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

2005 called and wants it’s quotes back. It is trivial to spawn worker
processes today and VM performance is only 2-5x behind compiled ones, often
closer. Not to mention WASM.

The elephant in the room is not JavaScript or it’s VM but the browser engine.

EDIT: seems like JavaFX has its own browser-like “scene graph” _and_ embeds
WebKit, so this will suffer from the same bloat in memory use and app sizes?

~~~
StreamBright
The real question is why though. Why should we use the HTTP/HTML/JS/CSS
universe to deliver interactive applications to users. I only see downsides
here. Not to mention that we pull these things into native applications like
it was great to begin with.

WASM might be able to address some of the shortcomings of this stack, not
sure.

~~~
jiofih
Because it is an extremely effective model for building UIs. As I mentioned
above even this project uses a similar architecture, only backed by Clojure.
Any UI toolkit will have similarities with the web stack.

The real issue is the size of these monster engines, but a lightweight engine
that only supports the subset needed for apps is possible - see Sciter, and I
have high hopes for [https://ultralig.ht/](https://ultralig.ht/)

~~~
StreamBright
>>Because it is an extremely effective model for building UIs.

Is it? Have you ever tried? I think it is extremely hard to build reponsive
UIs that work on every platform and there are no UX problems. We have to deal
with a giant amount of boilerplate in many cases because using vanilla things
(JS especially) won't cut it anymore. I am not even sure how many frameworks
are targeting this space and try to solve the typical issues with these
technologies and fail miserably. I think some of these issues are finally
started to be handled the right way (Elm or ReasonML) but there are still some
painpoints left (CSS). The question still, is it worth it? Couldn't we do
better? The performance implications (energy consumption) for an average
website is just hilarious. We should have a day where everybody disables JS
and measure how much percentage of our computing capacity is going to render
websites because we do not want to move away from HTML which was not (by any
strectch of imagination) designed for this to start with. If anything, this
should be a concern to most of us.

~~~
jiofih
> Have you ever tried?

I’ve been doing it for 15 years.

> I think it is extremely hard to build reponsive UIs that work on every
> platform and there are no UX problems

It is hard on native platforms as well. Developing for mobile is not that far
off from web today, everything converging towards flexbox or constraint
solving, reducers etc.

I think you’re conflating the problem of a bloated JS stack with the platform
itself. We were building perfectly fine interactive UI without any of this
crap ten years ago, and can still do it.

~~~
StreamBright
HTTP and HTML were not designed to do this. It was specifically static and
stateless. We are retrofitting statefulness and dynamic content to this
platform. Imagine if we designed a stateful and dynamic platform from the
ground up that could be implemented with minimal effort to every platform and
started to use the right abstractions with the right set of tools.

------
vijaybritto
This looks nice and promising for multi core apps. But, 85Mb for a demo app?
It sounds heavy and might end up like intelliJ IDEs I think. How about
compiling to binaries with Graal native? Can it be done?

~~~
vlaaad
Yes, it can be done: JavaFX developers created a library that deals with
various pitfalls of compiling it to GraalVM:
[https://github.com/gluonhq/substrate](https://github.com/gluonhq/substrate)

I think using jpackage is easier to start with since there are less cutting-
edge moving parts involved, and it imposes fewer restrictions on the runtime.

~~~
vijaybritto
I hope this one picks off quickly! Electron needs serious competition

------
kitd
Most interesting takeaway for me was jpackage working with GH actions. V
useful. Who needs a local build system now? ;)

~~~
ptrik
Same here. Have been trying to build something similar with GitHub Actions and
this is a nice reference.

------
Klonoar
This is going to sound subjective, but: you can't claim that something is a
good desktop-app building experience in 2020 and lead with something that
looks like... that.

Browsers have simply set the bar too high.

------
pengwing
Why do you package a javascript VM at all? You could have a local server
written in clojure listening on localhost:<someport>. The entire js-driven UI
could just be in a browser tab.

~~~
elcritch
One issue with that is the lack of encryption and/or security between UI and
the local server. Also I always forget the port for the local host style apps.
If PWA's were easier on desktops that'd help.

------
sdegutis
> outperforms plain React a lot of the time due to optimizations that re-
> render components only when their inputs change

Is this not exactly how React also works?

~~~
mst
Not quite, because if react is re-rendering a component it re-renders all its
subcomponents as well whether they've changed or not - the DOM diffing stuff
means it won't screw with the DOM for stuff that hasn't changed, but it still
has to regenerate it to find out.

~~~
sdegutis
So reagent skips this by only re rendering a parent of only the parent changed
and only re rendering the child of the child’s j out changed? (Presumably
using its atoms for props then too and not just state?) This somehow sounds
error prone.

~~~
mst
That sort of thing is honestly pretty pleasant to write and test in lisp like
languages.

I wouldn't want to try and write it in javascript, but I'm entirely willing to
believe that clojure/cljs got it right.

~~~
sdegutis
I don't see what s-expressions have to do with this concept. It should be just
as doable and nice in JS...

------
nico_h
Anyone knows how Jpackages compares to good/bad old JNLP or is that
complementary?

------
dezzeus
I've just started designing something very similar in my free time, but for Go
(golang): interface around native GUI + WebKit webview + React + bindings.

I'm glad that we are slipping away from Electron...

~~~
dgellow
Have you checked React Native for Windows and macOS?

It goes even further as it get rid of the webview

[https://microsoft.github.io/react-native-
windows/](https://microsoft.github.io/react-native-windows/)

Native widgets, bridge between JS thread and the native world. That gives you
good performances and the flexibility of JS/Typescrit and React to glue things
together.

(I haven’t tried yet, but that looks quite nice on paper)

~~~
dezzeus
Of course I have, but that's not exactly what I was aiming for... ;)

