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

Issues

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)

Performance

(defn fib [n]
  (if (<= n 1)
    1
    (+ (fib (dec n)) (fib (- n 2)))))

(time (fib 38))
"Elapsed time: 2973 msecs"

;; hint arg and return
(defn fib ^long [^long n]
  (if (<= n 1)
    1
    (+ (fib (dec n)) (fib (- n 2)))))

(time (fib 38))
"Elapsed time: 390 msecs"

;Use promoting op' ops for arbitrary precision
(defn fib [n]
  (if (<= n 1)
    1
    (+' (fib (dec' n)) (fib (-' n 2)))))

(time (fib 38))
"Elapsed time: 3092 msecs"

;; double arg/return
(defn fib ^double [^double n]
  (if (<= n 1)
    1
    (+ (fib (dec n)) (fib (- n 2)))))

(time (fib 38))
"Elapsed time: 944 msecs"

;; double arg/return, double operands
(defn fib ^double [^double n]
  (if (<= n 1.0)
    1.0
    (+ (fib (dec n)) (fib (- n 2.0)))))

(time (fib 38))
"Elapsed time: 448 msecs"

Promotion Options

(set! *warn-on-reflection* false)

(defn fact [n]
  (loop [cnt n acc 1]
    (if (zero? cnt)
      acc
      (recur (dec cnt) (* acc cnt)))))

#'user/fact

(set! *warn-on-reflection* true)

(defn fact [n]
  (loop [cnt n acc 1]
    (if (zero? cnt)
      acc
      (recur (dec cnt) (* acc cnt)))))

NO_SOURCE_FILE:1738 recur arg for primitive local: acc is not matching primitive, had: Object, needed: long
Auto-boxing loop arg: acc
#'user/fact

;; hinted version, non-promoting
(defn fact [^long n]
  (loop [cnt n acc 1]
    (if (zero? cnt)
      acc
      (recur (dec cnt) (* acc cnt)))))

 #'user/fact
;;no warning

(fact 12)
479001600

;; but not auto-promoting
(fact 42)
ArithmeticException integer overflow  clojure.lang.Numbers.throwIntOverflow (Numbers.java:1575)

;; non-hinted version *is* general
(set! *warn-on-reflection* false)

(defn fact [n]
  (loop [cnt n acc 1]
    (if (zero? cnt)
      acc
      (recur (dec cnt) (* acc cnt)))))

;; caller can just leverage BigInt contagion
(fact 42N)
1405006117752879898543142606244511569936384000000000N

;; or, force promoting operations with prime (') ops
(defn fact [n]
  (loop [cnt n acc 1]
    (if (zero? cnt)
      acc
      (recur (dec' cnt) (*' acc cnt)))))

(fact 42)
1405006117752879898543142606244511569936384000000000N