In Clojure you can destructure a map using an arbitrary expression as the key. For example, here kw is a local binding. ( let [ kw :key { a kw } { :key 1 }] a ) ;=> 1 Usually this syntax is demonstrated as {sym0 :kw0 sym1 :kw1 ...} , which doesn’t reveal that the keywords are actually in expression position , or an evaluation context . The reason why this more recognizable syntax works is because…
Copilot coding agent is a service provided by GitHub that supervises Large Language Models within Actions runners to complete prompted coding tasks. Users can interact with the LLM using standard GitHub features like issue assignments, comments and pull request reviews, but also handy buttons littered across the UI (look for the cloud-surrounded >_ icon). While these familiar user controls make…
Syntax-quote in Clojure is an expressive reader macro for constructing syntax. The library backtick demonstrates how to write syntax-quote as a macro. Both approaches are ripe for optimization—take these programs that both evaluate to [] : '` [] ;=> (clojure.core/apply ; clojure.core/vector ; (clojure.core/seq (clojure.core/concat))) ( macroexpand-1 ' ( backtick/syntax-quote [])) ;=>…
Some functions types in Clojure seamlessly handle infinite arguments, while others misuse them and freeze our programs. Clojure 1.11 .1 user=> ( apply ( fn [ & args ]) ( range )) nil user=> ( apply ( fn []) ( range )) ^ C ;; loops forever Let’s level the playing field. In the first case, the infinite arguments are preserved as a lazy sequence which is never realized by the function body, while the…
“We need to show that off to the Scheme programmers.” Rich Hickey David Nolen was live coding at Clojure/conj and Rich Hickey raises his hand. David—unsure of what to expect—summons Rich’s question. It was more of a suggestion about his open Emacs buffer: “Why don’t you use the set as a function?” David had eta-expanded a set. Dan Friedman and Will Byrd were in the audience and had presented one…
Using test assertions in a multithreaded test often seems to work by magic or silently break. I found that even Clojure’s own test suite has trouble getting it right. Let’s dig into how to do it correctly. clojure.test uses thread-local state to record the progress of a unit test, and is used by test assertions ( is forms) to report test results. Without this state, it can lead to test failures…
A program evolves to be more like itself. Zach Tellman At Papers We Love this week, I was struck by Zach’s perspective on software growth. One of the joys I find in project maintainership is discovering the nature of the program itself. Sometimes the results are surprisingly beautiful. Zach puts into words the gut feelings maintainers often consult when guiding the evolution of their programs.…
Java 19 introduces “Virtual Threads”, which is a new kind of thread on the JVM. One big difference with regular Threads is that it’s unidiomatic to create thread pools: [Using synchronization constructs] is more effective and convenient than thread pools [of Virtual Threads], and is also more secure since there is no risk of thread-local data accidentally leaking from one task to another. JEP 425:…
some-fn and every-pred started life as single-line combinators proposed by Fogus , Christophe Grand and others: ( defn every-pred [ & preds ] ( fn [ & args ] ( every? # ( every? % args ) preds ))) ( defn some-fn [ & preds ] ( fn [ & args ] ( some # ( some % args ) preds ))) Rich Hickey accepted them into Clojure with an important condition: they must be “unrolled”. This increases the performance…
I’d wager any Clojure programmer has come across CLJ-2525 . You know, when your test fixture throws an error and doesn’t mention any of your testing strings? Just “Uncaught exception, not in assertion”, leaving you momentarily dumbfounded about which fixture or doseq branch caused it? That. I’m guessing I’ve triggered this hundreds of times. Who knows how many times Alex Miller (the issue…
Does your macro provide & body arguments? Continue reading. 1. try splicing Does your macro splice body into a try like so? ` ( try ~@ body ( finally ... )) Try passing (catch Exception e) as your body arguments. Your macro can catch exceptions. Try passing (finally) as your body arguments. Your macro cannot invoke variables called finally . For example: ( defmacro foo [ & body ] ` ( try ~@ body (…
I recently added support for instrumenting defprotocol in Plumatic Schema. It has worked out quite nicely. It requires no usage changes in invocation or extensions, supports any kind of protocol extension (interface, extend , and metadata), and runs on Clojure and ClojureScript (however, babashka is not possible yet). So what does this even do? Basically, it allows types to be attached to Clojure…
In 2021, I proposed around a dozen patches to Clojure. I developed many of them concurrently and created a pull-request based workflow that might be useful for others. Here’s my usual process for contributing significant work to GitHub repositories: Create a meta-repo with git worktree helpers. Clone main branch to the main directory in the meta-repo. Create feature branches in directories via git…
Over the last few months I’ve been building typed.clj/spec on top of spec-alpha2 , the new and improved version of Clojure spec that Alex, Rich and others are building. Of course, I haven’t been using spec-alpha2 “as intended”, so I don’t have opinions here about open vs closed maps, or the new select operations that are front-and-center in spec2’s marketing. In brief, typed.clj/spec is a spec…
Recently, Jim Newton reached out to me to clarify a post he submitted to ClojureVerse. Jim is doing interesting work on defining heterogeneous types in Common Lisp, with a set-theoretic flavor. In ongoing followup work, he is generalizing his approach to to other languages and type systems, and he wants to learn more about Clojure and how it relates to Common Lisp, and how Typed Clojure and…
Matthew Butterick’s Practical Typography is one of my favorite books. It has been my reference point for anything web-related—including (clearly) this blog—so discovering the second edition was a treat. The book supports a single-column layout, with consistent body text : Try resizing and scrolling How? Mostly CSS. Here’s how I upgraded this blog. Font sizes in viewport width units are…
Which set of features would deserve a 1.0 release for Typed Clojure? We’ve learnt valuable lessons from real-world developers about the pain points of using Typed Clojure, and after several years of mulling it over, we have a much better idea of how we might improve it. This series of posts will outline our proposed solutions, and give an impression of what we hope for Typed Clojure 1.0. -->…
This is a transcript of a PL Wonks lightning talk. Ok. So this talk is going to be about providing a toolkit to macro writers in Clojure, to be able to communicate to the type system. Here’s the setting. On the left, we have the type system. It finds macros it needs to expand, but it has no idea how these macros work — all the type system has is the ability to expand macros, and then make a…
Our work in automatic annotations for clojure.spec uses aliases to create readable, compact, and useful specs. Here’s the kind of thing we’re striving for: informative and pretty alias names (like Ints and Syms ), while reusing aliases more than once (like Ints in this example). ( s/def Ints ( s/coll-of integer? )) ( s/def Syms ( s/coll-of symbol? )) ( s/fdef f :args ( s/cat :x Ints :y Syms ) :ret…
This post describes how to generate specs for any Leiningen project. After 8 months of internships, teaching, and quals, I needed a refresher. You might also find it helpful. First, add a dependency to lein-typed in your ~/.lein/profiles . Here’s what mine looks like. ~/.lein/profiles.clj { :user { :plugins [[ lein-typed "0.4.2" ]]}} Let’s generate specs for clj-time . Shell $ git clone…
David Foster published a thoughtful response to my previous post on unsoundness, vouching for unsound type systems as useful and worthy of research. I really like this sentiment–unsound systems can provide usability that is ahead of its time, while motivating entire fields of research! Here’s my perspective. A “(static) type system” is a discipline that helps design, verify, and document programs.…
As part of my work on gradual typing for Typed Clojure, I used a different approach than Typed Racket in importing and exporting top-level defines. It was initially by necessity, as Clojure namespaces are much simpler than Racket modules, but we discovered it was now possible to export macros defined in typed modules (unlike Typed Racket). This work (and the ambition to add gradual typing to Typed…
This is an essay I wrote early-2016. After battling with soundness vs. usability in Typed Clojure for many years, I was starting to reconsider the “obviously wrong” approach of baking unsoundness into the type system from the get-go. I look at some historical examples of intentional unsoundness in type systems, and try to present both sides of the argument. Type systems come in all shapes and…
This is a transcript of a practice talk I gave for my Ph.D. qualifying exam. I’ve left in a lot of the mistakes to give it some character, and to learn a bit about how I talk. It’s also available in different formats here . Ok, today I’m going to talk about automatic type annotations, and this is part of my PhD qualifying exam. But first, I’d like to start with a story about annotating, from the…
Previously, we covered why automatic annotations are useful , and some basics underlying the infrastructure to perform automatic annotations . In this post we’ll see how function types are inferred, as well as simple map types. This work is part of a crowdfunding effort, please support the campaign here ! Automatic annotations for Typed Clojure only work if you provide tests. Let’s suppose we have…
We have covered some of the reasons why automatic annotations are useful in my previous post , now let’s build up the infrastructure necessary to perform it from scratch. This work is part of a crowdfunding effort, please support the campaign here ! At the center of our approach is the track function. It wraps values and remembers a ‘path’ to report the source of the value. How is track defined?…
Annotations in Typed Clojure are essential, but tedious to write. We need a tool to generate type annotations automatically, based on the specifications we already give via tests. In this post, I set the stage for why this tool is necessary. We discuss how it provides valuable generated documentation at any point in the development cycle, assists in the effort to generate contracts for untyped…
Today’s programming landscape is dynamic and polyglot . Interlanguage interoperability is a mainstay — it’s almost unavoidable that a given piece of code will be used without the knowledge of the original author. So it’s surprising to learn most static type systems completely ignore the pragmatics of running statically typed code in the real world. Who can use this code? Can they break the type…
An interesting aspect of type checking a Clojure dialect is dealing with host interop. In Typed Clojure, Java interop is handled by utilising Java type information. For the collected Java types to be sound in Typed Clojure, we then transform them to deal with corner cases in the Java type system like covariant arrays and null . In checking Clojurescript, we need to type check interactions with…
Typed Clojurescript is in early development, but it’s still fun to play around with. It’s designed to be very similar to Typed Clojure. The usual vars like ann and ann-form are identical to the Clojure implementation, except the prefix of clojure.core.typed is replaced by cljs.core.typed . One of the major goals of the Typed Clojure crowdfunding campaign is to work on Typed Clojurescript. It will…
I am crowdfunding towards 12 months of full-time Typed Clojure development. If you value strong guarantees like the static prevention of Null Pointer Exceptions, pledge or share today ! Typed Clojure is opinionated: some type errors are checked at compile time, and other checks are delayed until runtime. One kind of type error Typed Clojure is designed to eradicate at compile time is the Null…
I am crowdfunding 12 months of full time development on Typed Clojure. Please pledge or share! One of the first problems I attempted to solve with Typed Clojure turned out to be one of the most difficult: statically verifying Red/Black tree rebalancing invariants. This post describes how close I am to checking these invariants. Note that the code examples might not be currently checkable. I was…
The first stretch goal for the Typed Clojure campaign concentrates on a significant missing piece in JVM tooling: a self-hosted Clojure compiler, (aka. a Clojure compiler written in Clojure). This post is intended to briefly demonstrate where core.typed can benefit from CinC (read Clojure-in-Clojure), an actively developed Clojure compiler. Performance Probably the most frequently asked question…
Assertions are a popular technique for runtime verification of Clojure code. These assertions are often a rich source of type information that core.typed takes advantage of. Simple flow reasoning Using simple reasoning about control flow, core.typed can understand typical Clojure assertions and preconditions. Consider the following common macroexpansion for an assertion: ( fn [ a ] ( if ( number?…
core.typed is intended to be used at the REPL for rapid feedback, however there are a few things to look out for. Checking mode core.typed only has effect when in checking mode. cf and check-ns are two ways of starting checking mode. This is most apparent when type checking a definition at the REPL. clojure.core.typed=> ( ann my-inc [ Number -> Number ]) nil clojure.core.typed=> ( defn my-inc [ a…
When it comes to annotating functions in core.typed , there are some special rules to keep in mind. Here are two. all function parameters should be annotated polymorphic functions should be instantiated when used as a parameters to polymorphic higher-order functions. Annotating function parameters There are several ways for function argument annotations to propagate downwards. To annotate a full…
Clojure’s emphasis on immutable bindings and data structures lead us to write simpler, more obvious code. We don’t need to worry about immutable things changing over time. This reduces the cognitive load of both writing and reading code. It is common to assume invariants for mutable structures, however, even in Clojure. Sometimes refactoring such logic in terms of immutable things can be clearer,…