This behavior makes no sense to me:
user=> (type 1)
java.lang.Long
user=> (type (cast Long 1))
java.lang.Long
user=> (type 1)
java.lang.Long
user=> (type (Long. 1))
java.lang.Long
user=> (type (cast Long 1))
java.lang.Long
user=> (BigDecimal. 1)
1M
user=> (BigDecimal. (Long. 1))
CompilerException java.lang.IllegalArgumentException: More than one matching method found: java.math.BigDecimal, compiling:(NO_SOURCE_PATH:22)
user=> (BigDecimal. (cast Long 1))
1M
Why does the (BigDecimal. (Long. 1)) case fail to find an unambiguous matching method signature while the other two expressions—which have exactly the same argument type—succeed?
Update:
What I find even more strange about this behavior is that it seems particular to the Long type:
user=> (BigDecimal. (Long. 1))
CompilerException java.lang.IllegalArgumentException: More than one matching method found: java.math.BigDecimal, compiling:(NO_SOURCE_PATH:1)
user=> (BigDecimal. (Integer. 1))
1M
From this discussion on the Clojure discussion group it seems you have encountered a design decision made by Rich Hickey. Specifically, because BigDecimal does not have a constructor of the signature
BigDecimal(Long long)(Edit: and therefore leaves the compiler having to choose between theintandlongconstructors – see comments below for discussion on why usingIntegerworks), the compiler will not attempt to “guess” which constructor you will meant, and explicitly fails.Note that literals are parsed as primitives, not “boxed” types, per this documentation:
To understand why the other operations work, you have to dig into Clojure source, specifically
Compiler.javaand its inner classNumberExpr. That is the where your literal gets auto-boxed to aLongand the compiler has no problem in turn callingObject.getClass()(which bothtypeandclassdo).In the
Compiler.getMatchingParams(), the Clojure compiler attempts to resolve which constructor ofBigDecimalto use. However, you have explicitly specified that your parameter has the typeLong– there is no constructor for BigDecimal that takes that type.Maybe this isn’t “common sense,” but Rich Hickey made the decision that you need to be precise about the type of your parameters and that they have to match the type of the Java class. The compiler is refusing to guess your intent.
Note the following:
Also note that this Java code is valid and resolves to the
intconstructor for BigDecimal:But this code also fails (even though it “should” use the
intconstructor):tl;dr: Clojure supports Java interop but that does not mean it has to follow the promotion rules of Java Language Specification.
What about
cast?A comment below asks about
(cast). In that case you are explicitly telling the Clojure compiler to delegate type resolution to the JVM. Note the following (nonsensical) code that compiles, yet fails at runtime:Epilogue II
There has been quite a bit of discussion on this topic in the Clojure community. Please check out these detailed threads:
Enhanced Primitive Support (Rich Hickey)
Clojure 1.3 treatment of integers and longs (Nathan Marz)