Skip to end of metadata
Go to start of metadata

Rationale / Problem statement

There are cases where it would be advantageous to allow type-hinted overloads of Clojure functions.


(defn coerce-long
"Converts a value to a primitive long, parsing strings if necessary. Throws an error if the value cannot be converted successfully."
(^long [^long n] n)
(^long [^int n] (long n))
(^long [^String s]
(Long/parseLong s))
(^long [^Number n]
(let [result (long n)]
(if (== n result)
(throw (IllegalArgumentException. (str "Can't convert accurately to long: " n))))))

With the advent of direct linking, it would be possible for the compiler to optimise dispatch to the correct method in cases where the type of the argument can be statically determined

Why existing solutions are not satisfactory

  • Primitive-hinted functions are already available, but don't allow multiple overloads and do not help with different Object types
  • You can use multi-methods or protocols, but these impose additional overhead, require extra code and provide open extension (which is unnecessary in many cases)
  • You can manually write `instance?` checks, but these are less idiomatic and don't allow the compiler to optimise dispatch in cases where the type is already statically known

Proposed implementation

  • The compiler should emit bytecode for a separate method for each function overload (possibly named `invokeStatic`, since the methods would be the same as those needed for static invocation / direct linking)
  • In order to satisfy the `IFn` interface, the compiler should also generate a method using `java.lang.Object` parameters and return values. Internally, this could be implemented by generating a `cond` expression with `instance?` checks that invokes the correct typed variant (probably in order of declaration? or following Java dispatch conventions?)
  • If an overloaded function signature is also allowable as a primitive-hinted function, it should also create the appropriate `invokePrim` method and implement the corresponding interface. In this sense, this implementation might be considered as a generalisation of the existing `invokePrim` functionality.

Backwards compatibility

This feature should not affect the behaviour of any existing code. Note that:

  • Primitive-hinted function should continue to operate as before
  • Existing type-hinted functions should continue to compile and behave as before (except that they may now be further optimised)
  • Separate arity overloads would continue to be possible (would be orthogonal to this feature, since arities would not conflict)

Some additional tool support may be required to allow overloaded functions to be recognised / analysed.