Skip to content

dynamic-object-1.4.0

Compare
Choose a tag to compare
@rschmitt rschmitt released this 05 Apr 23:45

This is a major release with loads of new features:

  • Fressian support has been added. Fressian is essentially binary Edn: a self-describing, extensible data encoding that offers high performance and compact output. (more)
  • DynamicObject is now implemented with invokedynamic-proxy, instead of conventional reflection-based Java proxies. Benchmarking has shown that default method invocations on the latest version of DynamicObject are up to 95% faster than the previous version. Additional JMH data is available here.
  • The DynamicObject interface now implements java.util.Map by delegating to the underlying Clojure map. This makes calling DynamicObject#getMap redundant for many use cases, and creates a migration path from maps to DynamicObjects. (Needless to say, the mutating methods like put will throw an UnsupportedOperationException.)
  • All DynamicObject instances now implement Clojure's map interfaces, such as IPersistentMap. This means that a DynamicObject instance, when passed to Clojure code, looks and behaves exactly like an ordinary Clojure map. (The main exception is transient support, which has not been added.)
  • DynamicObject nesting no longer involves wrapping and unwrapping of Clojure maps. In previous versions, a nested DynamicObject was stored as a Clojure map in the parent hash map; now, DynamicObjects are never unwrapped. This makes type-based dispatch (e.g. for Fressian writers and pretty-printing) much more robust and reliable.
  • DynamicObject's :type metadata has been abolished. Metadata is no longer used to control print-method dispatch, or to identify unwrapped DynamicObjects.
  • Support for pretty-printing has been improved substantially: calling toFormattedString or prettyPrint will produce machine-readable output. In previous versions, pretty-printing of DynamicObjects resulted in reader tags being dropped. (Clojure's defrecord has the same problem.)

Internal changes:

  • Classes that are not part of the public API have been moved to the internal package. This leaves a total of eight classes (two interfaces, three classes, three annotations) in the public package.
  • As part of the invokedynamic-proxy changes, reflection has been decoupled from invocation (1, 2, 3). The DynamicObjectInstance class has been introduced; this class is a de facto abstract base class for all DynamicObject types. All calls on a DynamicObject interface (excepting static methods) now delegate to this class, according to a call site binding determined by the invocation handler. (Note that thanks to indy-proxy, methods can now be added to the DynamicObject interface without changing the invocation handler. This is how support for the Map interface was added.)
  • Reflective proxy support has been removed, and DynamicObjectInvocationHandler has been eliminated. Its replacement is InvokeDynamicInvocationHandler, which only runs once per Method (i.e. on the first invocation of a given method for a given DynamicObject type).
  • RecordPrinter has been removed. Printing of all DynamicObject types, whether tagged or not, is now delegated to DynamicObjectPrintMethod or DynamicObjectPrettyPrint, both of which are defined in EdnSerialization.
  • The massive DynamicObjects (plural) class has been broken up into the following classes: Instances, Serialization, EdnSerialization, and FressianSerialization.
  • The Metadata class has been removed. DynamicObject no longer uses metadata in its implementation.
  • As a matter of convention, the type variable D is now used for all DynamicObject types; T is still used for type variables that do not have a DynamicObject upper bound.