announcements

2026-04-01T03:05:28.663719Z

https://github.com/NoahTheDuke/lazytest/releases/tag/v2.0.0: standalone BDD test framework v2.0.0 First update since September! This one has a small breaking change and a rather large feature, which is why I've bumped it to v2. The big feature: hooks! (or plugins.) These are entrypoints into the running state of lazytest, called on a given object which allow Hook authors to inspect and/or update the object before it's used/returned. Think of them as a "poor man's Kaocha Plugins". • Add lazytest.hooks to support writing plugins/hooks that modify the run. • Write initial demonstration hooks, profiling and randomize. • Add around-each context functions which propagate to test cases from parents and wrap before-each and after-each context functions. (Thanks @edenworky!) • BREAKING: Change the order of context function calls so that around wraps before and after. There are very few use cases where this will matter, so I am not too worried about it, but it's worth noting. • Fix expect-it to respect context functions.

❤️ 3
🎉 14
mpenet 2026-04-01T07:15:17.518799Z

Long overdue https://github.com/mpenet/tapeChronicle Queue library) release/update • Bumping dependencies and added support for restartable tailers (by id)

🎉 9
dan.lentz 2026-04-01T19:22:22.194119Z

clj-format 0.1.0 A Clojure DSL for cl-format strings inspired by Hiccup. No dependencies. Drop-in compatibility. The power of FORMAT made easy. Daunting format strings become intuitive, and fully expressive:

(cl-format nil "~@(~R~) error~:P detected." 23)

  Becomes:

  (clj-format nil
    [[:cardinal {:case :titlecase}]
     " error" [:plural {:rewind true}] " detected."]
    23)

  ;; => "Twenty-three errors detected."
Or
(cl-format nil "~@R ~(~@R~)" 14 14)

  becomes:

  (clj-format nil
    [:roman " " [:roman {:case :downcase}]]
    14 14)

  ;; => "XIV xiv"
See https://github.com/danlentz/clj-format/blob/master/doc/examples.md from Practical Common Lisp, CLtL2, and the CL HyperSpec. Key features: - full string passthrough for existing clojure.pprint/cl-format usage - Hiccup-style [:keyword {opts} & body] convention - Semantic option names: {:group true :sign :always} instead of :colon/:at flags - All 33 cl-format directives - parse-format parsing format strings into the DSL - compile-format compiling the DSL back to format strings - round-trip fidelity https://github.com/danlentz/clj-format This is the initial release, feedback welcome. Hope folks will check it out -- I've been finding it quite handy.

👍 4
👍🏻 1
6
👀 3
🎉 6
2026-04-10T01:41:51.091419Z

hey, i expanded the languge. now it has a robust tabular and figlet directive that compiles down for format stings

2026-04-10T01:43:57.601299Z

also supports nested tables and figlet in tables https://github.com/danlentz/clj-figlet

enn 2026-04-01T20:17:20.358039Z

I love this, thanks for sharing! I'm a fan (?) of CL's format from way back but the syntax has always been pretty hard to defend.

👍 1
dan.lentz 2026-04-01T20:21:40.602469Z

yeah format is pretty amazing but the format strings have always been a huge obstacle for me. hiccup is like second nature

2026-04-01T22:19:03.063529Z

Not to be confused with https://github.com/weavejester/cljfmt

dan.lentz 2026-04-01T22:59:40.454769Z

i'm sorry i forgot to add examples of justification and logical blocks. Those are some of the best. doh.

enn 2026-04-01T23:53:22.521249Z

I can't resist sharing my own collection of toy cl-format examples, which I put together a few years ago to share with colleagues at a former job: https://github.com/enaeher/cl-format-lunch-and-learn/blob/main/cl_format.clj. (No output in the file because I presented this interactively in a REPL.)

dan.lentz 2026-04-02T01:52:50.472909Z

oh cool --i should add a word wrap example

dan.lentz 2026-04-02T01:54:16.485519Z

I'm going to leave it as an exercise for the reader to implement DOOM with format string.

😂 1
dan.lentz 2026-04-02T13:52:54.346449Z

i'll include these in a new 0.1.1 release that adds #clojurescript support and #babashka testing

dan.lentz 2026-04-03T00:25:11.325789Z

@enn

"~A~16T~A~28T~A~46T~A~%~14,,,'-A~16T~10,,,'-A~28T~16,,,'-A~46T~5,,,'-A~%~:{~A~16T~6,2F~28T~V~~46T~:*~D~%~}"

