(if (string? x)
(inc x)
10)
31 January 2019
ClojureScript Team
Spec instrumentation (cljs.spec.test.alpha/instrument
and related functionality)
no longer requires test.check
. If you use cljs.spec.test.alpha/check
, the data
generation functionality of test.check
is needed; in that case you need to require
the clojure.test.check
and clojure.test.check.properties
namespaces.
Keywords used in the cljs.spec.test.alpha/check
API pertaining to Spec’s use
of test.check
are now qualified with clojure.spec.test.check
, thus aligning
with Clojure. The previous way of qualifying with clojure.test.check
is still
supported.
The improved exception infrastructure added in Clojure 1.10 has been ported to ClojureScript with this release.
A new clojure.edn
namespace is added with this release which delegates to
cljs.reader
for functionality. This facilitates writing portable
Clojure / ClojureScript source making use of clojure.edn/read
and
clojure.edn/read-string
.
The type inference algorithm will now consider core predicates when inferring the types of locals used in conditional expressions.
For example, in
(if (string? x)
(inc x)
10)
because x
satisfies string?
, it will be inferred to be of string type
in the then branch (and thus cause a warning to be emitted because inc
is being applied to it).
Because cond
and when
are macros built on top of if
, predicate-induced
inference also works as expected for expressions involving cond
and when
.
In addition to core predicates, predicate-induced type inference also works
for instance?
checks. So, for example testing (instance? Atom x)
will
result in x
being inferred of type cljs.core/Atom
.
In situations where a value could potentially be nil
(represented by the
symbol clj-nil
in type tags), if a simple symbol referring to such a value
is used as the test in a conditional, the type inference algorithm will
infer that the value cannot be nil
in the then branch.
This is perhaps best illustrated by way of example. Let’s say you have the following function:
(defn f [x]
(when (even? x)
(inc x)))
This function’s return type is #{number clj-nil}
, meaning that
either a number or nil
can be returned.
The following function, which uses f
and would previously be inferred as
returning #{number clj-nil}
, is now inferred as returning number
:
(defn g [y]
(let [z (f y)]
(if z
z
17)))
In fact, owing to the way the or
macro expands, the expression
(or (f 1) 17)
is now inferred as being simply number
.
loop
/ recur
InferenceThe type-inferrence algorithm will now consider recur
parameter types
when inferring loop
local types.
For example, in
(loop [x "a"]
(if (= "a" x)
(recur 1)
(+ 3 x)))
the local x
would previously be inferred to be of string type (and
this would cause a warning to be emitted for the expression adding it
to 3
). Now, the compiler will infer x
to be either string or numeric
(and thus the warning will no longer appear).
ClojureScript 1.10.439 added function return type inference, but this capability only worked for single-arity functions. This release extends this capability to multi-arity and variadic functions.
Furthermore, the inferred return type will properly vary if different arities return different types. For example,
(defn foo
([x] 1)
([x y] "a"))
then the expression (foo true)
will be inferred to be of numeric type
while (foo :a :b)
will be inferred to be of string type.
Several improvements in the Spec implementation are in this release, making it easier to spec functions in the standard core library, as well as improving instrumentation performance when a large number of functions in a codebase have specs.
ClojureScript now supports chunked-seqs for ranges. An example where this capability improves performance is
(reduce + (map inc (map inc (range (* 1024 1024)))))
which is evaluated 5 times faster in V8, 7 times in SpiderMonkey, and 2 times in JavaScriptCore.
re-seq
Performancere-seq
performance has been improved, with a speedup of 1.5 or more under major JavaScript engines.
Generally, arguments supplied to the str
function are first coerced
to strings before being concatenated. With this release, unnecessary
coercion is eliminated for arguments that are inferred to be of string
type, leading to more compact codegen as well as a speed boost.
For example, in
(defn foo [x y]
(str (+ x y)))
(str (name :foo/bar) "-" (foo 3 2))
the last str
expression is evaluated 3 times faster in V8 and 4 times
faster in JavaSriptCore as a result of the improved codgen.
For a complete list of updates in ClojureScript 1.10.516 see Changes.