clojure.core.typed
This namespace contains typed wrapper macros, type aliases
and functions for type checking Clojure code. check-ns is the interface
for checking namespaces, cf for checking individual forms.
ann
macro
(ann varsym typesyn)
Annotate varsym with type. If unqualified, qualify in the current namespace.
If varsym has metadata {:no-check true}, ignore definitions of varsym
while type checking. Supports namespace aliases and fully qualified namespaces
to annotate vars in other namespaces.
eg. ; annotate the var foo in this namespace
(ann foo [Number -> Number])
; annotate a var in another namespace
(ann another.ns/bar [-> nil])
; don't check this var
(ann ^:no-check foobar [Integer -> String])
ann-datatype
macro
(ann-datatype & args)
Annotate datatype Class name dname with expected fields.
If unqualified, qualify in the current namespace.
Takes an optional type variable binder before the name.
Fields must be specified in the same order as presented
in deftype, with exactly the same field names.
Also annotates datatype factories and constructors.
Binder is a vector of specs. Each spec is a vector
with the variable name as the first entry, followed by
keyword arguments:
- :variance (mandatory)
The declared variance of the type variable. Possible
values are :covariant, :contravariant and :invariant.
- :< (optional)
The upper type bound of the type variable. Defaults to
Any, or the most general type of the same rank as the
lower bound.
- :> (optional)
The lower type bound of the type variable. Defaults to
Nothing, or the least general type of the same rank as the
upper bound.
eg. ; a datatype in the current namespace
(ann-datatype MyDatatype [a :- Number,
b :- Long])
; a datatype in another namespace
(ann-datatype another.ns.TheirDatatype
[str :- String,
vec :- (Vec Number)])
; a datatype, polymorphic in a
(ann-datatype [[a :variance :covariant]]
MyPolyDatatype
[str :- String,
vec :- (Vec Number)
ply :- (Set a)])
ann-interface
macro
(ann-interface & args)
Annotate a possibly polymorphic interface (created with definterface) with method types.
Note: Unlike ann-protocol, omit the target ('this') argument in the method signatures.
eg. (ann-interface IFoo
bar
(Fn [-> Any]
[Number Symbol -> Any])
baz
[Number -> Number])
(definterface IFoo
(bar [] [n s])
(baz [n]))
; polymorphic protocol
; x is scoped in the methods
(ann-protocol [[x :variance :covariant]]
IFooPoly
bar
(Fn [-> Any]
[Number Symbol -> Any])
baz
[Number -> Number])
(definterface IFooPoly
(bar [] [n s])
(baz [n]))
ann-many
macro
(ann-many t & vs)
Annotate several vars with type t.
eg. (ann-many FakeSearch
web1 web2 image1 image2 video1 video2)
ann-protocol
macro
(ann-protocol & args)
Annotate a possibly polymorphic protocol var with method types.
eg. (ann-protocol IFoo
bar
(IFn [IFoo -> Any]
[IFoo Number Symbol -> Any])
baz
[IFoo Number -> Number])
(t/tc-ignore
(defprotocol IFoo
(bar [this] [this n s])
(baz [this n])))
; polymorphic protocol
; x is scoped in the methods
(ann-protocol [[x :variance :covariant]]
IFooPoly
bar
(IFn [(IFooPoly x) -> Any]
[(IFooPoly x) Number Symbol -> Any])
baz
[(IFooPoly x) Number -> Number])
(t/tc-ignore
(defprotocol IFooPoly
(bar [this] [this n s])
(baz [this n])))
ann-record
macro
(ann-record & args)
Annotate record Class name dname with expected fields.
If unqualified, qualify in the current namespace.
Takes an optional type variable binder before the name.
Fields must be specified in the same order as presented
in defrecord, with exactly the same field names.
Also annotates record factories and constructors.
Binder is a vector of specs. Each spec is a vector
with the variable name as the first entry, followed by
keyword arguments:
- :variance (mandatory)
The declared variance of the type variable. Possible
values are :covariant, :contravariant and :invariant.
- :< (optional)
The upper type bound of the type variable. Defaults to
Any, or the most general type of the same rank as the
lower bound.
- :> (optional)
The lower type bound of the type variable. Defaults to
Nothing, or the least general type of the same rank as the
upper bound.
eg. ; a record in the current namespace
(ann-record MyRecord [a :- Number,
b :- Long])
; a record in another namespace
(ann-record another.ns.TheirRecord
[str :- String,
vec :- (Vec Number)])
; a record, polymorphic in a
(ann-record [[a :variance :covariant]]
MyPolyRecord
[str :- String,
vec :- (Vec Number)
ply :- (Set a)])
atom
macro
(atom & args)
Like atom, but with optional type annotations.
Same as (atom (ann-form init t) args*)
eg. (atom 1) : (Atom (Value 1))
(atom :- Num, 1) : (Atom Num)
cast
macro
(cast t x)
(cast t x opt)
Cast a value to a type. Returns a new value that conforms
to the given type, otherwise throws an error with blame.
eg. (cast Int 1)
;=> 1
(cast Int nil)
; Fail, <blame positive ...>
((cast [Int -> Int] identity)
1)
;=> 1
((cast [Int -> Int] identity)
nil)
; Fail, <blame negative ...>
(cast [Int -> Int] nil)
; Fail, <blame positive ...>
(defalias Options
(HMap :optional {:positive (U Sym Str),
:negative (U Sym Str)
:file (U Str nil)
:line (U Int nil)
:column (U Int nil)}))
(IFn [Contract Any -> Any]
[Contract Any Options -> Any])
Options:
- :positive positive blame, (U Sym Str)
- :negative negative blame, (U Sym Str)
- :file file name where contract is checked, (U Str nil)
- :line line number where contract is checked, (U Int nil)
- :column column number where contract is checked, (U Int nil)
cf
macro
(cf form)
(cf form expected)
Takes a form and an optional expected type and
returns a human-readable inferred type for that form.
Throws an exception if type checking fails.
Do not use cf inside a typed namespace. cf is intended to be
used at the REPL or within a unit test. Note that testing for
truthiness is not sufficient to unit test a call to cf, as nil
and false are valid type syntax.
cf preserves annotations from previous calls to check-ns or cf,
and keeps any new ones collected during a cf. This is useful for
debugging and experimentation. cf may be less strict than check-ns
with type checker warnings.
eg. (cf 1)
;=> Long
(cf #(inc %) [Number -> Number])
;=> [Number -> Number]
check-ns
(check-ns)
(check-ns ns-or-syms & {:as opt})
Type check a namespace/s (a symbol or Namespace, or collection).
If not provided default to current namespace.
Returns a true value if type checking is successful, otherwise
throws an Exception.
Do not use check-ns within a checked namespace.
It is intended to be used at the REPL or within a unit test.
Suggested idiom for clojure.test: (is (check-ns 'your.ns))
Keyword arguments:
- :trace If true, print some basic tracing of the type checker
Default: nil
- :check-config Configuration map for the type checker.
- :check-ns-dep If `:recheck`, always check dependencies.
If `:never`, ns dependencies are ignored.
#{:recheck :never}
Default: :recheck
- :unannotated-def If `:unchecked`, unannotated `def`s are ignored
and their type is not recorded.
If `:infer`, unannotated `def`s are inferred by their
root binding and the type is recorded in the type environment.
#{:unchecked :infer}
Also applies to `defmethod`s on unannotated `defmulti`s.
Default: :infer
- :unannotated-var If `:unchecked`, unannotated vars are given an *unsound*
annotation that is used to statically infer its type
based on usages/definition (see `infer-unannotated-vars`).
If `:any`, usages of unannotated vars are given type `Any` (sound).
If `:error`, unannotated vars are a type error (sound).
#{:unchecked :any :error}
Default: :error
- :unannotated-arg (Not Yet Implemented)
If `:unchecked`, unannotated fn arguments are given an *unsound*
annotation that is used to statically infer its argument types
based on definition.
If `:any`, unannotated fn arguments are give type `Any` (sound).
#{:unchecked :any}
Default: :any
Removed:
- :profile If true, use Timbre to profile the type checker. Timbre must be
added as a dependency. Must use the "slim" JAR.
Default: nil
If providing keyword arguments, the namespace to check must be provided
as the first argument.
Bind clojure.core.typed.util-vars/*verbose-types* to true to print fully qualified types.
Bind clojure.core.typed.util-vars/*verbose-forms* to print full forms in error messages.
eg. (check-ns 'myns.typed)
;=> :ok
; implicitly check current namespace
(check-ns)
;=> :ok
check-ns-info
(check-ns-info)
(check-ns-info ns-or-syms & opt)
Same as check-ns, but returns a map of results from type checking the
namespace.
Options
- :type-provided? If true, use the expected type to check the form
- :profile Use Timbre to profile the type checker. Timbre must be
added as a dependency. Must use the "slim" JAR.
- :file-mapping If true, return map provides entry :file-mapping, a hash-map
of (Map '{:line Int :column Int :file Str} Str).
- :check-deps If true, recursively type check namespace dependencies.
Default: true
Default return map
- :delayed-errors A sequence of delayed errors (ex-info instances)
check-ns2
(check-ns2)
(check-ns2 ns-or-syms & {:as opt})
declare-alias-kind
macro
(declare-alias-kind sym ty)
Declare a kind for an alias, similar to declare but on the kind level.
declare-datatypes
macro
(declare-datatypes & syms)
Declare datatypes, similar to declare but on the type level.
declare-names
macro
(declare-names & syms)
Declare names, similar to declare but on the type level.
declare-protocols
macro
(declare-protocols & syms)
Declare protocols, similar to declare but on the type level.
def
macro
(def name & fdecl)
Like clojure.core/def with optional type annotations. Registers
annotations like t/ann.
NB: in Clojure it not useful refer a macro called `def` as it is a
special form. Use an alias prefix (eg., `t/def`).
eg. ;same as clojure.core/def
(def vname 1)
;with Number `ann`
(def vname :- Number 1)
;doc
(def vname
"Docstring"
:- Long
1)
defalias
macro
(defalias sym doc-str t)
(defalias sym t)
Define a recursive type alias on a qualified symbol. Takes an optional doc-string as a second
argument.
Updates the corresponding var with documentation.
eg. (defalias MyAlias
"Here is my alias"
(U nil String))
;; recursive alias
(defalias Expr
(U '{:op ':if :test Expr :then Expr :else Expr}
'{:op ':const :val Any}))
default-check-config
(default-check-config)
defn
macro
(defn & args)
Like defn, but registers annotation with t/ann.
eg. (defn fname [a :- Number, b :- (U Symbol nil)] :- Integer ...)
;annotate return
(defn fname [a :- String] :- String ...)
;multi-arity
(defn fname
([a :- String] :- String ...)
([a :- String, b :- Number] :- Long ...))
;polymorphic function
(defn :forall [x y]
fname
([a :- x] :- (Coll y) ...)
([a :- Str, b :- y] :- y ...))
defprotocol
macro
(defprotocol & body)
Like defprotocol, but with optional type annotations.
Omitted annotations default to Any. The first argument
of a protocol cannot be annotated.
Add a binder before the protocol name to define a polymorphic
protocol. A binder before the method name defines a polymorphic
method, however a method binder must not shadow type variables
introduced by a protocol binder.
Return types for each method arity can be annotated.
Unlike clojure.core/defprotocol, successive methods can
have the same arity. Semantically, providing multiple successive
methods of the same arity is the same as just providing the left-most
method. However the types for these methods will be accumulated into
a Fn type.
eg. ;annotate single method
(defprotocol MyProtocol
(a [this a :- Integer] :- Number))
;polymorphic protocol
(defprotocol [[x :variance :covariant]]
MyProtocol
(a [this a :- Integer] :- Number))
;multiple types for the same method
(defprotocol [[x :variance :covariant]]
MyProtocol
(a [this a :- Integer] :- Integer
[this a :- Long] :- Long
[this a :- Number] :- Number))
;polymorphic method+protocol
(defprotocol [[x :variance :covariant]]
MyProtocol
([y] a [this a :- x, b :- y] :- y))
dotimes
macro
(dotimes bindings & body)
Like clojure.core/dotimes, but with optional annotations.
If annotation for binding is omitted, defaults to Int.
eg. (dotimes [_ 100]
(println "like normal"))
(dotimes [x :- Num, 100.123]
(println "like normal" x))
envs
(envs)
Returns a map of type environments, according to the current state of the
type checker.
Output map:
- :vars map from var symbols to their verbosely printed types
- :aliases map from alias var symbols (made with defalias) to their verbosely printed types
- :special-types a set of Vars that are special to the type checker (like Any, U, I)
fn
macro
(fn & forms)
Like clojure.core/fn, but with optional annotations.
eg. ;these forms are equivalent
(fn [a] b)
(fn [a :- Any] b)
(fn [a :- Any] :- Any b)
(fn [a] :- Any b)
;annotate return
(fn [a :- String] :- String body)
;named fn
(fn fname [a :- String] :- String body)
;rest parameter
(fn [a :- String & b :- Number *] body)
;dotted rest parameter
(fn [a :- String & b :- Number ... x] body)
;multi-arity
(fn fname
([a :- String] :- String ...)
([a :- String, b :- Number] :- String ...))
; polymorphic binder
(fn :forall [x y z]
fname
([a :- String] :- String ...)
([a :- String, b :- Number] :- String ...))
infer-unannotated-vars
(infer-unannotated-vars)
(infer-unannotated-vars nsym-or-ns)
EXPERIMENTAL
Return a vector of potential var annotations in the given
namespace, or the current namespace.
To enable for the current namespace, add the :infer-vars
:experimental feature to the ns metadata like so:
(ns infer-me
{:lang :core.typed
:core.typed {:experimental #{:infer-vars
:infer-locals}}}
...)
Then run check-ns like usual, and infer-unannotated-vars
will return the inferred vars without annotations.
(t/infer-unannotated-vars)
=> [(t/ann u/bar t/Int)
(t/ann u/foo (t/U [t/Any -> t/Any] Int))]
inst
macro
(inst inst-of & types)
Instantiate a polymorphic type with a number of types.
eg. (inst foo-fn t1 t2 t3 ...)
inst-ctor
macro
(inst-ctor inst-of & types)
Instantiate a call to a constructor with a number of types.
First argument must be an immediate call to a constructor.
Returns exactly the instantiatee (the first argument).
eg. (inst-ctor (PolyCtor. a b c)
t1 t2 ...)
install
(install)
(install features)
Install the :core.typed :lang. Takes an optional set of features
to install, defaults to `:all`, which is equivalent to the set of
all features.
Features:
- :load Installs typed `load` over `clojure.core/load`, which type checks files
on the presence of a {:lang :core.typed} metadata entry in the `ns` form.
The metadata must be inserted in the actual `ns` form saved to disk,
as it is read directly from the file instead of the current Namespace
metadata.
- :eval Installs typed `eval` over `clojure.core/eval`.
If `(= :core.typed (:lang (meta *ns*)))` is true, the form will be implicitly
type checked. The syntax save to disk is ignored however.
eg. (install) ; installs `load` and `eval`
eg. (install :all) ; installs `load` and `eval`
eg. (install #{:eval}) ; installs `eval`
eg. (install #{:load}) ; installs `load`
into-array>
macro
(into-array> cljt coll)
(into-array> javat cljt coll)
(into-array> into-array-syn javat cljt coll)
Make a Java array with Java class javat and Typed Clojure type
cljt. Resulting array will be of type javat, but elements of coll must be under
cljt. cljt should be a subtype of javat (the same or more specific).
*Temporary hack*
into-array-syn is exactly the syntax to put as the first argument to into-array.
Calling resolve on this syntax should give the correct class.
let
macro
(let bvec & forms)
Like clojure.core/let but supports optional type annotations.
eg. (let [a :- Type, b
a2 1.2]
body)
letfn>
macro
(letfn> fn-specs-and-annotations & body)
Like letfn, but each function spec must be annotated.
eg. (letfn> [a :- [Number -> Number]
(a [b] 2)
c :- [Symbol -> nil]
(c [s] nil)]
...)
load-if-needed
(load-if-needed)
Load and initialize all of core.typed if not already
loop
macro
(loop bindings & exprs)
Like clojure.core/loop, and supports optional type annotations.
Arguments default to a generalised type based on the initial value.
eg. (loop [a :- Number 1
b :- (U nil Number) nil]
...)
method-type
(method-type mname)
Given a method symbol, print the core.typed types assigned to it.
Intended for use at the REPL.
nilable-param
macro
(nilable-param msym mmap)
Override which parameters in qualified method msym may accept
nilable values. If the parameter is a parameterised type or
an Array, this also declares the parameterised types and the Array type as nilable.
mmap is a map mapping arity parameter number to a set of parameter
positions (integers). If the map contains the key :all then this overrides
other entries. The key can also be :all, which declares all parameters nilable.
non-nil-return
macro
(non-nil-return msym arities)
Override the return type of fully qualified method msym to be non-nil.
Takes a set of relevant arities,
represented by the number of parameters it takes (rest parameter counts as one),
or :all which overrides all arities.
eg. ; must use full class name
(non-nil-return java.lang.Class/getDeclaredMethod :all)
override-constructor
macro
(override-constructor ctorsym typesyn)
Override all constructors for Class ctorsym with type.
override-method
macro
(override-method methodsym typesyn)
Override type for qualified method methodsym.
methodsym identifies the method to override and should be a
namespace-qualified symbol in the form <class>/<method-name>.
The class name needs to be fully qualified.
typesyn uses the same annotation syntax as functions.
Use non-nil-return instead of override-method if you want to
declare that a method can never return nil.
Example:
(override-method java.util.Properties/stringPropertyNames
[-> (java.util.Set String)])
This overrides the return type of method stringPropertyNames
of class java.util.Properties to be (java.util.Set String).
pred
macro
(pred t)
Generate a flat (runtime) predicate for type that returns true if the
argument is a subtype of the type, otherwise false.
The current type variable and dotted type variable scope is cleared before parsing.
eg. ((pred Number) 1)
;=> true
prepare-infer-ns
(prepare-infer-ns & {:keys [ns strategy], :as config, :or {strategy :compile, ns *ns*}})
Instruments the current namespace to prepare for runtime type
or spec inference.
Optional keys:
:ns The namespace to infer types for. (Symbol/Namespace)
Default: *ns*
:strategy Choose which inference preparation strategy to use.
- :compile recompile the namespace and wrap at compilation-time.
Supports local annotation inference. Source is analyzed
via core.typed's custom analyzer.
- :instrument wrap top-level vars without recompilation.
No support for local annotations, but the default
Clojure analyzer is used.
Default: :compile
:track-strategy Choose which track strategy to use.
- :lazy wrap hash maps and possibly other data structures, and
lazily track values as they are used.
- :eager eagerly walk over all values, a la clojure.spec checking.
Default: :lazy
print-env
(print-env debug-str)
During type checking, print the type environment to *out*,
preceeded by literal string debug-str.
print-filterset
(print-filterset debug-string frm)
During type checking, print the filter set attached to form,
preceeded by literal string debug-string.
Returns nil.
eg. (let [s (seq (get-a-seqable))]
(print-filterset "Here now" s))
ref
macro
(ref & args)
Like ref, but with optional type annotations.
Same as (ref (ann-form init t) args*)
eg. (ref 1) : (Ref (Value 1))
(ref :- Num, 1) : (Ref Num)
refresh-runtime-infer
(refresh-runtime-infer)
Clean the current state of runtime inference.
Will forget the results of any tests on instrumented code.
reset-caches
(reset-caches)
Reset internal type caches.
runtime-infer
(runtime-infer & kws)
Infer and insert annotations for a given namespace.
There are two ways to instrument your namespace.
Call `prepare-infer-ns` function on the namespace
of your choosing.
Alternatively, use the :runtime-infer
feature in your namespace metadata. Note: core.typed
must be installed via `clojure.core.typed/install`.
eg. (ns my-ns
{:lang :core.typed
:core.typed {:features #{:runtime-infer}}}
(:require [clojure.core.typed :as t]))
After your namespace is instrumented, run your tests
and/or exercise the functions in your namespace.
Then call `runtime-infer` to populate the namespace's
corresponding file with these generated annotations.
Optional keys:
:ns The namespace to infer types for. (Symbol/Namespace)
Default: *ns*
:fuel Number of iterations to perform in inference algorithm
(integer)
Default: nil (don't restrict iterations)
:debug Perform print debugging. (:all/:iterations/nil)
Default: nil
:track-depth Maximum nesting depth data will be tracked.
Default: nil (don't restrict nestings)
:track-count Maximum number of elements of a single collection
will be tracked.
Default: nil (don't restrict elements)
:root-results Maximum number of inference results collected per top-level
root form, from the perspective of the tracker (eg. vars, local functions).
Default: nil (don't restrict)
:preserve-unknown If true, output the symbol `?` where inference was cut off
or never reached.
Default: nil (convert to unknown to `clojure.core.typed/Any`)
:out-dir A classpath-relative directory (string) to which to dump changes to files,
instead of modifying the original file.
Default: nil (modify original file)
:no-squash-vertically If true, disable the `squash-vertically` pass.
Default: nil
eg. (runtime-infer) ; infer for *ns*
(runtime-infer :ns 'my-ns) ; infer for my-ns
(runtime-infer :fuel 0) ; iterations in type inference algorithm
; (higher = smaller types + more recursive)
(runtime-infer :debug :iterations) ; enable iteration debugging
spec-infer
(spec-infer & kws)
Infer and insert specs for a given namespace.
There are two ways to instrument your namespace.
Call `prepare-infer-ns` function on the namespace
of your choosing.
Alternatively, use the :runtime-infer
feature in your namespace metadata. Note: core.typed
must be installed via `clojure.core.typed/install`.
eg. (ns my-ns
{:lang :core.typed
:core.typed {:features #{:runtime-infer}}}
(:require [clojure.core.typed :as t]))
After your namespace is instrumented, run your tests
and/or exercise the functions in your namespace.
Then call `spec-infer` to populate the namespace's
corresponding file with these generated specs.
Optional keys:
:ns The namespace to infer specs for. (Symbol/Namespace)
Default: *ns*
:fuel Number of iterations to perform in inference algorithm
(integer)
Default: nil (don't restrict iterations)
:debug Perform print debugging. (:all/:iterations/nil)
Default: nil
:track-depth Maximum nesting depth data will be tracked.
Default: nil (don't restrict nestings)
:track-count Maximum number of elements of a single collection
will be tracked.
Default: nil (don't restrict elements)
:root-results Maximum number of inference results collected per top-level
root form, from the perspective of the tracker (eg. vars, local functions).
Default: nil (don't restrict)
:preserve-unknown If true, output the symbol `?` where inference was cut off
or never reached.
Default: nil (convert to unknown to `clojure.core/any?`)
:higher-order-fspec If true, generate higher-order fspecs.
Default: false
:out-dir A classpath-relative directory (string) to which to dump changes to files,
instead of modifying the original file.
Default: nil (modify original file)
:no-squash-vertically If true, disable the `squash-vertically` pass.
Default: nil
:spec-macros If true, output specs for macros.
Default: nil (elide macro specs)
eg. (spec-infer) ; infer for *ns*
(spec-infer :ns 'my-ns) ; infer for my-ns
(spec-infer :fuel 0) ; iterations in spec inference algorithm
; (higher = smaller specs + more recursive)
(spec-infer :debug :iterations) ; enable iteration debugging
statistics
(statistics nsyms)
Takes a collection of namespace symbols and returns a map mapping the namespace
symbols to a map of data
tc-ignore
macro
(tc-ignore & body)
Ignore forms in body during type checking
typed-deps
macro
(typed-deps & args)
Declare namespaces which should be checked before the current namespace.
Accepts any number of symbols. Only has effect via check-ns.
eg. (typed-deps clojure.core.typed.holes
myns.types)
untyped-var
macro
(untyped-var varsym typesyn)
Check a given var has the specified type at runtime.
var-coverage
(var-coverage)
(var-coverage nsyms-or-nsym)
Summarises annotated var coverage statistics to *out*
for namespaces nsyms, a collection of symbols or a symbol/namespace.
Defaults to the current namespace if no argument provided.
var>
macro
(var> sym)
Like var, but resolves at runtime like ns-resolve and is understood by
the type checker. sym must be fully qualified (without aliases).
eg. (var> clojure.core/+)
warn-on-unannotated-vars
macro
(warn-on-unannotated-vars)
Allow unannotated vars in the current namespace.
Emits a warning instead of a type error when checking
a def without a corresponding expected type.
Disables automatic inference of `def` expressions.
eg. (warn-on-unannotated-vars)
when-let-fail
macro
(when-let-fail b & body)
Like when-let, but fails if the binding yields a false value.