announcements 2026-04-13

Released https://github.com/ring-clojure/ring. This is a patch release that updates the following dependencies: • Jetty 12.1.0 -> 12.1.8 • Apache Commons FileUpload 2.0.0-M4 -> 2.0.0-M5 • Apache Commons IO 2.20.0 -> 2.21.0

👍 2
🙏 9
🙏🏻 1
🎉 14

Thanks again!

Released https://github.com/weavejester/cljfmt. In this release we: • Added validation of user configuration • Fixed :split-keypairs-over-multiple-lines? on first map key • Fixed :align-single-column-lines? for last elements

🙌 3
🎉 21

https://github.com/replikativ/raster 0.1.1 — Fast numerical computing for Clojure I'm happy to announce the first public release of Raster, a library for numerical and scientific computing on the JVM. The core idea: write math with deftm (typed multi), get Julia/JAX-level performance — with full REPL interactivity. What it does deftm gives you typed multiple dispatch (inspired by Julia). The compiler resolves dispatch at definition time, emits JVM bytecode, and fuses array operations across function boundaries:

clojure
(deftm relu [xs :- (Array double)] :- (Array double)
  (par/map [i (alength xs)]
    (Math/max 0.0 (aget xs i))))
par/map, par/reduce, and par/scan are parallel combinators (inspired by Futhark). The compiler treats them as first-class IR nodes, fuses producer-consumer chains (SOAC fusion), and lowers them to SIMD loops or GPU kernels — from the same source. compile-aot inlines an entire call chain — forward pass, reverse-mode AD, optimizer update — into a single JVM method. Zero heap allocations in the hot path. Performance All on Valhalla JDK 27, single-threaded CPU:
| Workload                        | Raster | Reference                             |
|---------------------------------|--------|---------------------------------------|
| ODE solve (DP5 Lorenz)          | 432 us | Julia 583 us — 1.4x faster            |
| LeNet-5 train step (f32)        | 148 us | JAX 356 us — 2.4x faster              |
| LeNet-5 train step (f64)        | 222 us | JAX 370 us — 1.7x faster              |
| MLP train step (f64)            | 136 us | JAX 86 us — JAX 1.6x faster           |
| AD sensitivity (Lotka-Volterra) | 15 us  | Julia ForwardDiff 16 us — 1.1x faster |
The DL numbers include the full compiled pipeline: forward, backward (reverse-mode AD), and SGD update. What's included - Typed multiple dispatch with Julia-style method specialization - Forward-mode and reverse-mode automatic differentiation - Nanopass compiler with SOAC fusion, buffer fusion, SIMD vectorization - ODE/PDE/SDE solvers (Euler, RK4, DP5, Tsit5, Rosenbrock) - Optimization (L-BFGS, Nelder-Mead, Newton) - Linear algebra with LAPACK via Panama FFI (SVD, QR, eigendecomposition, Krylov methods) - Deep learning layers (linear, conv1d/2d, attention, normalization) with compiled training - GPU backends (OpenCL, Level Zero, Vulkan compute) - Symbolic computation, geometric algebra, agent-based simulation - Interactive notebooks (Clay/Kindly) Try it
bash
git clone 
cd raster && clojure -M:nREPL
Then open one of the notebooks: getting started, autodiff, ODE solvers, linear algebra, optimization, or deep learning. Requires JDK 24+ (ClassFile API, Panama FFM). Valhalla JDK 27 recommended for best performance. GitHub: https://github.com/replikativ/raster Docs / Notebooks: https://replikativ.github.io/raster/ Slack: #simmis on Clojurians Feedback, questions, and bug reports very welcome.

👍 1
8
5
🔥 9
🎉 28

I have a question - why does this (and many other type-annotating libs) use sym :- Type rather than using metadata? Clojure could have done type hints like this but didn't, instead inventing metadata for that. The big advantage of metadata is that it makes it easy for macros to flow a single type-annotated symbol around, whereas now you've got three things.

Common Lisp has detached type info like this and it's a real pain to write program generators that convey types as a result

