ClojureScript is, in my estimation, the best compile-to-JS language available. I have tried many, and while each has some enjoyable qualities, none satisfy all my (admittedly limited) needs/wants as a coder. I have long thought that what JavaScript is missing is not a type system, but a complete language implementation. ClojureScript provides that, and tooling like Shadow-CLJS gives you the same robust access to the wealth of JS libraries that JS programmers lean on to do most of their consequential work.
I face a conundrum, though. I don't like web development, and I don't
like the ceremony of single-page apps. They are cool, and CLJS's
solutions for React-based SPAs are still state-of-the-art many years
after their initial release. Recent efforts have been made to keep
CLJS modern, while not giving up on the things that made it much more
advanced than JS at the time of its inception. Projects like
Squint have been the result,
and last year's release of
ClojureScript
added support for :lite-mode.
Unfortunately, I have found it difficult to achieve a balance between the benefits of CLJS and some of the contingencies one is forced to reckon with, like the historical deep coupling between React and CLJS, a situation only very little bettered by some of the innovations mentioned just above.
Recently, I started learning Perl. I surprised myself by enjoying it. It's got clunky syntax and sometimes it doesn't do what I mean. Still, for a language that's turning 40 next year, it has some nice features, and it satisfies my mental model of programming, which is approximately the same as the one I used when I was 12 and I passed the time by obsessively solving Sudoku puzzles.
These days, I'm just a hobbyist, having "retired" from professional coding in February 2025. Maybe I will return some day, but if that proves to be the case, I do not doubt that my contributions will be the richer for having amused myself by finding the most offensively elaborate ways to do small things.
Enter The Worst Way ⚰️ to add ClojureScript to your web app.
One of Perl's particularly absurd (read: useful) features is the
__DATA__ directive. You can tack it onto the end of a Perl file and
the Perl interpreter, when once it encounters this symbol, will stop
parsing Perl code. You can put just about anything here, and it will be
available in the program above through a file handle called...wait for
it: DATA.
This file handle is stateful and can be seeked on and what not.
Perl has a cool, modern web framework called
Mojolicious. It has several intriguing
features, not the least of which is a custom data pipeline syntax that
is described
here. Another
of its value propositions is true single-file web apps, for the
simple cases. Using __DATA__, Mojolicious allows appending HTML
templates to the same file that defines your app's routing table and
any other business logic.
I know "separation of concerns" is the idée fixe of the industry (except when it isn't cough cough CSS-in-JS cough cough), but when you have just a few routes and a small number of templates, having the ability to reference things without jumping around from file to file (even with an IDE) is a breath of fresh air.
Howsoever that may be, what follows is such an egregious violation of all good sense that I will not pretend to hide behind such flimsy excuses as making counterarguments against the customs of the web development world.
Behold, maybe the worst code you will see all day:
#! /usr/bin/perl
use Mojolicious::Lite -signatures;
open my $cljs, '>', 'src/hello_world/core.cljs' or die;
while (<DATA>) {
my @source = grep { m/(ns hello-world.core/ ... m/;{5}/ } <DATA>;
for (@source) {
print $cljs $_;
}
}
get '/' => sub ($c) {
my $script = $c->asset_tag("/main.js");
$c->stash(cljs => $script);
$c->render(template => "index");
};
app->start;
__DATA__
;;;;;
(ns hello-world.core)
(def p-el (.getElementById js/document "greeting"))
(set! (.-innerText p-el) "Hello ClojureScript, from Perl 5!")
;;;;;
@@ layouts/top.html.ep
<!DOCTYPE html>
<html>
<meta charset="UTF-8">
<head>
</head>
<body>
<%= content %>
<%= $cljs %>
</body>
</html>
@@ index.html.ep
% layout 'top';
<div>
<p id="greeting"></p>
</div>
This keeps the $cljs filehandle open, and thanks to the code
reloading done by Mojolicious, DATA is automatically updated, too,
meaning that while loop runs on every save. The CLJS hot reloading
done by Shadow just works, even if this example is cursed and possibly
useless to others.
Insofar as this post is more than a joke, it might serve to offer a
brief closing justification for its existence. My logic is as follows:
I am just one guy of whom it is maybe going too far to say that I am
building "real" apps. Indeed, it may also be false to say that I am
having fun, in any ordinary sense of that word. That being said, I was
quite tickled when I saw that I succeeded in making this proof of
concept. I was worried that there would be race conditions and
unexpected behavior with the DATA filehandle, but everything just
runs as I expect it to. DWIM has a new and extremely diabolical
friend.
