announcements 2026-04-01

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

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

🎉 9

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

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

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

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

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

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

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.)

oh cool --i should add a word wrap example

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

😂 1

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

@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

i expanded your tabular data example

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

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

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

Can't believe you did it

Thanks Claude

You have no excuse not to try now 🤣

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

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

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

😅 1

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

😆 1

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

Become the parser, Andriy. Be one with the form

😵‍💫 1

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

😅 1

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

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

the unwashed, unparened masses

🤷‍♂️ 1

"indentation treatment" I refuse to make humans count whitespaces

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

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

🤔 1

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

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

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

And don't go vertical by default

But a fully inline solution would be better

maybe take back the comma?

✅ 1

it's a usable but ignored token in clojure

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

if not ~test, do ~@body

and it looks good in psuedo code

semicolon is arguably claimable as well 😉

why go backward? Why not go forward? 🙂

// ?

It's a fun problem

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.

Are you going for two-way transformability like superficie?

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

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

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

@john what would you say of Wolfram Language ?

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.

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

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

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

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

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

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

🙌 1

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.

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

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

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

I cheerfully conflate those two things

😂 1

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

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

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.

I just conflate all functions as operators in the prefix position

Does operater mean params must be on both sides?

operator ~ form ?

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

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

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

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

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

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