I might be to blame there: Typed Racket uses a reader macro : which is invalid syntax, so it became :- in core.typed. IIRC Plumatic Schema picked it up etc. You're spot on, it led to a proliferation of wrapper macros. I then went back and supported annotations via metadata which look like this: https://github.com/typedclojure/typedclojure/blob/c103738554091c6debbb87ad47225d1b68e34024/example-projects/zero-deps/src/typed_example/zero_deps.cljc#L20

Metadata annotations have a subtlety which can be hard to convey to users: depending on the macro, metadata can either be an evaluation context or a quoted context. So it ends up being a bit brittle encouraging metadata annotations because the user has to figure this out for each macro. I don't have a great example, but this is the kind of issue:

^{:foo (nil)} (fn [])
=> NPE
(defn a {:foo (nil)} [])
=> NPE
I've also found this is platform dependent. IIRC the defn is a quoted context in clojurescript, so if you quote the meta val, it becomes double quoted.

But that's a minor issue compared to abandoning the great properties of flowing data around implicitly.

Raster internally flows the type information (and static information in general) through metadata as well, so this is only a surface issue and could be fixed fairly easily. One concern I have though is that metadata could be much more verbose, in particular if map syntax and quoting are needed, e.g. ^{:raster.type '(Array float)} vs. :- (Array float) . But I would also be in favor of a standardized metadata based type annotation approach because it composes better.

I found I could squeeze it into about half a dozen extra chars:

^{::t/- ann/MyStr} e
vs
e :- ann/MyStr
This has really fantastic compositionally as intended. Drawbacks are noisy syntax and putting burden on users to figure out whether it's an evaluation context (99% of the time it isn't).

IMO it's really the singleton map literal that makes it noisy. I've yet to think of a good syntax proposal, but essentially I was looking for a namespaced ^tag syntax.

It seems like less of a challenge for Raster since you (Ambrose) are looking to adorn arbitrary Clojure code while Raster is working only inside its own macro I think

Yeah since Raster controls the macro it could even hijack the :param-tags reader macro with a malli-like syntax:

^[Array double] foo

My goal with Raster is to stick to TypedClojure as much as possible since it already allows me to devirtualize and do type inference like Julia does. I am happy to follow TC in general (e.g. if it starts to use a Malli syntax), and don't need to reinvent the wheel. I would in general appreciate to have a standardized optional type annotation syntax like Python has now and that library authors start to use more in Clojure, as this makes implementing embedded languages/compilers like Raster much more convenient. TC can express complex parametric type constraints between inputs and outputs, https://github.com/replikativ/ansatz (Lean4) can additionally express dependent type constraints and types over types, but also has a somewhat adhoc syntax atm. Ideally a standardized type annotation syntax would cover this ground as well. @ambrosebs How would you represent the (All [T] ...) parametric type scope with metadata?

How would you represent the (All [T] ...) parametric type scope with metadata?
you'd annotate the entire function at once instead of each arg. https://github.com/typedclojure/typedclojure/blob/c103738554091c6debbb87ad47225d1b68e34024/typed/clj.checker/src/typed/cljc/doc.cljc#L250-L292 all the docs on metadata. Here's a syntax idea that might be workable in practice. Appending a : immediately after any IObj form attaches the next form as :ann metadata (perhaps along with the namespace it was read in).
x: Foo
=> (with-meta 'x {:ann 'Foo})
[]: Bar
=> (with-meta [] {:ann 'Bar})

(defn foo ([a: Int, b: Bool]: Return, code))
=> (defn foo (^{:ann Return} [^{:ann Int} a, ^{:ann Bool} b] code))

Tho something like this would probably work fine and works today.

(defn foo (^{- Return} [^{- Int} a, ^{- Bool} b] code))

Impressive! Congratulations on the release 🙌

Brief update: Both Ansatz and Raster support now metadata type annotations. I still kept the shorthands, might remove them as well if we agree they are harmful duplications, I find them a bit easier to read though.