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.
Long overdue https://github.com/mpenet/tapeChronicle Queue library) release/update • Bumping dependencies and added support for restartable tailers (by id)
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.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.
yeah format is pretty amazing but the format strings have always been a huge obstacle for me. hiccup is like second nature
Not to be confused with https://github.com/weavejester/cljfmt
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.
i'll include these in a new 0.1.1 release that adds #clojurescript support and #babashka testing
"~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 ~~~~~~~ 7i expanded your tabular data example
https://clojurians.slack.com/archives/C015AL9QYH1/p1776273044314079
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.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
🎉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
Can't believe you did it
Thanks Claude
Nice
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.
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
I think all this is great syntax sugar to get people into lisp/clojure, so I'm not mad at it
Once they get into it, I think they'll more easily grok the parens
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
You're a fool, Andriy! Turn back! Do not look behind the curtain!
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?
Hordes of Paren-free-men are coming https://www.looper.com/img/gallery/dune-the-meaning-of-lisan-al-gaib-explained/what-lisan-al-gaib-means-in-the-dune-universe-1741978122.webp
the unwashed, unparened masses
"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
ah hmm
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?
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
maybe
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.
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
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 operatorThat'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!
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.