Biff support for XTDB v2 is in pre-release

I've been working on/preparing for migrating Biff to XTDB v2 since that became generally available in June. After investigating the deployment options and performance characteristics, I've added some XTDB v2 helper functions to the Biff library (under a new com.biffweb.experimental namespace) and I've made a version of the starter project that uses XTDB v2.

You can create a new XTDB v2 Biff project by running clj -M -e '(load-string (slurp "https://biffweb.com/new.clj"))' -M xtdb2. See this gist for a diff between the old/main starter project and this new one.

To give you a quick overview of what Biff provides:

  • There are use-xtdb2 and use-xtdb2-listener components, roughly the same as we have already for XTDB v1.
  • The ctx map will have a :biff/conn key in it (a Hikari connection pool object) which you can pass to xtdb.api/q to do queries.
  • There is no longer a custom Biff transaction format. There is still a lightweight wrapper function, com.biffweb.experimental/submit-tx, which will apply Malli validation to any :put-docs / :patch-docs operations.

There's still plenty of work to do before XTDB v2 support in Biff is officially released and becomes the default:

  • Next up, I'm migrating Yakread to XTDB v2. This will help me find any more issues that need to be addressed/make sure that Biff is indeed ready for XTDB v2.
  • After that I need to update a bunch of documentation, including the tutorial project.

Since those next two steps will take a while, I wanted to do this "pre-release" for anyone who would like to get a head start on trying out Biff with XTDB v2. If you do so, let me know whatever questions/comments you have. Just note that the new functions in Biff's API are still experimental and might have breaking changes before I do the official release.

And for anyone who would rather not deal with migrating an existing app, Biff will still support XTDB v1. It's totally fine to stay on that.

Finally: I'll be at Clojure/Conj next week, at least if my flight doesn't get canceled. Come say hi.

Permalink

Build Asteroids with ClojureScript & Scittle

Create a classic Asteroids arcade game with physics simulation, collision detection, canvas graphics, and retro sound effects - now with mobile touch controls! All running in the browser with zero build tools!

Permalink

Advanced Beginner’s guide to ClojureScript

So how does your tiny ClojureScript program transform into runnable JavaScript?

(ns app.core)

(println "Howdy!")

First of all, you need a compiler! ClojureScript is a hosted language, which means you have to compile it into JavaScript, unless you want to ship the compiler into a browser to be able to interpret ClojureScript at runtime, which is slow and in most cases doesn't make sense (but maybe you wanna build online REPL, that's ok).

This is not going to be easy. ClojureScript sits on top of JavaScript, the compiler is a library written in Clojure, which in turn hosts on JVM. This means you need Java installed, huh.

I prefer to use sdkman to manage my Java installations.

sdk install java

Next step is to install Clojure, head over to installation guide at clojure.org. If the following command returns 2, you are good!

clj -M -e "(inc 1)"

Create project directory somewhere and put deps.edn file into it with the following contents. deps.edn is kind of package.json, if you are coming from JavaScript world.

{:deps {org.clojure/clojurescript {:mvn/version "1.12.42"}}}

I hope you have Node installed? Now we can run ClojureScript REPL in Node environment. This line runs JVM process that loads Clojure, that loads ClojureScript compiler, starts Node process and runs it in REPL mode.

clj -M -m cljs.main --target node --repl

If this runs, it means you are hooked into Node process, nice!

(js/console.log "hello")

Try to list files in the current directory using Node'js fs module.

(def fs (js/require "fs"))
(.readdirSync fs "./")

