Welcome! Please see the About page for a little more info on how this works.

0 votes
in Spec by

Right now, core.specs.alpha only has specs for some basic special forms and macros.

Can it be expanded to include all functions defined in clojure.core? Or, perhaps there can be another optional namespace that contains these specs? I imagine having these specs available for instrumentation could be really helpful for training new Clojure programmers and bug-catching.

1 Answer

+2 votes

We do not plan to add specs for the functions in core. Specs are most useful for describing syntax (largely macros, which we will likely continue to add), or informational types (maps with keys etc). The temptation with generic and higher order functions is to try to write very detailed specs, which are likely to be broken in the face of additions to the aggressively generic or polymorphic functions in core. Additionally, there is a real performance impact when using a lot of spec'ed functions, and due to frequent use of a relatively small number of functions, a property that Clojure programs tend to have, this performance impact is magnified.

There are various things being mulled for spec 2 function integration that would make it possible to say more interesting things about higher order functions and perhaps that will change the balance of interest here at some future time.

All that said, there is an unofficial community repo of core specs at https://github.com/borkdude/speculative and they are largely reasonable (but still fall into the issues above). You can experience the performance issues yourself if you instrument with all of those on.

So, I think mentioning instrumentation may have been a mistake on my part. The better use case I was imagining was more advanced static code analysis. I come from a TypeScript background, where people would avoid creating highly detailed types like you say, and instead partially describe their codebases with types, but when this is put through the TypeScript language server built into VSCode, it produces an immense amount of value! Clojure has recently massively improved its static analysis tools like clj-kondo. If these tools had access to even a partial spec of clojure.core, they could provide a massive amount of information to developers at 0 performance impact. You could even make use of an 'any' spec that simply refers to anything, you could preserve that level of generic and polymorphic condition in the spec
Doesn’t kondo already have that?
edited by
Not really, not completely. kondo's system for type checking is a bit patched together. It has to put in a custom function every time it has to do a conditional type check, for instance.

Kondo has a custom function for core.into that says to make the return type the same as the type of the collection passed in as its first argument. Already there is a ticket on their JIRA board to add that to another function, and it doesn't follow suit on a variety of other functions either, like the function inc. If you pass an int to inc, Kondo doesn't know if the output is still an int.

I. E.
This code: (nth [1 2 3] 1.5) throws an error in kondo (which is incorrect btw, nth is defined for non-integers)
But this code (nth [1 2 3] (inc 0.5)) comes through with no error.

If this continues, kondo will have to end up with a huge amount of logic, that doesn't necessarily belong in their codebase.

If a user wanted to implement or extend logic like that, it would be difficult to do so. There is no easy and well understood means of defining that. I was hoping spec would grow to become a standard way for developers to encode necessary type logic within their own codebases in a TypeScript-y way. For instance, if it was important in your codebase to define a collection as a collection of ints, or a collection of some custom type for your database, that would get communicated to kondo, and it would be able to perform logic on that, and see several transformations down the line if something will fail. Spec and Malli already have some systems to do that, but kondo doesn't have the logic yet to complete it.

I am unsure if the appropriate logic for making this kind of analysis belongs in kondo itself, or if it belongs somewhere in the clojure codebase, since it is solely dependent on clojure's api. Nor do I know if spec or spec2 are the right places to hold it. That's why I posted this question here.