Issue
I want to call the binomial
Clojure function from Java. One problem I encounter is that it returns different data types, either long
(e. g., n=5, k=3) or BigInt
(e. g., n=20, k=10). On Java side, it should be a BigInteger.
There are at least two options to overcome this, which is preferable?
- Force Clojure function to return BigInt (I don't know if this is possible. I tried type hints, but it still returns either
long
orBigInt
). - In Java, use Pattern match on return value and check type, then convert appropriate.
Clojure
(ns sample.hello
(:import (clojure.lang BigInt)))
(defn binomial
"Calculate the binomial coefficient."
^BigInt [^Integer n ^Integer k]
(let [a (inc n)]
(loop [b 1
c 1]
(if (> b k)
c
(recur (inc b) (* (/ (- a b) b) c))))))
Java
public class Hello {
public static final IFn binomial;
static {
Clojure.var("clojure.core", "require").invoke(Clojure.read("sample.hello"));
binomial = Clojure.var("sample.hello", "binomial");
}
public static BigInteger binomial(int n, int k) {
Object a = binomial.invoke(n, k);
return ((BigInt) a).toBigInteger();
}
}
Solution
Please note that type hints (emphasis on the work "hint") are only used in Clojure for compiler optimizations, and cannot coerce types (or even warn if the wrong type is present!). I would never use them unless you have profiled with Criterium etc.
Having said that, I would tweak Martin's answer just a bit:
(defn binomial
"Calculate the binomial coefficient."
[n k]
(biginteger
(let [a (inc n)]
(loop [b 1
c 1]
(if (> b k)
c
(recur (inc b) (* (/ (- a b) b) c)))))))
This emphasizes that the result of the let
block will always be coerced to BigInteger
. Also, it removes the deceptive "casting" to type Integer
at the beginning.
If you are really worried a user may provide non-integer arguments, use an assert
or similar as the first line of the function:
(assert (and (int? n) (int? k))) ; or `integer?` or `pos-int?`
It is also often helpful to coerce input args to a known type, which can bypass the combinatorical explosion of "maybes"
(defn binomial
"Calculate the binomial coefficient."
[n k]
(assert (and (pos-int? n) (pos-int? k)))
(let [n (biginteger n)
k (biginteger k)
a (inc n)]
(loop [b 1
c 1]
(if (> b k)
c
(recur (inc b) (* (/ (- a b) b) c))))))
In this case, the coercion of the return type is unnecessary, since math operations on BigInteger
values will always produce another BigInteger
.
Answered By - Alan Thompson
Answer Checked By - Timothy Miller (JavaFixing Admin)