https://github.com/arohner/spectrum
https://github.com/arohner/spectrum
Last synced: 27 days ago
JSON representation
- Host: GitHub
- URL: https://github.com/arohner/spectrum
- Owner: arohner
- License: other
- Created: 2016-07-04T19:43:52.000Z (over 9 years ago)
- Default Branch: master
- Last Pushed: 2021-04-17T12:41:59.000Z (over 4 years ago)
- Last Synced: 2025-10-22T01:57:10.784Z (27 days ago)
- Language: Clojure
- Size: 1.32 MB
- Stars: 602
- Watchers: 38
- Forks: 20
- Open Issues: 14
-
Metadata Files:
- Readme: README.md
- Changelog: CHANGELOG.md
- License: LICENSE
Awesome Lists containing this project
- awesome-clojure - spectrum
README
[](https://circleci.com/gh/arohner/spectrum)
[](https://clojars.org/spectrum)
# spectrum
A library for doing static analysis of Clojure code, catching clojure.spec conform errors at compile time.
**Wait what?**
It's like core.typed, but it relies on clojure.spec annotations.
**So it's an optional static type system?**
Kind-of. It finds errors at compile time, and predicates kind of look like types. So sure.

## Current Status
Developer Preview, not yet ready for any kind of use.
Current development is working towards making spectrum self-check.
## Goals
- usable
- pragmatic
- readable implementation
- fast
- configurable strictness levels
- incremental checking
## Non-goals
- perfection
- correctness
- requiring 100% spec coverage.
In particular, spectrum aims to be fast and usable, and catching
bugs. It aims to have low false positives, at the expense of
potentially not catching 100% of type errors.
A tool that catches 80% of bugs that you use every day is better
than a 100% tool that you don't use. Spectrum will converge on 100% correct,
but won't guarantee correctness for a while.
## Usage
Still under active development, so this is incomplete.
Use clojure.spec as normal. Then, at the repl:
```clojure
(require '[spectrum.flow :as f])
(require '[spectrum.types :as t])
(f/infer-var #'foo)
```
There is also
```clojure
(f/infer-form '(foo 3))
```
which is useful when you want to debug the signature of a form.
`infer-form` can optionally take a map of keywordized variables to types
```clojure
(f/infer-form '(string? x) {:x #'string?})
#Value[true]
(f/infer-form '(string? x) {:x (t/value-t 3)})
#Value[false]
```
## Limitations
- It's still very early.
## Justification
- because I could
- working example of types = proofs = predicates
- Some properties of a program are easier to prove at runtime. Some at compile-time
- this is extra checking on top of spec, not a replacement for it
- to get HN to shut up about how adding a static type system to a dynamic language is impossible
- why not just use spec by itself?
- `instrument` doesn't check return values
- `check` works best on pure functions with good generators.
- Not all clojure code is pure
- Not always easy to write a good generator for all functions (hello, any fn that takes a DB connection)
- Not always easy to write a generator w/ 100% coverage
- you can't spec non-defn functions
- you can't spec higher order functions
- generative testing with side-effects doesn't work well
- generative testing gives false negatives on rare codepaths
- static typing guarantees all codepaths are checked
- spec doesn't deal with non-fn vars at all (binding, alter-var-root, set!, etc)
- generative testing can be slow
## Plan
Spectrum is still very early, and not ready for production use. Current development is focused on making spectrum self-check.
## License
Copyright © 2019 Allen Rohner
Creative Commons Attribution-NonCommercial-NoDerivs 3.0 Unported