Error formatting macro: pagetree: java.lang.NullPointerException
Skip to end of metadata
Go to start of metadata
You are viewing an old version of this page. View the current version. Compare with Current  |   View Page History

This document describes work in progress in the master (1.3) branch. This work is not promised to become part of Clojure, as is or otherwise, now or in the future. The purpose of this document is to explain the work and encourage discussion.

Objective - Support primitives as fn arguments and return values

  • Clojure supports primitive arithmetic locally, and primitives in deftype/defrecord
    • But not in fn arguments or returns
  • Means you can’t break up your logic, or write helper functions, without boxing


  • fn is specified by an interface (IFn), taking/returning objects
    • fns must still satisfy that interface
  • How will callers know about primitive params/return, and how to invoke?
    • New :static support
    • can we decouple from static?


  • defn supports {:static true} metadata
  • :static fns can take/return longs and doubles in addition to Objects
  • compiler will compile static methods in addition to IFn virtual methods
  • When compiling direct invocation of :static fn, compiler will look at var metadata and make call to static method
    • Note that because it is not going through the var at runtime, will not see any dynamic modifications to the var
      • thus 'static'
      • Caller won't see redefs, bindings etc
      • Like macros, recompile client code to see changes
  • The var still contains IFn object for HOFs etc
    • and you can get dynamic call by using (#'foo ...)
  • New ^:keyword metadata
    • turns into ^{:keyword true}
  • Metadata stacks - ^:static ^:private foo == ^{:static true :private true} foo
  • ^long and ^double hints allowed on args
    • other reference type hints allowed as well
      • unlike for non-statics, these will be enforced (when invoked statically)
  • hint for return goes on arg vector
    • e.g. (defn ^:static foo ^long x ...)
    • this so it supports multi-arity with varied returns
  • Static linking - replaces direct binding

Static linking

  • Eliminates the overhead of keeping fns in vars
  • Normally not an issue, but for small functions vars have 2 overheads:
    • volatile can cause cache miss
    • volatile inhibits inlining
  • Useful even when not using primitive args/returns
  • Known limitations
    • static fns can't be closures
    • temporary limitation
      • static fns can't be protocol call sites

Objective - normalize arithmetic semantics between boxed and primitive numbers

Currently (1.2 and prior), operations between e.g. longs and Longs have different semantics. The latter can return a BigInteger on overflow while the former can't (and thus throws an exception)

  • Use (type flexible) equiv for =
    • but narrow equiv, only same category numbers are =
    • so floats not = to integers
      • but == still available for that
  • new clojure.lang.BigInt class
  • BigInts do not auto-reduce, and are contagious (akin to doubles)
    • BigInts will enable optimizations when fits in long
      • optimzations not yet in place
    • unlike BigInteger, BigInt has consistent hashcodes with Long, through range of long
  • all long-or-smaller integers are boxed as Long
    • You can't use primitive coercions to force specific box types
      • Your code was broken if you were relying on that
  • hash maps and sets now use = for keys
    • this will make maps and sets unify 42 and 42N, since using =
    • will use stricter .equals when calling through java.util interfaces
      • not there yet
      • this will require renaming Associative.containsKey, since semantics will now differ
  • +' (prime ops) available when auto-promotion is required, but they are not needed for polymorphic arithmetic
  • The plan is to offer 2 sets of ops for the few ops that might overflow: +, -, *, inc, dec.
  • +, -, *, inc, dec have been enhanced. They will never auto-promote on integer overflow, and will instead throw an exception. Thus, the semantics of these operators are now uniform when used with boxed or primitive arguments. They can return integer primitives.
  • ' is now a constituent character. That means it may appear as a character in symbols in other than the first position, as does #.
  • +', -', *', inc', dec' For all arguments other than long-or-smaller integers, they do exactly what their non-prime counterparts do. For long-or-smaller integers, they will auto-promote on overflow in all cases, even when given integer primitives. As such, they will never return integer primitives. They can and do return double primitives when given doubles, as the semantics match. Unless you need to work with arbitrary-precision integers where BigInteger contagion is insufficient, there is no reason to use them.
  • literal numbers are always implicitly primitives, and never need (long 42) style coercion except for host interop overloading disambiguation.
    • Note: this means that locals initialized with literals will have primitive type, e.g. (let [x 42] ...), and especially: (loop [x 42] ...). If you intend to recur with a non-primitive, init like this, (loop [x (num 42)|x (num 42)] ...)