(clj-format nil
  ["Name" [:tab {:col 16}] "Value" [:tab {:col 28}] "Histogram" [:tab {:col 46}] "Count" :nl
   [:str {:width 14 :fill \-}] [:tab {:col 16}]
   [:str {:width 10 :fill \-}] [:tab {:col 28}]
   [:str {:width 16 :fill \-}] [:tab {:col 46}]
   [:str {:width 5 :fill \-}] :nl
   [:each {:from :sublists}
    :str [:tab {:col 16}]
    [:float {:width 6 :decimals 2}] [:tab {:col 28}]
    [:tilde {:count :V}] [:tab {:col 46}]
    :back :int :nl]]
  "" "" "" ""
  [["Alpha" 3.14 5]
   ["Beta" 12.0 2]
   ["Gamma" 98.5 9]
   ["Delta" 42.42 7]])

;; Name            Value       Histogram         Count
;; --------------  ----------  ----------------  -----
;; Alpha             3.14      ~~~~~             5
;; Beta             12.00      ~~                2
;; Gamma            98.50      ~~~~~~~~~         9
;; Delta            42.42      ~~~~~~~           7

👍 1
dan.lentz 2026-04-03T00:26:14.953369Z

i expanded your tabular data example

whilo 2026-04-01T20:29:11.197569Z

https://github.com/replikativ/superficie — surface syntax for Clojure clojure We just shipped a major update to Superficie, the bidirectional renderer that translates Clojure S-expressions into Python/Julia/TypeScript-readable syntax and back. What is it? You write normal Clojure. When you need to show it to someone — in a talk, a paper, a blog post, a code review with a domain expert — you render it to syntax they can already read:

;; Clojure
(defn process-users [users]
  (->> users (filter :active) (map :name) (sort) (take 10)))

;; Superficie
defn process-users [users]:
  users |> filter(:active)
        |> map(:name)
        |> sort()
        |> take(10)
