Upgrade to Pro — share decks privately, control downloads, hide ads and more …

ClojureScript Reimagined - Dutch Clojure Days 2022

ClojureScript Reimagined - Dutch Clojure Days 2022

Michiel Borkent

October 30, 2022
Tweet

More Decks by Michiel Borkent

Other Decks in Programming

Transcript

  1. Michiel Borkent @borkdude Dutch Clojure Days 2022


    ClojureScript


    Reimagined

    View Slide

  2. 🪶 Light-weight (memory)


    ⚡ Fast to start

    👩🔧 Easy to install & operate (single binary)


    🔋 Batteries included

    View Slide

  3. Can we fulfil new Clojure use cases with similar
    approaches?
    What about (CL)JS targets?
    JavaScript as universal glue language

    View Slide

  4. ClojureScript
    Is awesome 🙏


    Targets JavaScript ecosystem with Clojure


    Google Closure: advanced whole program optimizer


    Mostly used from JVM


    Self-hosted exists (8mb of JS, non-advanced)


    Are there are opportunities?

    View Slide

  5. Support for ES6 modules


    Async/await


    Compiling ad-hoc small snippets (scripting, on the
    fl
    y compilation)


    (Also) run without JVM, fast startup for small scripts


    JSX


    Distributing compiled CLJS libraries on NPM for usage from JS


    Interop improvements: JS destructuring
    CLJS Opportunities

    View Slide

  6. Small


    Clojure


    Interpreter
    $ clj

    Clojure 1.11.0


    user
    = >
    (require '[sci.core :as sci])


    nil


    user
    = >
    (sci/eval
    -
    string "(+ 1 2 3)")

    6
    $ clj -M:cljs
    -
    m cljs.main
    -
    re node

    ClojureScript 1.11.54


    cljs.user
    = >
    (require '[sci.core :as sci])


    nil


    cljs.user
    = >
    (sci/eval
    -
    string "(+ 1 2 3)")

    6

    View Slide

  7. Nbb (Node.js)


    Scittle (browser)


    Joyride (VSCode)


    4ever-clojure


    Clerk viewers


    Maria.cloud (soon)


    More...
    Small


    Clojure


    Interpreter
    NEW USAGES OF CLJS THROUGH SCI

    View Slide

  8. CLJS Scripting on Node.js
    $ npx nbb


    user
    = >
    (require '["child_process" :as cp])


    nil


    user
    = >
    (str (cp/execSync "echo dude"))


    "dude\n"


    user
    = >
    (require '[clojure.math :as math])

    nil

    user
    = >
    (math/IEEE-remainder 10 math/PI)


    0.5752220392306207

    View Slide

  9. Promesa

    View Slide

  10. Nbb-compatible


    CLJS web


    framework
    Sitefox

    View Slide

  11. AWS Lambda, now with first class parentheses
    Deploying Clojure on AWS Lambda with no compromises
    Ray McDermott


    [email protected]

    View Slide

  12. https://www.loop-code-recur.io/simple-site-analytics-with-serverless-clojure/
    Serverless site analytics with Clojure nbb and AWS

    View Slide

  13. See https://github.com/nextjournal/clerk/blob/main/ui_tests/playwright_tests.cljs
    Playwright (UI Tests)

    View Slide

  14. Bun (Node.js alternative ) + nbb

    View Slide

  15. https://youtu.be/7DQ0ymojfLg

    View Slide

  16. Scittle
    Eval ClojureScript (SCI) directly in browser tags!

    View Slide

  17. Scittle
    Load ClojureScript directly from GitHub source

    https://babashka.org/scittle/wordle.html

    View Slide

  18. Scittle

    View Slide

  19. https://codepen.io/ordnungswidrig/pen/xxpPYNO

    View Slide

  20. Scittle on Apropos
    https://vimeo.com/754492531

    View Slide

  21. Joyride

    View Slide

  22. View Slide

  23. Joyride: NPM libaries

    View Slide

  24. View Slide

  25. 🪶 Light-weight (memory)


    ⚡ Fast to start

    👩🔧 Easy to install & operate


    🔋 Batteries included
    What about a small(er) CLJS
    compiler?

    View Slide

  26. Cherry 🍒


    • ClojureScript (immutable data structures, cljs.core lib)


    • Bundle size starts at 300kb / 56kb gzipped


    Squint 🤤


    • CLJS syntax but compiles directly to JS:


    • No immutability, just JS objects all the way down


    • Bundle size starts at 5 bytes? :)


    Both projects share common compiler code (fork of scriptjure +
    cherry-picked code from CLJS)

    View Slide

  27. Cherry
    • Compile .cljs
    fi
    les on the
    fl
    y into ES6-compatible .js
    fi
    les.


    • npm install cherry-cljs + available on JVM (.cljc)


    • CLJS core functions shared across builds in common lib


    • Enables distributing compiled CLJS on NPM


    • No Google Closure (light weight, compatible with
    everyday JS tools)


    • Macro support


    • REPL support


    • Async/await


    • Native JS destructuring


    • Native JSX support
    🍒

    View Slide

  28. $ npm install cherry
    -
    cljs

    $ cat example.cljs


    (ns example)


    (defn foo [x]


    (assoc x :foo true))


    (prn (foo {}))


    $ npx cherry run example.cljs


    [cherry] Running example.cljs


    {:foo true}


    $ cat example.mjs


    import { assoc, keyword, prn, arrayMap } from 'cherry
    -
    cljs/cljs.core.js'


    var foo = function (x) {


    return assoc.call(null, x, keyword("foo"), true);


    };


    prn.call(null, foo.call(null, arrayMap()));
    Cherry .cljs → .mjs

    View Slide

  29. $ npm install esbuild

    $ npx esbuild example.mjs
    - -
    bundle
    - -
    minify
    - -
    platform=node
    - -
    outf
    i
    le=foo.mjs
    - -
    format=esm


    foo.mjs 296.3kb


    ⚡ Done in 36ms

    $ time node foo.mjs


    {:foo true}


    node foo.mjs 0.05s user 0.01s system 94% cpu 0.065 total
    Cherry + JS Toolchain

    View Slide

  30. $ cat example.cljs

    (ns example)


    (defn ^:async fetch
    -
    clojure []


    (let [response (js/await (js/fetch "https:
    / /
    clojure.org"))]


    (.text response)))


    (prn (.substring (js/await (fetch
    -
    clojure)) 0 150))


    $ node
    -
    v


    v18.6.0


    $ npx cherry run example.cljs


    [cherry] Running example.cljs


    "\n
    < ! - -
    This site was created in Web
    f l
    ow. http:
    / / w w w
    .web
    f l
    ow.com
    - - >
    \n
    < ! - -
    Last Published: Fri Nov 13 2015 01
    :
    48
    :
    45 GMT+0000 (UTC)
    - - >
    \nCherry + async/await

    View Slide

  31. Cherry + JSX

    View Slide

  32. Next.js, Vite, SolidJS, …

    Hot-reloading, bundling: driven by JS tooling
    Smallest bundle: 300kb (56kb zipped)

    View Slide

  33. Bundling cherry code
    Why do we have a baseline of 300kb / 56kb gzipped?


    • cherry-cljs/core.cljs is produced using Google Closure advanced
    compilation


    • All of ClojureScript is included


    • Google Closure does not emit code that can be optimized by ES6
    treeshakers like esbuild, webpack


    • Everything is wrapped in one global object, instead of ES6-idiomatic
    code


    • Open issue: https://github.com/squint-cljs/cherry/issues/24

    View Slide

  34. Vanilla and ES6-friendly JS output


    Bene
    fi
    ts:


    • Smaller outputs


    • More direct JS interop (back and forth)


    Downsides:


    • Giving up on persistent/immutable CLJS data
    structures
    🤤
    Squint

    View Slide

  35. Squint + SolidJS: 5kb

    View Slide

  36. $ npm install squint
    -
    cljs

    $ cat example.cljs


    (ns example)


    (defn foo [x]


    (assoc x :foo true))


    (prn (foo {}))


    $ npx squint run example.cljs
    - -
    extension mjs


    [squint] Running example.cljs


    {"foo":true}


    $ cat example.mjs


    import { assoc, prn } from 'squint
    -
    cljs/core.js'


    var foo = function (x) {


    return assoc(x, "foo", true);


    }


    ;


    prn(foo(({ })));


    export { foo }
    Squint .cljs → .mjs

    View Slide

  37. $ npm install esbuild

    $ npx esbuild example.mjs
    - -
    bundle
    - -
    minify
    - -
    platform=node
    - -
    outf
    i
    le=foo.mjs
    - -
    format=esm


    foo.mjs 1.4kb


    ⚡ Done in 4ms



    $ time node foo.mjs


    {"foo":true}


    node foo.mjs 0.04s user 0.01s system 90% cpu 0.057 total
    Squint + JS Toolchain

    View Slide

  38. Squint Core Lib:


    Shallow Immutability
    Will Acton


    @lilactown
    Cora Sutton


    @corasaurus_hex

    View Slide

  39. Squint Core Lib:


    Laziness via generators

    View Slide

  40. Browser, Node, JVM / babashka
    Supported Environments

    View Slide

  41. View Slide

  42. REPL + ES6
    REPL = compile + eval


    Eval in ES6 context = dynamic
    import


    Dynamic import returns module,
    not a value - what to print?


    Special REPL mode in compiler


    • De
    fi
    nes global namespace state


    • De
    fi
    nes global return value

    View Slide

  43. Future work
    • Unify common compiler code into one repo


    • Better (n)REPL support


    • Better macro support


    • Optimizations


    • Contribute back to CLJS?

    View Slide

  44. CLJS reimagined?
    • Support for ES6 modules


    • Async/await


    • Compiling ad-hoc / small snippets (scripting, on the
    fl
    y
    compilation)


    • (Also) run without JVM


    • JSX


    • Distributing compiled CLJS libraries on NPM for usage from JS


    • Interop improvements: JS destructuring

    View Slide

  45. Thanks to sponsors!
    https://opencollective.com/babashka/contribute
    https://github.com/sponsors/borkdude
    🙏

    View Slide

  46. Thanks!
    Michiel Borkent @borkdude


    Dutch Clojure Days 2022


    View Slide