It works? Great, let's move onto actually compiling a project into JavaScript files. Put our tiny program into src/app/core.cljs file. src is your project's source root directory. Also note that namespace name app.core (that's how modules are called in Clojure) in the code looks exactly like app/core.cljs. That's a convention in Clojure projects: namespace names should reflect directory structure starting from project's source root directory.

(ns app.core)

(println "Howdy!")

Now let's compile the program into Node script

clj -M -m cljs.main --target node --output-dir ./out --compile app.core

and run the script

node out/main.js

Do you see the output? Great, let's stop here and inspect compiler's output. Specified out directory includes whole bunch of files.

ls out

app cljs cljs_deps.js cljsc_opts.edn goog main.js nodejs.js nodejs.js.map nodejscli.js nodejscli.js.map

The actual compiled JavaScript program from your src/app/core.cljs file is in out/app/core.js, let's see what's inside

cat out/app/core.js
// Compiled by ClojureScript 1.12.42 {:target :nodejs, :nodejs-rt true, :optimizations :none}
goog.provide("app.core");
goog.require("cljs.core");
cljs.core.println.call(null, "Howdy!");

//# sourceMappingURL=core.js.map

cljs.core.println.call(null, "Howdy!"); is your (println "Howdy!"), where println is actually a part of implicit cljs.core namespace, which provides stdlib.

goog.provide and goog.require is a part of JavaScript module format of Google's Closure Compiler. Wait, what? Another compiler? Yeah, you see... ClojureScript compiler takes your source and outputs equivalent JavaScript code. Similarly to out/app/core.js, there's also out/cljs/core.js file which is compiled stdlib of ClojureScript. This just means that ClojureScript compiler is a source to source compiler. But how do we bundle all those JavaScript files into production ready bundle?

That's the job of Closure Compiler. Closure is an optimizing JavaScript compiler that ClojureScript is using since its initial release, in 2011. At the time JavaScript didn't have standard module format, remember AMD, UMD, RequireJS and CommonJS? Closure folks at Google invented another one, where goog.provide declares a module and goog.require imports another module.

ClojureScript emits Closure's module format so that the compiler can pick up and optimize generated JavaScript. Putting everything together, that's the stack you have to deal with:

To plug Closure into the equasion and produce optimized JavaScript bundle run the following command.

--optimizations advanced option is what tells Closure to gather all generated JavaScript files into a single bundle and optimize the program by removing unused code. Closure is quite powerful compiler, you can learn more about all kinds of optimizations it performs in this handbook that I created a while ago.

clj -M -m cljs.main --target node --output-dir ./out --optimizations advanced --compile app.core

Output out/main.js file is now a self-contained JavaScript program.

Let's make something cool now. Put the following code into src/app/core.clj

(ns app.core
  (:require [clojure.java.shell :as sh]))

(defmacro version []
  (:out (sh/sh "git" "rev-parse" "--short" "HEAD")))

and update your src/app/core.cljs to

(ns app.core
  (:require-macros [app.core :refer [version]]))

(println "Howdy!" (version))

Now init a git repo in your project directory and make the first commit

git init
git add src
git commit -m "first commit"

Compile your ClojureScript project

clj -M -m cljs.main --target node --output-dir ./out --optimizations advanced --compile app.core

and run it

node out/main.js

The script should print last git commit hash. Now inspect JavaScript generated from src/app/core.cljs

cat out/app/core.js

The hash is embedded in the code!

// Compiled by ClojureScript 1.12.42 {:static-fns true, :optimize-constants true, :target :nodejs, :nodejs-rt true, :optimizations :advanced}
goog.provide("app.core");
goog.require("cljs.core");
goog.require("cljs.core.constants");
cljs.core.println.cljs$core$IFn$_invoke$arity$variadic(
  cljs.core.prim_seq.cljs$core$IFn$_invoke$arity$2(["Howdy!", "7f7e8c3\n"], 0)
);

That's the cool part about Lisps. You can write code in your program that runs at compile time on a program itself. It's almost like a compiler inside of a compiler, except that you don't need special plugins to traverse AST, in Clojure and Lisps in general you create macros, special functions that take code, transform it and return new code.

Now, everything that you learned here is only a part of the story. We haven't touched on the language itself, how real projects are built and how ClojureScript integrates with JavaScript's ecosystem of NPM packages. ClojureScript is definitely not the easiest beast to pick up, but if you are doing front-end development and feel like digging into alternatives and learning from other languages, then there are definitely a few things you can learn from!

Permalink

Calling Jank from C

For those that don’t know, Jank is a Clojure implementation but instead of targeting Java, it targets LLVM (Low-Level Virtual Machine). That means, Jank compiles to native code, and interops directly with C++.

Jank already have ways to call C++, but I wanted to do the opposite – to call Jank’s code from C. The reason might not be obvious, so here is an why: writing libraries.

Not all things need to be “stand-alone executables”. One example is libraries for Node, for Ruby, or Python. These languages are amazing on the levels of abstraction they support, and it’s easy to interact directly with code and their runtime (in Node, using Devtools, in Ruby, using something like pry or Lazuli, my own plug-in). They are also quite slow, and in some cases, we might need to call some native API that these languages don’t support. So what now? The canonical way is to write some extension in C or C++; now we have to manually manipulate memory and deal with safety issues (and before people say something about it “not being that hard”, it is. Most of CVEs happen because of manual memory manipulation in C – every cast, every printf, every strcpy can cause ACE and/or privilege escalation issues). They are also not interactive so if you’re trying to easily hack some solution, you need to write the code, compile, make a shared library, use the library via the Ruby/Node/Python code, see if it does the thing you want, repeat.

It’s tedious. Maybe with Jank we can speed up this process?

First a disclaimer: Jank currently doesn’t seem to officially support what I want to do. It seems that its creator wants to support the use-case I want later, but right now, this is just a happy coincidence that I can do what I do. So let’s start with a base code:

(ns jank-test)

(defn some-code []
  (println "HELLO?"))

Save that to jank_test.jank and let’s compile it with Jank, but instead of making an executable, let’s instruct it to make a library with jank --module-path . compile-module jank-test.

This will generate some build files – in my case, in directory target/x86_64-unknown-linux-gnu-6edc6e02e1bf8d875f77f87b5820996901c1894b142485e01a7785f173afb8df/jank_test.o. You might notice that this is not a shared library – as I said earlier, Jank doesn’t really support what I want to do right now but it will in the future. For now, we can either create a shared library from this .o file or we can create a final binary by linking it together with our code. So let’s do this second choice, because it’s easier: you will now create a C++ file containing:

// (1)
extern "C" {
  void jank_load_jank_test();
}

#include <jank/c_api.h>

// (2)
using jank_object_ref = void*;
using jank_bool = char;
using jank_usize = unsigned long long;

extern "C" jank_object_ref jank_load_clojure_core_native();
extern "C" jank_object_ref jank_load_clojure_core();
extern "C" jank_object_ref jank_var_intern_c(char const *, char const *);
extern "C" jank_object_ref jank_deref(jank_object_ref);

int main(int argc, const char** argv)
{
  // (4)
  auto const fn{ [](int const argc, char const **argv) {
    // (5)
    jank_load_clojure_core_native();
    jank_load_clojure_core();

    // (6)
    jank_load_jank_test();
    auto const the_function(jank_var_intern_c("jank-test", "some-code"));
    jank_call0(jank_deref(the_function));

    return 0;
  } };

  // (3)
  jank_init(argc, argv, true, fn);
  return 0;
}

Lots of things here, so let’s go one by one: in (1), we declared an “external” reference. When Jank compiles a code, it’ll generate these jank_load_<namespace> which will do what it’s supposed to do: load the namespace. Unfortunately, it won’t actually load Clojure Core’s namespace, nor it will load any dependencies (I told you this isn’t officially supported yet! You have been warned!). The “external” will be resolved at linker time, and right now it resides only on the .o intermediate file. In (2) we’ll define some data we’ll need to use later in more “extern” declarations. These are used, again, to refer the Jank library that will be linked with the code.

Now, in (3) (which is the second to last line of actual code) we’ll init the Jank runtime. This will boostrap the “Clojure”-ish classes defined in Jank, and we’ll need to pass a fn argument, that is defined previously in `(4). Without this step, you will get a segfault trying to run Jank code, so this is absolutely necessary.

In (5) we will load “Native core” and “Clojure core”, meaning we’ll start the core libraries that are builtin in Jank (native) and these will be used to implement the clojure.core namespace using Clojure code; in (6), we’ll also load our namespace – the one we defined in our .jank file. And finally, after loading this namespace we’re ready to call our function – we’ll first create a “jank var” using jank_var_intern_c, that will essentially resolver to '#jank-test/some-code, and then we’ll deref it to get back the function. We use jank_call0 to call a function with arity 0, and obviously we can use jank_call1 or jank_call2 if the function have arity 1 or 2, for example.

Finally, to compile your final binary:

clang++ \
  -L/usr/local/lib/jank/0.1/lib/ \
  -I/usr/local/lib/jank/0.1/include \
  test.cpp \
  target/x86_64-unknown-linux-gnu-6edc6e02e1bf8d875f77f87b5820996901c1894b142485e01a7785f173afb8df/jank_test.o \
  -lclang-cpp \
  -lLLVM \
  -lz \
  -lzip \
  -lcrypto \
  -l jank-standalone \
  -o program

So, test.cpp is the code we just created, and target/x86..../jank_test.o is the intermediate file that Jank compiled. The -L and -I point to directories where Jank was installed, -o program instructs the compiler to output to program, and the rest are just libraries that we need to link to generate the final binary. And that is it – running that binary will print HELLO on the screen!

But of course, Jank can do that by itself, so…

But Why?

Supposing we’re working in some language – for example Ruby – and we want to optimize some code, or integrate with some native library. The canonical way to do that is to use C or C++, sometimes Rust, to make the library. Now, how would we create a class – let’s say Jank – on C++, for it to be usable in Ruby? It’s quite simple, in fact:

#include &lt;ruby.h&gt;

extern &quot;C&quot; void Init_jank_impl() {
  rb_define_class(&quot;Jank2&quot;, rb_cObject)
}

That’s literally just it. Now, suppose we want to send this to Jank, so we will define the class in Jank – how could we do that? Well, it’s also very simple: we will use the same technique in this post, but instead of defining a main code, we’ll keep the extern... code and move all the code that was supposed to be on main to this Init_jank_impl code. Then, on Jank side, we’ll add:

(ns jank-impl)

(defn init-extension []
  (cpp/rb_define_class &quot;Jank&quot; cpp/rb_cObject))

That’s it. Can we create Ruby methods, and do more stuff with this? Hopefully! But not right now: while I was trying this approach, I found some bugs in Jank, so until these bugs get fixed (which I suspect, based on how fast the language is evolving, will be very fast) we can’t.

But this might even open some very interesting possibilities, that I expect to expand on a future post!

Permalink

Clojure Deref (Nov 6, 2025)

Welcome to the Clojure Deref! This is a weekly link/news roundup for the Clojure ecosystem (feed: RSS).

The State of ClojureScript 2025 Survey is live!

If you ever wondered what’s happening in cljs world, this is your chance to contribute and learn back from the community. Take a few minutes to fill out the survey and share it in your circles.

Upcoming Events

Blogs, articles, and news

Libraries and Tools

Debut release

  • moon - RPG Maker & Engine

  • reagami - A minimal zero-deps Reagent-like for Squint and CLJS

  • aero-1p - Bridge between Aero and 1Password

  • litellm-clj - A universal translator for LLM models

  • muutos - Muutos is a zero-dependency Clojure library for reacting to changes in a PostgreSQL database.

  • webserial-starter - WebSerial API starter with Clojurescript and Replicant

  • clj-threats - Clojure implementation of Threagile

  • qclojure-ml - Quantum Machine Learning based on QClojure

  • DSCloj - Structured LLM prompts in Clojure

  • clojure-mcp-light - Experimental Clojure tooling for Claude Code - automatic delimiter fixing via hooks and parinfer

Updates

  • tools.build 0.10.11 - Clojure builds as Clojure programs

  • statecharts 1.2.24 - A Statechart library for CLJ(S)

  • fulcro-inspect 4.1.0 - A tool for inspecting and debugging Fulcro applications during development.

  • fulcro-devtools-remote 0.2.8 - An adapter for writing development tooling that runs as a Chrome extension or an electron app.

  • test-filter 1.0.6 - A tool for reducing CI times by finding minimal test set based on code analysis.

  • nexus 2025.10.1 - Data-driven action dispatch for Clojure(Script): Build systems that are easier to test, observe, and extend

  • powerpack 2025.10.22 - A batteries-included static web site toolkit for Clojure

  • clj-kondo 2025.10.23 - Static analyzer and linter for Clojure code that sparks joy

  • component 1.2.0 - Managed lifecycle of stateful objects in Clojure

  • spacemacs-config 2025-10-25 - rich Clojure & LSP config for Spacemacs

  • dompa 1.1.0 - A zero-dependency, runtime-agnostic HTML parser and builder.

  • markdown 0.7.196 - A cross-platform clojure/script parser for Markdown

  • pedestal 0.8.1 - The Pedestal Server-side Libraries

  • cli 1.27.121 - Opinionated command line argument handling, with excellent support for subcommands

  • edamame 1.5.33 - Configurable EDN/Clojure parser with location metadata

  • thneed 1.1.4 - An eclectic set of Clojure utilities that I’ve found useful enough to keep around.

  • calva-backseat-driver 0.0.24 - VS Code AI Agent Interactive Programming. Tools for CoPIlot and other assistants. Can also be used as an MCP server.

  • qclojure 0.23.0 - A functional quantum computer programming library for Clojure with backend protocols, simulation backends and visualizations.

  • malli 0.20.0-alpha3 - High-performance data-driven data specification library for Clojure/Script.

  • clay 2.0.2 - A REPL-friendly Clojure tool for notebooks and datavis

  • kindly 4-beta21 - A small library for defining how different kinds of things should be rendered

  • durable-queue 0.2.0 - a disk-backed queue for clojure

  • joyride 0.0.71 - Making VS Code Hackable like Emacs since 2022

  • reagent 2.0.1 - A minimalistic ClojureScript interface to React.js

  • sente 1.21.0 - Realtime web comms library for Clojure/Script

  • http-kit 2.9.0-beta3 - Simple, high-performance event-driven HTTP client+server for Clojure

  • cider 1.20.0 - The Clojure Interactive Development Environment that Rocks for Emacs

  • tempel 1.0.0 - Data security framework for Clojure

  • eca 0.77.1 - Editor Code Assistant (ECA) - AI pair programming capabilities agnostic of editor

  • carmine 3.5.0 - Redis client + message queue for Clojure

  • clojure-mcp 0.1.12 - Clojure MCP

  • manifold 0.4.4 - A compatibility layer for event-driven abstractions

  • squint 0.9.178 - Light-weight ClojureScript dialect

  • calva 2.0.540 - Clojure & ClojureScript Interactive Programming for VS Code

  • cursive 2025.2.1-eap4 - Cursive: The IDE for beautiful Clojure code

Permalink

Merco Talento Brazil 2025: Nubank ranks among the top 5 most attractive companies to work for

In 2025, Nubank appears for the first time among the five most attractive companies to work for in Brazil. The list is organized by Merco Talento, one of the leading corporate reputation and employer brand rankings in Latin America.

Climbing ten positions from 2024 to 2025 reinforces that investing in autonomy, trust, and a clear purpose isn’t just a cultural choice — it’s what drives real impact inside and outside the company.

As our CEO and Founder, David Vélez, puts it:

“Culture attracts people — and people build the products that attract customers. In the end, customers are consumers of culture. […] Culture is like the company’s spirit — it permeates everything we do.”

At Nubank, culture is more than a statement — it’s a living force that shapes how we hire, build, and grow. And this recognition reflects exactly that.

What is Merco Talento and why it matters

Merco Talento, the Corporate Reputation Business Monitor, evaluates how attractive companies are as places to work based on the perception of different audiences such as university students, professionals, human resources specialists, unions, headhunters and society in general.

In addition to spontaneous recognition, the ranking combines information about corporate reputation, well being practices, professional development, organizational culture and social purpose.

In other words, it is not just an employer branding award. It reflects trust, credibility and the ability to inspire people.

Inside the methodology

The methodology used to build the Merco Talento ranking includes:

  • More than 9,000 interviews with different groups such as professionals, students, business leaders, human resources specialists and the general public.
  • Evaluation of 26 variables grouped into three main pillars: strong internal reputation, employer brand and quality of life at work.
  • External audit conducted by KPMG, which ensures independence and methodological integrity.

This combination of multiple perspectives is what makes the ranking solid and respected in countries like Brazil, Mexico and Colombia, where Nubank has been recognized before.

A culture built every day

Being among the top 5 most desired companies to work for in Brazil is the result of everyday decisions.

We got here because we work with autonomy and responsibility in teams that genuinely trust people and the decisions they make. We create a safe environment to learn, make mistakes and grow, without rigid hierarchies that limit initiative or creativity.

Diversity and inclusion are true foundations of the way we build products, develop leaders and form teams. And everything connects to a clear purpose: to fight complexity and empower people to have more control over their financial lives.

The post Merco Talento Brazil 2025: Nubank ranks among the top 5 most attractive companies to work for appeared first on Building Nubank.

Permalink

Daily Artificial Intelligence Digest - Nov 04, 2025

AI Infrastructure & Strategic Partnerships

Major cloud providers are solidifying their positions as the backbone for AI development, with AWS and OpenAI partnership cementing a substantial cloud computing deal, further detailed by OpenAI and Amazon cloud deal, AWS supports OpenAI workloads, and CNBC on the deal. Microsoft continues its global expansion of AI cloud capacity through a multi-billion-dollar Lambda AI infrastructure deal and a Microsoft Australia AI cloud deal, alongside a strategic Microsoft UAE AI investment aimed at advancing US AI diplomacy. The sheer demand for AI compute power highlights significant challenges, as Microsoft's AI GPU electricity needs are growing, while Elon Musk controversially suggests leveraging Tesla cars as AI computers for a massive distributed computing network.

AI Model Advances & Enterprise Applications

Advancements in AI models continue, with Anthropic Claude research making progress and Apple's Siri-Google Gemini deal reportedly exploring integration to enhance personal AI capabilities. Tools for building and monitoring stateful LLM agents are also emerging, exemplified by Agent-O-Rama for LLM agents for Java and Clojure development. Beyond core development, AI is rapidly finding diverse applications, from Coke's AI holiday ad to Elon Musk's Grokipedia, which is undergoing Grokipedia academic assessment, while content creators like PewDiePie self-hosting AI are delving into building custom models.

AI Governance, Ethics & Business Dynamics

The expanding reach of AI is fueling discussions around data rights and ethical considerations, with publishers oppose OpenAI training pushing back against OpenAI's use of their work and LinkedIn AI training privacy plans raising concerns and prompting calls for opt-out mechanisms. Business leaders like Sam Altman are addressing financial transparency amidst speculation about Sam Altman on OpenAI IPO and Sam Altman on OpenAI revenue. Concerns about the potential for AI-driven cyber threats are also rising, alongside the ongoing debate about appropriate design and user interaction with AI hardware, as one notable test proposes assessing if AI hardware user experience devices provoke a desire to physically interact aggressively.

Permalink

Introducing Agent-o-rama: build, trace, evaluate, and monitor stateful LLM agents in Java or Clojure

We’ve just open-sourced Agent-o-rama, a library for building scalable and stateful LLM agents on the JVM. Agent-o-rama provides two first-class APIs, one for Java and one for Clojure, with feature parity between them.

AI tooling today is overwhelmingly centered on Python, and while the JVM ecosystem has seen growing support through libraries like LangChain4j, it lacks the kind of integrated tooling that lets developers evaluate, observe, and deploy LLM-based systems rigorously and at scale. Available tools are fragmented or complex to set up, and nothing handles the entire workflow from development to production with proper observability.

Agent-o-rama fills that gap. It brings the same ideas popularized by LangGraph and LangSmith – structured agent graphs, tracing, datasets, experiments, evaluation – but makes them native to Java and Clojure. LLMs are powerful but inherently unpredictable, so building applications with LLMs that are helpful and performant with minimal hallucination requires being rigorous about testing and monitoring.

Agents are defined as simple graphs of Java or Clojure functions that execute in parallel. Agent-o-rama automatically captures detailed traces and includes a web UI for offline experimentation, online evaluation, and time-series telemetry (e.g. model latency, token usage, database latency). It also supports streaming, with a simple client API to stream model calls or other outputs from nodes in real time. Agent-o-rama extends the ideas from LangGraph and LangSmith with far greater scalability, full parallel execution, and built-in high-performance data storage and deployment.

Agent-o-rama is deployed onto your own infrastructure on a Rama cluster. Rama is free to use for clusters up to two nodes and can scale to thousands with a commercial license. Every part of Agent-o-rama is built-in and requires no other dependency besides Rama. Agent-o-rama also integrates seamlessly with any other tool, such as databases, vector stores, external APIs, or anything else. Unlike hosted observability tools, all data and traces stay within your infrastructure.

Example agent

Let’s take a look at an example agent! This is a research agent from the examples/ directory in the project. In that directory you’ll find equivalent Java and Clojure versions.

You’ll need Java 21 installed and API keys for OpenAI and Tavily (Tavily’s free tier is sufficient). Put the API keys in environment variables like so:

1
2
export OPENAI_API_KEY=your_openai_key_here
export TAVILY_API_KEY=your_tavily_key_here

To run the agent, clone Agent-o-rama and follow these instructions (for Java or Clojure, whichever you prefer):

1
2
3
4
5
6
7
8
9
# Java instructions
cd examples/java
./run-example com.rpl.agent.research.ResearchAgentExample

# Clojure instructions
cd examples/clj
lein repl
(require '[com.rpl.agent.research-agent :as research-agent])
(research-agent/run-agent)

This runs Rama’s “in-process cluster” (IPC) and launches the research agent on it. You’ll get a prompt at the terminal to enter a research topic. The agent will generate a set of analyst personas to analyze the topic, and you’ll be prompted again whether you want to give feedback on the generated analysts. Once you tell the agent you have no more feedback, it will spend a few minutes generating the report, including using information it finds through web searches and through Wikipedia, and then the final report will be printed.

As the report is being generated or when it’s finished, you can open the Agent-o-rama UI at http://localhost:1974 .

Here’s an example back and forth:

Enter a topic: What's the influence and legacy of Billy Wilder?

Do you have any feedback on this set of analysts? Answer 'yes' or 'no'.

{"role" "Film Historian", "affiliation" "University of California, Los Angeles", "name" "Dr. Lucy Reynolds", "description" "Specializes in post-war American cinema and the contributions of filmmakers like Wilder. Focuses on Wilder's stylistic innovations and narrative techniques, exploring how they shaped modern filmmaking."}
{"role" "Cultural Critic", "affiliation" "Film Critic Magazine", "name" "Michael Chen", "description" "Analyzes the social and cultural impacts of Wilder's films, particularly in relation to gender and race issues. Concerned with how Wilder's work reflects and influences societal norms."}
{"role" "Cinema Studies Scholar", "affiliation" "New York University", "name" "Professor John Hartman", "description" "Investigates the legacy of classic Hollywood directors, with an emphasis on Wilder. His work focuses on the interplay between commercial success and artistic integrity in Wilder's films."}
{"role" "Screenwriter and Director", "affiliation" "Independent Filmmaker", "name" "Emma Thompson", "description" "Explores the thematic elements in Wilder's storytelling, particularly humor and satire. Engages with Wilder's ability to blend genres and how this influences contemporary narrative structures."}
>> no

# The Enduring Influence of Billy Wilder

## Introduction

Billy Wilder's legacy in Hollywood cinema is marked by his unparalleled ability to blend commercial success with artistic integrity. This report delves into Wilder's impact, highlighting his innovative storytelling techniques and social critiques through iconic films like "Sunset Boulevard," "The Apartment," and "Double Indemnity." We explore how his personal experiences shaped his keen observational skills and narrative style, as well as how his work laid the groundwork for contemporary storytelling and the exploration of gender dynamics. Ultimately, Wilder’s films illustrate the enduring relevance of balancing humor, critique, and emotional depth in cinema.

---

Billy Wilder stands as a towering figure in cinema, adept at fusing commercial viability with artistic integrity. His films often strike a delicate balance between engaging mainstream audiences and provoking critical reflection on serious themes, as exemplified in "Sunset Boulevard" (1950). This film vividly critiques the dark side of fame and highlights Wilder's unique ability to craft narratives that resonate deeply with viewers while navigating complex moral landscapes. His background as an Austrian émigré and early career as a screenwriter supplied him with the observational prowess necessary to convey the multifaceted nature of human experiences, allowing his work to transcend mere entertainment and engage with profound social commentary [1].

Wilder's amalgamation of humor and satire serves as a compelling vehicle for addressing serious social issues, influencing contemporary screenwriters to adopt similar techniques. Films like "Some Like It Hot" and "The Apartment" showcase his signature style, where humor enriches the narrative while prompting reflection on societal norms and human behavior. This approach remains pervasive in the works of modern filmmakers, illustrating Wilder's constructed legacy in storytelling that encourages the interplay of comedic elements and deeper thematic explorations. Notable contemporary films such as "The Big Sick" and "Parasite" echo these traditions, suggesting that humor can coexist with critical commentary and profound moral questions [2].

Central to Wilder's storytelling innovations is his ability to meld humor with dark themes, employing non-linear narratives and flashbacks in movies like "Double Indemnity" and "The Apartment." These techniques reveal complex character motivations and provide a framework for rich, layered narratives. Wilder’s knack for sharp dialogue and intricate comedic timing enhances this social commentary, resonating with audiences across generations. The blend of genres within his films also paved the way for a more diverse cinematic landscape, allowing modern filmmakers to challenge conventions and push creative boundaries [3].

Particularly significant is Wilder's exploration of gender dynamics in "The Apartment," where the protagonist Fran Kubelik's experiences reflect the challenges faced by women within a patriarchal corporate structure. The film critiques the objectification of women through key scenes and deft cinematography, simultaneously highlighting moral ambiguity and emotional depth. This examination of gender roles emphasizes the importance of authentic relationships in a transactional world, underlining the resonance of Wilder's critiques within contemporary discussions surrounding gender and power [4].

In conclusion, Billy Wilder's influence is multifaceted, shaping both the narrative and thematic dimensions of modern cinema. His legacy emerges from an enduring ability to captivate audiences while addressing the intricacies of human behavior, societal constructs, and moral dilemmas. Through a unique blend of artistry and commercial appeal, Wilder set a standard for storytelling that continues to inspire filmmakers and storytellers today.


---

## Conclusion

Billy Wilder's cinematic legacy is a testament to his exceptional ability to balance artistry and commercial appeal. His films, including "Sunset Boulevard," "The Apartment," and "Double Indemnity," not only entertained audiences but also provoked critical thought on profound societal themes and human dynamics. Through innovative storytelling techniques and a distinctive blend of humor and critique, Wilder paved the way for contemporary writers and filmmakers. His enduring influence can be seen in the way modern narratives confront gender dynamics and moral complexities, demonstrating that engaging storytelling can exist alongside rich thematic exploration. Ultimately, Wilder's impact remains a vital reference point in the evolution of cinema.

## Sources
[1] Interview with Professor John Hartman on the legacy of Billy Wilder.  
[2] https://glcoverage.com/2025/01/23/billy-wilder-screenwriting-tips/  
[3] Culture Vulture | Counter Culture  
[4] Breaking Down the Storytelling in Billy Wilder's 'The Apartment' https://nofilmschool.com/apartment-storytelling-breakdown  
[5] Analysis of ‘The Apartment’ – Infinite Ocean - Mawr Gorshin  https://mawrgorshin.com/2022/08/20/analysis-of-the-apartment/  
[6] On its 60th anniversary, Billy Wilder’s The Apartment looks like an indictment of toxic masculinity - AV Club  https://www.avclub.com/on-its-60th-anniversary-billy-wilder-s-the-apartment-l-1844004988  

If you click on the research agent in the UI, you’ll see this:

The invoke there is what we just ran. Clicking on it brings up the trace for the invoke:

This is displaying the parallel execution of the agent, with orange nodes being aggregations of data computed on multiple branches. On the right is aggregated statistics of everything that happened during the agent’s execution. You can see how many tokens it used, and if it did any database reads/writes you’d see stats about those too. If the agent invokes other agents, you can see a breakdown of stats by agent as well.

Clicking on the “write-report” node brings up a detailed trace of what happened when that node executed:

This node did one LLM call, and you can see the arguments to that LLM, what was returned, and stats on the call in the “Operations” section. The code for this node is just this:

JavaClojure
1
2
3
4
5
6
7
8
9
.node("write-report", "finish-report", (AgentNode agentNode, String sections, String topic) -> {
  ChatModel openai = agentNode.getAgentObject("openai");
  String instructions = String.format(REPORT_WRITER_INSTRUCTIONS, topic, sections);
  List<ChatMessage> chatMessages = Arrays.asList(
    new SystemMessage(instructions),
    new UserMessage("Write a report based upon these memos."));
  String report = openai.chat(chatMessages).aiMessage().text();
  agentNode.emit("finish-report", "report", report);
})
1
2
3
4
5
6
7
8
9
10
11
12
(aor/node
 "write-report"
 "finish-report"
 (fn [agent-node sections topic]
   (let [openai (aor/get-agent-object agent-node "openai")
         instr  (report-writer-instructions topic sections)
         text   (chat-and-get-text
                 openai
                 [(SystemMessage. instr)
                  (UserMessage. "Write a report based upon these memos.")])]
     (aor/emit! agent-node "finish-report" "report" text)
   )))

This code says that the node’s name is “write-report”, the node emits to the node “finish-report”, and the node’s implementation is the given function. The agentNode / agent-node argument is how you interact with the graph to return a result, emit to other nodes, or get agent objects like models, database connections, or anything else. When you emit to other nodes, you simply say what node you want to emit to and what arguments to pass to that node. Agent nodes run on virtual threads, so they can be efficiently written in a blocking style like this.

That’s most of what’s involved in programming agents with Agent-o-rama! There’s a bit more to learn with aggregation and how to declare agent objects, and this is all documented on the programming agents guide. The rest of using Agent-o-rama is creating and managing datasets, running experiments, setting up online evaluation and other actions on production runs, and analyzing agent telemetry.

Also, you can see from this code and the trace that model calls are automatically traced – this node didn’t have to record any tracing info explicitly. Though you can include your own info in traces with a simple API (see this Javadoc and this Clojuredoc).

Let’s take a look at running this on a real cluster! Let’s quickly set up a cluster locally by following these instructions:

  1. Download the latest Rama release from here.
  2. Unpack the release somewhere.
  3. Run: ./rama devZookeeper &
  4. Run: ./rama conductor &
  5. Run: ./rama supervisor &
  6. Visit: http://localhost:8888 . When the page loads, the cluster is ready.
  7. Download the latest Agent-o-rama release from here.
  8. Unpack it somewhere.
  9. Run: ./aor --rama /path/to/rama-root &

Next, to deploy you need to build a jar first. Here’s how to build either the Java or Clojure version from the Agent-o-rama project:

1
2
3
4
5
6
7
# For Java version  
cd examples/java
mvn clean package -Dmaven.test.skip=true

# For Clojure version
cd examples/clj
lein uberjar

The Java version will build target/java-examples-with-dependencies.jar , and the Clojure version will build target/agent-o-rama-examples-1.0.0-SNAPSHOT-standalone.jar .

Next, to deploy the module just run this command:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# Deploy the module (Java uberjar)
./rama deploy \
  --action launch \
  --jar /path/to/java-examples-with-dependencies.jar \
  --module com.rpl.agent.research.ResearchAgentModule \
  --tasks 4 \
  --threads 2 \
  --workers 1

# Deploy the module (Clojure uberjar)
./rama deploy \
  --action launch \
  --jar /path/to/agent-o-rama-examples-1.0.0-SNAPSHOT-standalone.jar \
  --module com.rpl.agent.research-agent/ResearchAgentModule \
  --tasks 4 \
  --threads 2 \
  --workers 1

Now it’s up and running! You can view the agent in the UI at http://localhost:1974 and play with it. From the agent screen you can invoke the agent with the arguments ["", {"topic": "your topic here"}] . On the trace, you’ll be able to see any human input prompts the agent makes and respond to them there.

Rama handles all of storage, deployment, and scaling. There are no other dependencies needed to run this. Setting up a production cluster is only slightly more work, and there are one-click deploys for AWS and for Azure.

Resources

Check out these resources to learn more or get involved:

Conclusion

Agent-o-rama lets developers gain the benefits of Rama without needing to learn it. Rama’s distributed programming model is powerful but has a learning curve: it introduces a rich dataflow API and uses compound data structures for indexing instead of fixed data models. Agent-o-rama abstracts away those concepts into a familiar API so developers can take advantage of Rama’s strengths for the specific domain of building LLM agents.

For those who want to learn how to program Rama directly, Agent-o-rama also serves as a great example of Rama in practice. The backend is about 15K lines of code and the front-end about 11K, yet together they form a complete, end-to-end distributed system with a diverse feature set. Along with our Twitter-scale Mastodon implementation, it shows the breadth of what can be built with Rama.

We’d love to hear what you build with Agent-o-rama. Join the rama-user mailing list or the #rama channel on the Clojurians Slack to ask questions, share feedback, or discuss ideas with others using Agent-o-rama.

If you’d like to talk directly with us about Agent-o-rama, whether to exchange ideas, get technical guidance, or explore working together on building an LLM agent, you can book a call with us.

Permalink

Functional programming, demystified: What we learned at Nu Engineering Meetup

At Nu Engineering Meetup #15, functional programming shed its “niche” label and became a tangible practice. Alberto Souza, Software Engineering at Nubank and creator of the Dev + Eficiente ecosystem, opened the session by connecting programming paradigms to everyday design decisions.

Rafael Ferreira, Senior Python Developer, educator at Rocketseat, and founder of the Programador Lhama initiative, shared a perspective on functional architecture that begins in theory and translates into testable, predictable code. Marcelo Arbore, Director of Engineering at Oracle Brazil with over fifteen years of experience in hybrid and distributed cloud environments, presented an experiment combining Clojure, Datomic, and Oracle 23AI for vector search and multimodel data applications.

This post brings together the key ideas and addresses a recurring question among those who look at Nubank and wonder if they’d fit in without knowing functional languages.

First, a good question

This question often comes up in interviews, events, and blog comments: “Do I need to know Clojure to work at Nubank?” The answer is as straightforward as the question itself: no. What we look for is curiosity, solid foundations, and the willingness to learn.

The language is a tool that serves the engineering principles we value. This discussion has appeared in community conversations, such as in Alex Miller’s interview on the Hammock Podcast, where he spoke about learning journeys and framed language choices as means, not ends. You can explore that conversation here.

What is functional programming, and why does it matter

Functional programming is an approach that favors immutable structures, pure functions, and predictable composition. It gives engineers greater clarity about what changes and where those changes occur.

In practical terms, it means that data transformations return new values instead of updating variables in place, that side effects are concentrated, and that the path of data can be read as a pipeline.

An accessible introduction to this way of thinking can be found in Functional Programming with Clojure, which shows how these principles translate into design decisions that make testing, parallelism, and maintenance easier.

How this translates into Clojure at Nubank

Clojure is our main language in many systems. The choice aligns with our emphasis on immutability and our use of history-oriented databases such as Datomic.

This story has already been told in Clojure’s Journey at Nubank, where we detailed the technical and cultural reasons for adopting the ecosystem. The decision doesn’t create an exclusive club—it builds an environment that encourages focus on business rules, clarity of effects, and responsible experimentation.

For those who want to dive deeper behind the scenes, the special series Clojure Turns 15 captures discussions about the language’s evolution and its impact on our daily work.

Joining without knowing Clojure is possible

Many people who joined Nubank had never written Clojure before. During his talk, Alberto Souza shared how he learned from scratch and how functional principles began to influence his code in other languages.

The main message is liberating: paradigms are not dogmas—they are lenses. You can apply immutability, cohesion, and pure functions in Java, Python, or JavaScript, just as you can bring concepts of cohesion and domain-driven design into Clojure.

On the Hammock Podcast, we explore this technical and personal journey in the episode Journeys in Code: Clojure, Datomic, and Personal Growth. The conclusion is that learning the language comes naturally when you have the right context, peers nearby, and interesting problems to solve.

From the whiteboard to production code

The three presentations at the meetup show how theory translates into practice.

Alberto highlighted how immutability simplifies debugging and reduces bugs by concentrating mutations in single points of the flow. Rafael presented functional architecture patterns in Python, such as service handle and tagless final, which isolate effects and maintain system predictability.

Marcelo, on the other hand, demonstrated an experiment combining Clojure, Datomic, and Oracle 23AI to build a vector search service with embeddings—proof that the functional paradigm can coexist with modern data and AI technologies.

These approaches reflect an engineering philosophy driven by simplicity and clarity. Every decision—from pure functions to declarative pipelines—is designed to keep systems understandable in the long run.

Learning as part of the work

When we say that you don’t need to know Clojure to participate in our hiring process, we’re also talking about how we support learning from the very first day. The technical onboarding is structured to combine product context, experienced peers, and a safe environment to ask questions, explore, and make mistakes.

During the first few weeks, new joiners are exposed to concepts from the functional ecosystem and learn hands-on through pair programming, code reviews, and mentorship. The learning curve exists—and it’s shared.

What changes in practice

Working in a functionally oriented environment is about transforming the way you think.

  • Predictability: States are controlled, and concurrency bugs are drastically reduced.
  • Testability: Pure functions make automation and unit testing easier.
  • Readability: Declarative pipelines make data flows clearer.
  • Evolution: Deliberate cohesion reduces the impact of changes in the long term.

These principles cut across languages and paradigms, explaining why functional engineering is a mindset that shapes how we build products at Nubank.

Functional programming is an invitation to think more clearly about data, effects, and the evolution of systems. At Nubank, we’ve built an environment that encourages continuous learning, collaboration, and safe experimentation.

If you identify with this way of building, this is a great place to begin your journey.

The post Functional programming, demystified: What we learned at Nu Engineering Meetup appeared first on Building Nubank.

Permalink

Gaiwan: October Recap

MCP-SDK Released

Gaiwan: October Recap

New blog post! mcp-sdk: an Introduction to creating an MCP service with Clojure.
Last month we released mcp-sdk, a pure Clojure SDK for working with MCPs. If you&aposd like to create your own MCP service, check out our blog post to help you get started.

What&aposs in a name?

Most of our open source projects carry the Lambda Island name. You find them under lambdaisland on github, and under [com.]lambdaisland on clojars. Lambda Island is the name I chose when in 2017 I decided to get into screencasting, making premium video tutorials about Clojure. These videos are still online, nowadays you can watch them all for free.

The first library I released under the same name was lambdaisland/uri, a small utility I extracted from the code base that runs the lambdaisland website. Many more libraries and tools would follow. Kaocha (2018), Ornament (2021), Launchpad (2021), Plenish (2022), CLI (2024), just to name a few highlights.

Since 2019 the maintainance and stewardship of what are by now literally dozens of projects falls upon Gaiwan colleagues and me. This is a collection of open source that Gaiwan proudly shares for the betterment of the Clojure community, but the old name has stuck. I&aposve never been quite sure what to do with that. People would tell me I should rename Gaiwan to Lambda Island to benefit from the name recognition, or go the other way, and migrate all these projects over to the Gaiwan team and organisation. I will agree this confusion of names has not done us any favors.

For me there&aposs always been a clear distinction though. Lambda Island is not an official entity, but if it was, it would be a non-profit. It&aposs our connection to the community, hence why Lambda Island has an opencollective page, or why we run the ClojureVerse forum. There&aposs no commercial motive here, rather it&aposs in our DNA to give back, to share, and to strengthen the community and ecosystem we benefit from. I guess it&aposs my own anti-corporate tendencies that have always wanted to keep that separate from the business, even though Gaiwan is about as indy as it gets. A handful of people running a bootstrapped business.

Lately however we have at last started releasing newer stuff directly under the Gaiwan name, notably our in-progress IAM implementation called Oak. This is a project that distills years of consulting experience, and so it felt right to put our own name on it. A mark of the maker. Oak is also a starting point for us to explore commercial possibilities in the identity space. If that sounds like something you&aposd like to chat to us about, get in touch!

Gaiwan: October RecapReset password screenshot from Oak, our IAM implementation

Coming Up

Arne will do an online talk about The Gaiwan Stack on November 11, 18:30 London / 19:30 CET. Gaiwan has built a lot of Clojure applications over the years, and we&aposve developed an opinionated stack and tooling. It&aposs overdue that we share more of these learnings.

What We Are Reading

  • Europe&aposs plan to ditch US tech giants is built on open source - and it&aposs gaining steam &apos Digital Sovereignity is a hot topic in Europe, and it&aposs something we&aposve been having a lot of conversations about inside the Gaiwan team as well. We&aposve started the process of migrating from Github to our own Forgejo instance. It&aposs a space we are actively exploring to see if we can help European tech companies break their dependency on US clouds.
  • The Majority AI View Some of you may have read the post from our founder back in September where he explains his view on AI and some of the cognitive dissonance it causes (link). While we do keep an eye on these technologies and try to evaluate their worth, like the people in this article we are concerned and sceptical as well.
  • Your data model is your destiny: "when code is cheap, competition is fierce, and vertical depth matters, your data model is the foundation of your moat. The companies that win won’t be those with the most or even the best features. AI will democratize those. The winners will be built on a data model that captures something true about their market, which in turn creates compounding advantages competitors can’t replicate."

Permalink

OSS updates September and October 2025

In this post I&aposll give updates about open source I worked on during September and October 2025.

To see previous OSS updates, go here.

Sponsors

I&aposd like to thank all the sponsors and contributors that make this work possible. Without you, the below projects would not be as mature or wouldn&apost exist or be maintained at all! So a sincere thank you to everyone who contributes to the sustainability of these projects.

gratitude

Current top tier sponsors:

Open the details section for more info about sponsoring.

Sponsor info

If you want to ensure that the projects I work on are sustainably maintained, you can sponsor this work in the following ways. Thank you!

Updates

The summer heat has faded, and autumn is upon us. One big focus for me is preparing my talk for Clojure Conj 2025, titled "Making tools developers actually use". I did a test run of the talk at the Dutch Clojure Meetup. It went a bit too long at 45 minutes, so I have to shrink it almost by half for the Conj. The more I work on the talk the more ideas come up, so it&aposs challenging!

presentation at Dutch Clojure meetup

Of course I spent a ton of time on OSS the past two months as well. Some special mentions:

  • I&aposm pretty excited by Eucalypt, a remake of Reagent for Squint without React by Chris McCormick. It lets you build UIs with the Reagent API in less than 10kb of gzip&aposed JS. The code was initially generated by an LLM, but now work is going into making the code base thoroughly tested and simplified where possible.
  • After studying Eucalypt&aposs code I figured that making an even more minimal Reagent-like by hand would be fun. This is where I came up with Reagami. The API looks like a hybrid between Reagent and Replicant. You can build apps with Reagami starting around 5kb gzip&aposed.
  • Edamame got Clojure CLR support thanks to Ambrose Bonnaire-Sergeant.
  • SCI Clojure CLR support is underway. The sci.impl.Reflector code, based on clojure.lang.Reflector was ported to Clojure with the purpose that it would then be easier to translate to Clojure CLR.
  • Cljdoc chose squint for its small bundle sizes and easy migration off of TypeScript towards CLJS
  • Via work on Squint, I found a way to optimize str in ClojureScript (worst case 4x, best case 200x)

Here are updates about the projects/libraries I&aposve worked on in the last two months in detail.

  • babashka: native, fast starting Clojure interpreter for scripting.

    • Bump to clojure 1.12.3
    • #1870: add .addMethod to clojure.lang.MultiFn
    • #1869: add clojure.lang.ITransientCollection for instance? checks
    • #1865: support reify + equals + hashCode on Object
    • Add java.nio.charset.CharsetDecoder, java.nio.charset.CodingErrorAction, java.nio.charset.CharacterCodingException in support of the sfv library
    • Fix nrepl-server completions and lookup op to be compatible with rebel-readline
    • Add clojure.lang.Ref for instance? checks
    • Bump SCI: align unresolved symbol error message with Clojure
    • Use GraalVM 25
    • Bump deps.clj to 1.12.3.1557
    • Change unknown or REPL file path to NO_SOURCE_PATH instead of <expr> since this can cause issues on Windows when checking for absolute file paths
    • #1001: fix encoding issues on Windows in Powershell. Also see this GraalVM issue
    • Fixes around java.security and allowing setting deprecated Cipher suites at runtime. See this commit.
    • Support Windows Git Bash in bash install script
  • SCI: Configurable Clojure/Script interpreter suitable for scripting

    • ClojureCLR support in progress (with Ambrose Bonnaire Sergeant)
  • edamame: configurable EDN and Clojure parser with location metadata and more

    • 1.5.33 (2025-10-28)
    • Add ClojureCLR support (@frenchy64)
  • clj-kondo: static analyzer and linter for Clojure code that sparks joy.

    • Unreleased
    • #2651: resume linting after paren mismatches
    • 2025.10.23
    • #2590: NEW linter: duplicate-key-in-assoc, defaults to :warning
    • #2639: NEW :equals-nil linter to detect (= nil x) or (= x nil) patterns and suggest (nil? x) instead (@conao3)
    • #2633: support new defparkingop macro in core.async alpha
    • #2635: Add :interface flag to :flags set in :java-class-definitions analysis output to distinguish Java interfaces from classes (@hugoduncan)
    • #2636: set global SCI context so hooks can use requiring-resolve etc.
    • #2641: fix linting of def body, no results due to laziness bug
    • #1743: change :not-empty? to only warn on objects that are already seqs
    • Performance optimization for :ns-groups (thanks @severeoverfl0w)
    • Flip :self-requiring-namespace level from :off to :warning
    • 2025.09.22
    • Remove dbg from data_readers.clj since this breaks when using together with CIDER
    • 2025.09.19
    • #1894: support destruct syntax
    • #2624: lint argument types passed to get and get-in (especially to catch swapped arguments to get in threading macros) (@borkdude, @Uthar)
    • #2564: detect calling set with wrong number of arguments
    • #2603: warn on :inline-def with nested deftest
  • squint: CLJS syntax to JS compiler

    • Support passing keyword to mapv
    • Inline identical? calls
    • Clean up emission of paren wrapping
    • Add nat-int?, neg-int?, pos-int? (@eNotchy)
    • Add rand
    • Fix rendering of null and undefined in #html
    • #747: #html escape fix
    • Optimize nested assoc calls, e.g. produced with ->
    • Avoid object spread when object isn&apost shared (auto-transient)
    • Optimize =, and, and not= even more
    • not= on undefined and false should return true
    • Optimize code produced for assoc, assoc! and get when object argument can be inferred or is type hinted with ^object
    • Optimize str using macro that compiles into template strings + ?? &apos&apos for null/undefined
    • Fix #732: take-last should return nil or empty seq for negative numbers
    • #725: keys and vals should work on js/Map
    • Make map-indexed and keep-indexed lazy
    • Compile time optimization for = when using it on numbers, strings or keyword literals
    • Switch = to a deep-equals implementation that works on primitives, objects, Arrays, Maps and Sets
    • Fix #710: add parse-double
    • Fix #714: assoc-in on nil or undefined
    • Fix #714: dissoc on nil or undefined
    • Basic :import-maps support in squint.edn (just literal replacements, prefixes not supported yet)
  • reagami: A minimal zero-deps Reagent-like for Squint and CLJS

    • First releases
  • clerk: Moldable Live Programming for Clojure

    • Support evaluation of quoted regex
    • Support macros defined in notebooks
    • Bump cherry
  • cljs-str

    • More efficient drop-in replacement for CLJS str. This work was already upstreamed into CLJS, so coming near you in the next CLJS release.
  • unused-deps: Find unused deps in a clojure project

    • Support finding unused git deps
  • scittle: Execute Clojure(Script) directly from browser script tags via SCI

    • Fix SCI regression where interop on keywords like (.catch ...) was accidentally munched
  • Nextjournal Markdown

    • Add :start attribute to ordered lists not starting with 1 (@spicyfalafel)
  • cherry: Experimental ClojureScript to ES6 module compiler

    • Bump squint compiler common component and standard library
    • Bump other deps
    • Optimize =, str, not=
    • Support :macros option + :refer so you can use unqualified macros using compiler state (see macro-state-test)
  • deps.clj: A faithful port of the clojure CLI bash script to Clojure

    • Released several versions catching up with the clojure CLI
  • pod-babashka-go-sqlite3: A babashka pod for interacting with sqlite3

    • Add close-connection
    • Fix #38: add get-connection to cache connection
    • Fix potential memory leak
    • Better handling of parent process death by handling EOF of stdin
    • #25: use musl to compile linux binaries to avoid dependency on glibc
  • quickdoc: Quick and minimal API doc generation for Clojure

    • Fix extra newline in codeblock

Contributions to third party projects:

Other projects

These are (some of the) other projects I&aposm involved with but little to no activity happened in the past month.

Click for more details - [CLI](https://github.com/babashka/cli): Turn Clojure functions into CLIs! - [pod-babashka-fswatcher](https://github.com/babashka/pod-babashka-fswatcher): babashka filewatcher pod - [sci.nrepl](https://github.com/babashka/sci.nrepl): nREPL server for SCI projects that run in the browser - [babashka.nrepl-client](https://github.com/babashka/nrepl-client) - [fs](https://github.com/babashka/fs) - File system utility library for Clojure - [http-server](https://github.com/babashka/http-server): serve static assets - [nbb](https://github.com/babashka/nbb): Scripting in Clojure on Node.js using SCI - [sci.configs](https://github.com/babashka/sci.configs): A collection of ready to be used SCI configs. - [http-client](https://github.com/babashka/http-client): babashka's http-client - [quickblog](https://github.com/borkdude/quickblog): light-weight static blog engine for Clojure and babashka - [process](https://github.com/babashka/process): Clojure library for shelling out / spawning sub-processes - [html](https://github.com/borkdude/html): Html generation library inspired by squint's html tag - [instaparse-bb](https://github.com/babashka/instaparse-bb): Use instaparse from babashka - [sql pods](https://github.com/babashka/babashka-sql-pods): babashka pods for SQL databases - [rewrite-edn](https://github.com/borkdude/rewrite-edn): Utility lib on top of - [rewrite-clj](https://github.com/clj-commons/rewrite-clj): Rewrite Clojure code and edn - [tools-deps-native](https://github.com/babashka/tools-deps-native) and [tools.bbuild](https://github.com/babashka/tools.bbuild): use tools.deps directly from babashka - [bbin](https://github.com/babashka/bbin): Install any Babashka script or project with one command - [qualify-methods](https://github.com/borkdude/qualify-methods) - Initial release of experimental tool to rewrite instance calls to use fully qualified methods (Clojure 1.12 only) - [neil](https://github.com/babashka/neil): A CLI to add common aliases and features to deps.edn-based projects.
- [tools](https://github.com/borkdude/tools): a set of [bbin](https://github.com/babashka/bbin/) installable scripts - [babashka.json](https://github.com/babashka/json): babashka JSON library/adapter - [speculative](https://github.com/borkdude/speculative) - [squint-macros](https://github.com/squint-cljs/squint-macros): a couple of macros that stand-in for [applied-science/js-interop](https://github.com/applied-science/js-interop) and [promesa](https://github.com/funcool/promesa) to make CLJS projects compatible with squint and/or cherry. - [grasp](https://github.com/borkdude/grasp): Grep Clojure code using clojure.spec regexes - [lein-clj-kondo](https://github.com/clj-kondo/lein-clj-kondo): a leiningen plugin for clj-kondo - [http-kit](https://github.com/http-kit/http-kit): Simple, high-performance event-driven HTTP client+server for Clojure. - [babashka.nrepl](https://github.com/babashka/babashka.nrepl): The nREPL server from babashka as a library, so it can be used from other SCI-based CLIs - [jet](https://github.com/borkdude/jet): CLI to transform between JSON, EDN, YAML and Transit using Clojure - [lein2deps](https://github.com/borkdude/lein2deps): leiningen to deps.edn converter - [cljs-showcase](https://github.com/borkdude/cljs-showcase): Showcase CLJS libs using SCI - [babashka.book](https://github.com/babashka/book): Babashka manual - [pod-babashka-buddy](https://github.com/babashka/pod-babashka-buddy): A pod around buddy core (Cryptographic Api for Clojure). - [gh-release-artifact](https://github.com/borkdude/gh-release-artifact): Upload artifacts to Github releases idempotently - [carve](https://github.com/borkdude/carve) - Remove unused Clojure vars - [4ever-clojure](https://github.com/oxalorg/4ever-clojure) - Pure CLJS version of 4clojure, meant to run forever! - [pod-babashka-lanterna](https://github.com/babashka/pod-babashka-lanterna): Interact with clojure-lanterna from babashka - [joyride](https://github.com/BetterThanTomorrow/joyride): VSCode CLJS scripting and REPL (via [SCI](https://github.com/babashka/sci)) - [clj2el](https://borkdude.github.io/clj2el/): transpile Clojure to elisp - [deflet](https://github.com/borkdude/deflet): make let-expressions REPL-friendly! - [deps.add-lib](https://github.com/borkdude/deps.add-lib): Clojure 1.12's add-lib feature for leiningen and/or other environments without a specific version of the clojure CLI

Permalink

Copyright © 2009, Planet Clojure. No rights reserved.
Planet Clojure is maintained by Baishamapayan Ghose.
Clojure and the Clojure logo are Copyright © 2008-2009, Rich Hickey.
Theme by Brajeshwar.