end
It also runs: .sup files evaluate directly on the JVM, via Babashka, or in a browser SCI REPL. What's new in this release 🛠️ Fully rewritten parser Four-stage pipeline inspired by Racket's shrubbery notation: • Tokenizer — character scanner with precise source locations • Grouper — bracket resolver that never throws; mismatched delimiters become error nodes embedded in an otherwise valid tree • Enforest — heals the shrubbery back to a well-formed token stream before semantic parsing • Reader — LL(1) recursive-descent with Pratt infix climbing Errors look like this now:
Error: Unterminated string — missing closing " (line 2, col 7)
2 |   str("hello world)
  |       ^
⚙️ Extensible operator registry Infix operators are first-class and extensible. The registry ships with arithmetic, logic, comparison, threading (`|>`, .>), and Haskell-inspired operators (`<$>`, <*>, >>=, **, >>, <<). Register your own with defsupoperator:
(defsupoperator <+> :prec 40 [l r] `(xor ~l ~r))

;; now usable as infix in any superficie source:
a <+> b <+> c
🏗️ Adaptive block syntax Block keywords (`defn`, if, let, match, …) are registered in a live dispatch table. When you evaluate a defmacro with :superficie/role metadata, it is immediately available as block syntax for subsequent forms in the same file or REPL session — no configuration required:
clojure
(defmacro ^{:superficie/role :defn-form} defhandler [name args & body]
  `(defn ~name ~args ~@body))
;; superficie now parses this as a block automatically:
defhandler on-click [event]:
  handle(event)
end
This works namespace-aware: the block registry resolves through the current *ns* so the right macro gets the right parse rule regardless of aliasing. 🔌 REPL integration The streaming parser follows Clojure's incremental model — each top-level form is fully parsed and evaluated before the next is read. Macros defined in form N are live for form N+1. The superficie.repl namespace provides a full interactive REPL backed by this model; superficie.sci_repl provides a browser SCI REPL with the same semantics. 📦 npm / browser bundles Three targets ship on npm (`superficie` package): • dist/superficie.js — Node.js / bundler • dist/browser/superficie.js — browser global (`superficie.renderString`, superficie.parseString) • dist/browser-repl/superficie-repl.js — browser SCI REPL (`superficieRepl.evalSup`, superficieRepl.reset) The playground at https://replikativ.github.io/superficie/examples/playground.html. Roundtrip coverage 596 / 699 files (85%) across 14 real-world Clojure projects.

👀 3
🙌 1
🎉 11
Andriy.Tyurnikov 2026-04-01T20:40:14.157659Z

Remember beme-clj, the M-expression reader I posted about? It's dead. 💀 Long live meme-clj 👑 What changed: • dropped begin/end, • dropped S-expression escape hatches, • dropped read-string delegation. One rule: f(x y) → (f x y) 🥇 Everywhere. Even inside your macros.

defmacro(unless [test & body]
  `if(not(~test) do(~@body)))
The meme dialect just hit 1.0.0. three platforms (Babashka, Clojure, ClojureScript), macros work. Looking for people to try it on real code and tell me what I broke. Also: meme is quietly becoming a language toolkit — composable pipelines for building syntax frontends for Clojure. That part is pre-alpha and not an April Fools' joke, I promise. 🤡 https://github.com/xpojure-lang/meme-clj 🎉

👀 2
🤣 2
⭐ 1
Casey 2026-04-03T13:35:12.779539Z

ah m-expres, very cool. fyi this is superficially related to https://github.com/replikativ/superficie which I also ran into the other day. another take on alt-lisp syntax

👀 1
john 2026-04-01T20:43:57.092319Z

Can't believe you did it

Andriy.Tyurnikov 2026-04-01T20:46:22.990949Z

Thanks Claude

john 2026-04-01T20:48:11.186989Z

Nice

Andriy.Tyurnikov 2026-04-01T20:52:32.483309Z

You have no excuse not to try now 🤣

john 2026-04-01T20:54:09.088189Z

Well, I think sexprs are strictly better... I'd be interesting in seeing where you can take this wrt eliminating more parens, while keeping the syntax macroable.

🤩 1
john 2026-04-01T20:55:49.125259Z

It's funny, IIRC, this isn't the first time infix/"normal syntax" has been fools joked... But to have one today, and have two evolutions of legit impls, that might even benefit from the impl of the april fools impl.... chef_kiss

🙌 1
john 2026-04-01T20:56:25.273829Z

I think all this is great syntax sugar to get people into lisp/clojure, so I'm not mad at it

😅 1
john 2026-04-01T20:56:39.772699Z

Once they get into it, I think they'll more easily grok the parens

😆 1
Andriy.Tyurnikov 2026-04-01T20:59:11.219599Z

Or... people would able to use clojure without making themselves into parsers https://media1.tenor.com/m/ce1VXmViNMgAAAAd/dune-mentat.gif

john 2026-04-01T21:03:11.006249Z

Become the parser, Andriy. Be one with the form

😵‍💫 1
john 2026-04-01T21:03:56.273509Z

You're a fool, Andriy! Turn back! Do not look behind the curtain!

😅 1
john 2026-04-01T21:04:28.921069Z

Nah, man, I doubt you can pull it off but it's possible

john 2026-04-01T21:06:29.471849Z

maybe white space indentation? Have you thought about that? Has somebody done a python indentation treatment on sexprs?

john 2026-04-01T21:07:38.677089Z

the unwashed, unparened masses

🤷‍♂️ 1
Andriy.Tyurnikov 2026-04-01T21:11:44.076559Z

"indentation treatment" I refuse to make humans count whitespaces

Andriy.Tyurnikov 2026-04-01T21:12:31.453319Z

but people done it, schemers even accepted as a WISP or something

john 2026-04-01T21:12:32.546779Z

Well, you could make it a single space. The \newline is the point

🤔 1
john 2026-04-01T21:12:41.611829Z

ah hmm

Andriy.Tyurnikov 2026-04-01T21:13:59.939319Z

I tried... notation gets "vertical" very fast. Would sell in the East, probably 🇯🇵

john 2026-04-01T21:14:35.361109Z

You need some way to discern application precedence for if(not(~test) do(~@body)) on the same line, right? Yeah, thought so, hmm.

john 2026-04-01T21:16:45.322769Z

You could make everything inline until it would violate your intended nesting and then use the next line as the escape hatch

john 2026-04-01T21:16:59.742629Z

And don't go vertical by default

john 2026-04-01T21:17:31.305479Z

But a fully inline solution would be better

john 2026-04-01T21:17:50.030919Z

maybe take back the comma?

✅ 1
john 2026-04-01T21:18:19.430849Z

it's a usable but ignored token in clojure

Andriy.Tyurnikov 2026-04-01T21:19:31.175439Z

that is an interresting point, comma is not dead syntax, but may be claimed safely indeed!

john 2026-04-01T21:19:35.697979Z

if not ~test, do ~@body

john 2026-04-01T21:20:33.108839Z

and it looks good in psuedo code

john 2026-04-01T21:20:45.913829Z

maybe

Andriy.Tyurnikov 2026-04-01T21:21:09.284259Z

semicolon is arguably claimable as well 😉

john 2026-04-01T21:21:34.791189Z

why go backward? Why not go forward? 🙂

Andriy.Tyurnikov 2026-04-01T21:21:51.005779Z

// ?

john 2026-04-01T21:22:46.933769Z

It's a fun problem

john 2026-04-01T21:24:16.792859Z

I guess you can claim anything you want at your level... But because they're commas you could be pretty slopply about letting them fall back into clj code and it'll parse.

john 2026-04-01T21:25:58.427819Z

Are you going for two-way transformability like superficie?

Andriy.Tyurnikov 2026-04-01T21:28:20.610669Z

Simply with M-exprs two way is hardly cahllenging, and a good quality test for implementation 😉

Andriy.Tyurnikov 2026-04-01T21:33:30.189889Z

You start as lossless two-way, then you compromise and get two-way with normalisation, then you get one-way 🤷‍♂️

john 2026-04-01T21:44:06.945869Z

I might have to spike out one of these 🤔 Now you got me thinkin...

Andriy.Tyurnikov 2026-04-01T21:47:09.524739Z

@john what would you say of Wolfram Language ?

john 2026-04-01T21:49:43.711059Z

I'm not too versed in it. I've explored it. My understanding is that it's basically a lisp and he's got a macro system in there.

john 2026-04-01T21:50:06.312099Z

IIRC I've seen him say it's his take on mxpers

Andriy.Tyurnikov 2026-04-01T21:51:17.594339Z

It is M-exprs with Infix, and Prefix, and Postfix on the top. He actually nailed it

john 2026-04-01T21:51:19.482639Z

Elixer is a ml take on macros too. The code is data. Pretty cool mix on it.

john 2026-04-01T21:53:10.419659Z

He nailed it, and yet I'd prefer to use it from wolframite... It might be subjective between the two, from a syntax perspective

john 2026-04-01T21:54:25.189199Z

I was never a huge fan of arithmetic syntax anyway... It honestly confuses me way more than prefix.

john 2026-04-01T21:56:01.028939Z

It's just a holdover from accidental notations humans used to communicate together. Accidental, arbitrary ording rules. I prefer explicit.

🙌 1
2026-04-01T22:27:06.037739Z

There is (almost) no bug or misreading in Perl or Java that you don't solve by inserting parentheses to clarify the order in which you want infix things done. Parentheses are, in general, unavoidable. The question is whether you plug them in upfront or wait to be reprimanded.

2026-04-01T22:29:18.148419Z

COBOL really got it right with ADD 9 TO X GIVING Z. There was also (wasn't there?) a more general COMPUTE but it was the beginning of the end for COBOL

Andriy.Tyurnikov 2026-04-01T22:30:18.448569Z

I tink we are conflating infix notation and (Precedence & grouping)

2026-04-01T22:30:19.427379Z

The "cell formula" with multiple operations is also a key reason why Excel has failed in the marketplace

2026-04-01T22:30:51.353039Z

I cheerfully conflate those two things

😂 1
Andriy.Tyurnikov 2026-04-01T22:33:02.428499Z

Isn't Elixir's pipeline |> just an infix operator ?

john 2026-04-02T02:19:24.985609Z

I'm not too familiar, it's been a while for me with Elixir

2026-04-02T16:58:34.738549Z

I thought operators were inherently infix. Clojure doesn't have any operator for example. In Clojure + is a function. But if you make it infix it becomes an operator.

john 2026-04-02T17:49:09.196229Z

I just conflate all functions as operators in the prefix position

john 2026-04-02T17:50:29.298529Z

Does operater mean params must be on both sides?

2026-04-02T21:32:46.492719Z

++

Andriy.Tyurnikov 2026-04-02T21:34:52.815089Z

operator ~ form ?

Andriy.Tyurnikov 2026-04-02T21:37:57.129539Z

looks like lisps don't have operators 🤷‍♂️

Andriy.Tyurnikov 2026-04-02T21:39:10.001859Z

condition ? true-branch : false-branch ; // ternary operator

2026-04-02T21:41:41.495979Z

That's the key. Functions alone are strictly simpler than functions-and-operators. And higher-order functions are simple while higher-order operators would be a puzzle!

✅ 1
2026-04-03T00:28:10.205229Z

I don't think it's necessarily binary, but it kind of only makes sense in infix, otherwise an operator is just a function.

2026-04-03T00:31:03.506139Z

Like say you have the merge function. Even in infix you'd do: merge(a, b) But if you turn it into an operator you could do: a merge b

genmeblog 2026-04-01T23:09:36.769889Z

https://github.com/scicloj/tablecloth - a dataset manipulation libarary on the top of tech.ml.dataset is bumped to a 8.0.16 version. There are no big changes (a couple of small fixes), just a major version bump.

👀 1
🎉 10