r/Clojure • • 27d ago

Clojure 1.13.0-alpha8 is now available

Selectors

A developer may need to apply the same key-selection logic embedded in a map destructuring form (as directives, :keys!/:syms!/:strs!/etc., nesting, etc.) against multiple maps, or want to hand that selection logic to another part of the program. Currently that logic is oriented around binding in destructuring and not for programmatic use.

This release adds a new selector macro that takes a destructuring form and returns a function that can be applied to maps:

(selector [m])
  Builds a selecting-fn from m, a map destructuring form that must
  include one or more of the :select, :all, :missing, and :excess
  directives. The return function takes a collection, destructures it
  per m, and returns a map of the result(s).

  If m has exactly one directive, the result is the value that
  directive would yield. If m has more than one directive, then it
  returns a map of directives to values.

  As in destructuring, :missing controls whether missing required keys
  throw or are collected.

  While a map destructuring form may and sometimes must include
  bindings, selector doesn't produce bindings, thus ignoring the
  associated directive names.

  Throws an exception if the argument is not a map.

Transient map performance

This release contains many performance improvements around the use of maps, particularly transient maps, and functions like into, merge, and merge-with.

Transient array maps now grow larger before transitioning to transient hash maps (similar to recent persistent array map changes), and can be loaded significantly faster. The increased use of persistent and transient array maps will also make many more call sites inside the Clojure runtime monomorphic, encouraging greater JVM inlining. For more info see CLJ-2980.

Improvements and bug fixes

  • CLJ-2939 QualifiedMethodExpr field overload does not propagate field type - fixed
  • CLJ-1468 merge-deep, merge-deep-with - added recursive merge fns
  • CLJ-2981 merge - added tests
  • CLJ-2623 tagged literals and Java constructors - allow full unicode for first char
  • CLJ-2623 tagged-literal equality - check tag before form for performance
  • CLJ-2842 defn - does not needlessly initialize class when defn returns class
  • CLJ-2896 clojure.zip/next - only call zip/up once (not 3x)
  • CLJ-2915 ExceptionInfo - added to default imports, so no import or qualification needed
63 Upvotes

5 comments sorted by

2

u/lostcoffee 27d ago

re https://clojure.atlassian.net/browse/CLJ-2915: interesting. Because clojure.lang.ExceptionInfo wasn't exposed via Clojure, I've been treating it like an implementation detail and dealing with ex-infos like

(try
  ...
  (catch Exception e
    (if-let [data (ex-data e)]
      ...
      (throw e))))

3

u/alexdmiller 27d ago

Seems totally fine too

1

u/pragyantripathi 27d ago

Are there benchmarks for the transient map changes on into and merge with small maps? Curious how much of the gain comes from the higher array-map threshold versus faster loading.

3

u/alexdmiller 27d ago

I think the biggest improvement in TAM assoc is actually from the key-specific indexOf change - that’s actually the bottleneck. The metadata propagation was also significant. Lots of other incremental improvements.

3

u/lgstein 27d ago

I'm fairly confident that the :req stuff might turn out a significant game changer for the Clojure developer experience. I did something similar in march or so, writing a little declarative "lean ad hoc type DSL" - runtime checked - for certain frequently used top level domain constructors in a project. While the "type" stuff was less generalizable, the ":req" stuff made things so much better alone. Just "this key is.missing" instead of an obscure NPE (or unintended nil punning with unintended consequences) to be fished out/traced back from a stacktrace somewhere else. It is not so much about "correctness" claims, just reducing friction from miscommunication between programmer and computer in both directions. Very pragmatic. Looking forward to have this available cheaply and ad hoc in map destructoring without having to invest in a whole spec/type system with separate declarations and names etc.