FP Web Conf

Functional Programming Conference for web development professionals

Mitesh

@oxalorg

Clojure: Have a REPLy good time

Submitted Sep 5, 2023

Abstract / Summary / Teaser

Many languages have REPLs, but some REPLs are more REPLy than other REPLs.

One of the most enlightening parts about working with Clojure is learning to wield the power of directly interacting with your programs.

It’s a 0→1 paradigm shift in the way we think about our programs, and about the way we code those programs. It is rarely found in the modern world of non-homoiconic (often typed) programming languages.

I think that it’s extraordinarily important that we in computer science keep fun in computing - Alan Perlis

I want to share this experience of having GROK’d what it means to do interactive programming with fast feedback cycles and a whole lot of FUN!

Additionally, I would also like to discuss about the expression problem and how certain language features can really aid you in designing your systems. Here I will discuss how we use Clojure’s capacity to solve these in some real-world use cases.

Details

A rough list of ideas I would talk about:

  • My REPL vs Your REPL

    • Interactive Programming is more in-tune with what we do, rather than REPL driven developement

    • Interacting with the program while it runs

    • Never write directly into the repl, your editor is STILL your primary interface

    • Nowadays the editor will parse the code, the LSP server will parse the code, the tree-sitter will parse it, then the actual compiler, then other tools, everyone lives in their own separate world. There’s no shared understanding of the world.

    • What if we had a single process which had really STRONG abilities for interaction, introspection, reflection, evaluation, compilation, re-evaluation, everything!

    • That coupled with the ability to MANIPULATE anything and everything, really makes interactive programming possible. And Clojure lends to us a very pragmatic vision of that.

    • In lisps/smalltalk it goes a level further with the execuation stack itself being a part of this process, so you can do things like breakloops to inspect/manipulate/change the code at runtime on an exception or before any form. In smalltalk, even the editor lives inside the process!

    • Think about GUI vs terminal/cli, can you imagine a world where we didn’t have terminals and cli’s to interact with our machines?

    • Even better with an inherent emphasis on working with just data, pure functions, and immutability by default

    • REPL Sessions - it’s easy to just start coding in a .clj file when a REPL connection gives you access to your entire program state compiled and loaded in memory

    • Turn your repl sessions into tests — much more fun!

    • Commit the repl sessions (again just a plain old file) in the repo, they act as replayable docs / integration demos

    • REPL into your production systems

      • Sometimes when you’re already in danger, REPLing in your system is not riskier
      • Example:
        • At Gaiwan in one of our production app servers we weren’t receiveing certain messages via the websocket. There was nothing in the logs, we REPL’d in, printed out our in-memory cache data structures which were storing these messages and spit the cache out in an .edn file. From there it was easy to figure out the fix, patch the function, and test it live on the running system!
        • I can count on my single hand the number of times I’ve REPL’d in to a production system, but when there are no alternatives, this may just save the day!
    • Ending with the note:

      rapid feedback is no substitute for software design and methodic problem-solving - clojure.org

  • Expression problem

    "It is better to have 100 functions operate on one data structure than to have 10 functions operate on 10 data structures.”

    • Clojure has a rock solid core api. It’s intuitive enough that often that is all you ever need. cond cons conj filter map reduce assoc dissoc etc.

    McCarthy built Lisp out of parts so fundamental that it is hard to say whether he invented it or discovered it.

    • But Clojure is very pragmatic, it gives you with a lot of different data structure like PersistentHashSet, PersistentArraySet, PersitentTreeSet, PersistentQueue, etc

    • But all the core functions work on all these data structures transparently. Moreover, you can create your own data structures to work with these functions too, AND not only that you can also patch other peoples data structures to work with these functions.

    • Rich Hickey calls this “parochialism”, where you have five things that implement some key-value mapping, and one has a .get(key), and the other has getValue(key) and another has lookup(key), and even if they have the same method signature they are in different interfaces so you can’t actually write agnostic (duck typed) code.

    • Most problems arise when you’re interacting with libraries, or modules, which don’t quack like the way you want it to quack. What do you do then?

    • Do we live with their design choices? Do we have a way to clean up someone else’s design? Yes we do!

    • Illustrate this with some examples of how we used these to solve problems

    • Eg In one case of wrapping a java library we found numerous classes being their own special snowflake.

      (reflect/extend-signatures HasUUID
        "java.util.UUID getUniqueId()"
        (-uuid [this] (.getUniqueId this))
        "java.util.UUID uuid()"
        (-uuid [this] (.uuid this))
        "java.util.UUID id()"
        (-uuid [this] (.id this))
        "java.util.UUID getId()"
        (-uuid [this] (.getId this))
        "java.util.UUID getUID()"
        (-uuid [this] (.getUID this))
        "java.util.UUID getUUID()"
        (-uuid [this] (.getUUID this)))
      

Some more fancy bits about Clojure I would like to discuss if time permits and if I am able to prepare some nifty understandable examples:

  • DSLs are just data structures
    • FFMpeg DSL samples
    • Hiccup / HTML / Markdown samples
    • All libraries use hiccup-like structures, very easy to walk and manipulate these DSLs (it’s just a list!). Everyone in the community embraces this idea of just using plain old data structures, which makes things fit and align intuitively across different libraries.
    • Homoiconicity - code is also a datastructure, examples of some handy macros
    • Few examples of how Gaiwan uses these
      • Ornament - a library to turn react components into styled components
      • cleaning up imports on an unmaintained codebase by traversing through the code (= :code :ast)
  • Code sharing
    • schema validation of ffmpeg forms on the frontend vs backend
    • Backend routes → Static site rendering routes → frontend routes
  • Data in Transit and EDN
    • Backend and frontend talk the same language, which the compiler understands
    • EDN has reader macros, extend the edn parsing easily
    • Represent your custom data any way you want
    • Share this logic between both frontend and backend, does not work with external interactions but makes internal calls much simpler

Bio

Mitesh (@oxalorg) learnt coding because of his desire to hack video games. This slowly turned to a passion for all things computer.

Active contributor to OSS, he was also funded by ClojuristsTogether to work on Clojurians Log V2.

He creates Clojure, Emacs, & Vim related screencasts on his YouTube Channel.

Comments

{{ gettext('Login to leave a comment') }}

{{ gettext('Post a comment…') }}
{{ gettext('New comment') }}
{{ formTitle }}

{{ errorMsg }}

{{ gettext('No comments posted yet') }}

Hosted by

JSFoo is a forum for discussing UI engineering; fullstack development; web applications engineering, performance, security and design; accessibility; and latest developments in #JavaScript. Follow JSFoo on Twitter more

Supported by

Community sponsor

FP-Juspay is a forum to dive deep and contribute to the world of Functional Programming - Frameworks, Applications and People. more