Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to call a Clojure variadic function from Java

Is it possible to declare a variadic function in Clojure that can be called as a varargs method from Java?

Consider this extract from some code under development:

(ns com.mydomain.expression.base 
  (:gen-class
    :name com.mydomain.expression.Base
    :methods [^:static [exprFactory [String String ?????] Object]]
  )

(defn expr-factory
  ; worker function that is also called from Clojure
  [id func & args]
  (let [ex ("construction code here")]
    ex))

(defn -exprFactory
  ; interface method called from Java
  [idStr funcStr & argsArray]
  (apply expr-factory idStr funcStr (seq argsArray)))

Is there anything I can put in place of ????? to allow Java to call the exprFactory method and to know that it is a varargs method:

import com.mydomain.expression.Base;
...
Object e1 = Base.exprFactory("e1", "day");
Object e2 = Base.exprFactory("e2", "length", "string");
Object e3 = Base.exprFactory("e3", "*", 4, 5);
Object sum = Base.exprFactory("sum", "+", e1, e2, e3);

To make this a little clearer, I know I use Object in place of ????? and change exprFactory to:

(defn -exprFactory
  ; interface method called from Java
  [idStr funcStr argsArray]
  (apply expr-factory idStr funcStr (seq argsArray)))

..but that means I have to write Java calls like this:

import com.mydomain.expression.Base;
...
Object e1 = Base.exprFactory("e1", "day", new Object[0] ));
Object e2 = Base.exprFactory("e2", "length", new Object[] { "string" }));
Object e3 = Base.exprFactory("e3", "*", new Integer[] { 4, 5 }));
Object sum = Base.exprFactory("sum", "+", new Object[] { e1, e2, e3 }));

Again, I know I could write a varargs wrapper method in Java that calls the non-variadic exprFactory, but I'd like to avoid that if possible.

like image 344
Dave Hartnoll Avatar asked May 02 '12 12:05

Dave Hartnoll


People also ask

Does Java have Variadic functions?

Core Java bootcamp program with Hands on practiceMethods which uses variable arguments (varargs, arguments with three dots) are known as variadic functions.

How do you return a function in Clojure?

Functions Returning Functions and Closures Our first function will be called adder . It will take a number, x , as its only argument and return a function. The function returned by adder will also take a single number, a , as its argument and return x + a . The returned function form adder is a closure.

How do you write a Variadic function?

*type* indicates the data type the *va_list ap* should expect (double, float, int etc.) This makes a copy of the variadic function arguments. This ends the traversal of the variadic function arguments. Here, va_list holds the information needed by va_start, va_arg, va_end, and va_copy.


1 Answers

I would suggest writing your helper function on the Java side, something like the "applyToHelper" in clojure.lang.AFn

This would take the form of a Java function that looks something like:

   public Object invokeVariadic(IFn function, Object... args) {
     switch (args.length) {
       case 0:
         return function.invoke();
       case 1:
         return function.invoke(args[0]);
       /// 20-odd more cases
     }
   }

It's a bit of a hack and depends on the internal definition of clojure.lang.IFn, but at least you will get relatively nice variadic syntax on the Java side (i.e. no need to do the new Object[] {...} stuff).

like image 136
mikera Avatar answered Oct 07 '22 05:10

mikera