--
You received this message because you are subscribed to the Google
Groups "Clojure" group.
To post to this group, send email to clo...@googlegroups.com
Note that posts from new members are moderated - please be patient with your first post.
To unsubscribe from this group, send email to
clojure+u...@googlegroups.com
For more options, visit this group at
http://groups.google.com/group/clojure?hl=en
I think you'll find that clojure doesn't change types, except where
required, mostly for boxing. Clojure 1.2 would construct a new Integer
around an int when required. Clojure 1.3 constructs a new Long around
an int instead, because rich has decided he prefers longs and doubles
to ints and floats. If you want to do your own boxing prior to using a
value in a way that would box it, you can, and your type will not
"change"
user> (def boxed-by-clojure (.intValue 3))
#'user/boxed-by-clojure
user> (type boxed-by-clojure)
java.lang.Long
user> (def boxed-by-me (Integer. (.intValue 3)))
#'user/boxed-by-me
user> (type boxed-by-me)
java.lang.Integer
user>
--
And what is good, Phaedrus,
And what is not good—
Need we ask anyone to tell us these things?
Thanks. I read through that and it didn't quite answer my question. To
me it seems more logical that:
1. Clojure defaults to longs when you create a new number (with a
literal 0, 1, etc)
2. You can create ints by doing (int 0)
3. Clojure never changes the types of things you're using
I find Clojure's behavior of changing the types of primitive ints to
longs highly unusual, and it is causing a lot of pain in upgrading
Storm to 1.3.
Existing Integer objects are unchanged. If the method call in your
example did return an Integer object then you would never notice
anything.
> types on you isn't worth it IMO.
>
> Here's my proposal for what I think would be better behavior:
>
> 1. Clojure boxes ints into Integers rather than convert them into
> longs
clojure does not convert ints into longs, you are putting a primitive
into a collection, which requires boxing, clojure 1.3 boxes ints as
Longs. If you put (Integer. …) around your call you will be fine.
> 2. If you wrap the form in "(long ...)", Clojure skips the boxing and
> does what it does now. Since this is done explicitly, there's no
> confusion about types.
>
> -Nathan
>
>
> On Oct 19, 7:38 am, Stuart Halloway <stuart.hallo...@gmail.com> wrote:
>> > Thanks. I read through that and it didn't quite answer my question. To
>> > me it seems more logical that:
>>
>> > 1. Clojure defaults to longs when you create a new number (with a
>> > literal 0, 1, etc)
>> > 2. You can create ints by doing (int 0)
>> > 3. Clojure never changes the types of things you're using
>>
>> > I find Clojure's behavior of changing the types of primitive ints to
>> > longs highly unusual, and it is causing a lot of pain in upgrading
>> > Storm to 1.3.
>>
>> Integers and longs are going to be painful no matter what because they are broken in Java, e.g.
>>
>> Object[] objects = new Object[] {-1, -1L};
>> System.out.println(objects[0].hashCode());
>> System.out.println(objects[1].hashCode());
>>
>> Clojure avoids this pit by standardizing on longs, which leaves you with the need to specifically request ints when you need them for interop. You can use (int n) hints to select the correct interop method invocation, or box an int if you want to hold on to a value guaranteed to be int.
>>
>> Can you post a code example that shows a problem you are having?
>>
>> Stu
>>
>> Stuart Halloway
>> Clojure/corehttp://clojure.com
>
> --
> You received this message because you are subscribed to the Google
> Groups "Clojure" group.
> To post to this group, send email to clo...@googlegroups.com
> Note that posts from new members are moderated - please be patient with your first post.
> To unsubscribe from this group, send email to
> clojure+u...@googlegroups.com
> For more options, visit this group at
> http://groups.google.com/group/clojure?hl=en
--
Somebody has to work hard: either users of collections, or interop callers. The current behavior makes things "just work" for collections, at the cost of having to be explicit for some interop scenarios.
There are two reasons to favor collection users over interop users:
(1) Interop problems are local, and can be resolved by checking the type signature at the point of the problem. Collection key problems are global and break the composability of collections. It is a *huge* benefit of Clojure that collections are sane.
(2) There are a lot more lines of code working with collections than doing interop.
Stu
Interop is environment specific and should not be driving the Clojure language design.
Otherwise Clojure generics would have to "bend" to Java, CLR, JS and future implementations in
other environments, loosing its identity along the way and creating a Babel tower.
Luc P.
--
Luc P.
================
The rabid Muppet
class is a clojure function that takes Objects, so the int must be boxed.
But Clojure is already inconsistent. ints and Integers in interop are
treated differently. The only way to make Clojure consistent is to
either:
as David said "Clojure now only has 64bit primitives".
an Integer is not a primitive, an int is.
--
My understanding is that Clojure 1.3 has 64-bit primitives, i.e.,
longs and double. You only have a primitive int if you coerce the
value to int (for an interop call that expects an int) - based on what
I've understood of the numerics discussions. Similarly, you only have
a primitive float if you coerce the value.
So Clojure boxes a long as Long. If you want to box a long as Integer,
you have to explicitly say so: (Integer. 42) - and Clojure will give
you an Integer and not do anything to it.
(Is my understanding correct? I'm finding the discussion interesting
but not 100% sure whether I fully understand Clojure 1.3's primitive
numerics)
--
Sean A Corfield -- (904) 302-SEAN
An Architect's View -- http://corfield.org/
World Singles, LLC. -- http://worldsingles.com/
Railo Technologies, Inc. -- http://www.getrailo.com/
"Perfection is the enemy of the good."
-- Gustave Flaubert, French realist novelist (1821-1880)
user=> (time (dotimes [i 10000000] (let [ a (Integer. 1) b (Integer. 2)] (+ a b))))
"Elapsed time: 31.14886 msecs"
nil
Instead of this:
user=> (time (dotimes [i 10000000] (let [ a 1 b 2] (+ a b))))
"Elapsed time: 15.680386 msecs"
nil
Using a wrapper instead of a primitive type as a significant cost in a computation.
One of the purpose of normalizing to 64 bits was to get maximum performance for
compute bound Clojure applications.
Computing with wrappers is inefficient. Your proposal looks only at one facet
of the whole problem.
It's not a Java centric issue, it's a Clojure performance enhancement.
You are coding in Clojure, not in Java. It happens that Clojure reuses some native types
efficiently implemented by the JVM and used by Java (String, long, ....) but not all of them.
Let's say one day you end up coding in ClojureScript or Clojure on JS, what do you prefer ?
Deal with idiosyncrasies of the underlying environment or have a consistent implementation that provides
the best performance for that given pseudo-metal ?
What about the day that long long (128 bits) comes around ? Clojure will drag behind because it's carrying
32 bit values ?
Obviously it creates issues when you work at the fringe but interop is not the purpose of
Clojure. It happens to be much more easier to access the "outside" world than in other environments but it
cannot justify to compromise the performance or feature list of Clojure.
Luc P.
--
I'm not sure we're arguing about the same thing. I think that Clojure
only supporting 64 bit primitive arithmetic is fine, and I'm not
proposing that it support 32 bit primitive arithmetic. The sole point
of contention is what Clojure does when it has to box a primitive int.
I think this is orthogonal to primitive args/return, but correct me if
I'm wrong.
user=> (class (Integer/parseInt "1"))
java.lang.Long
user=>
Integer/parseInt returns a primitive type. Not a boxed Integer object.
If used as a key in a map or anything else in Clojure, it will get promoted to a long value as per the math
promotion rules (long/double representation). Obviously needed if it is to be used later in a computation
otherwise it would break math operations consistency by allowing mixed int/long operands.
If passed as an interop parameter it will retain it's int type.
user=> (class (Integer/valueOf 1))
java.lang.Integer
Integer/valueOf returns an Integer object, not a primitive type.
It's an object, not a primitive type, Clojure will not change it.
If used as a key in a Clojure map or any Clojure data structure, it will retain its object status.
Just cast your keys accordingly if you want Integer objects as keys.
In your short example, 1 as a key will not do it, it gets promoted to primitive long.
You may not recall but in Java, int used not to be compatible with Integer objects.
It's only since java 5 that you can assign an Integer object to a primitive int.
That's the compiler tricking things to allow you to do that. In the JVM there's still
not represented in the same way.
The above Integer member functions and their behavior have nothing to do with Clojure.
They result from bad decisions made years ago when designing Java and the JVM and you are blaming
Clojure for not handling them according to some patch implemented afterward in the Java compiler.
You ran in the ClassCast exception by yourself. Clojure did not push you into it.
When using Java interop you have to obey to Java rules and bend accordingly.
It's not Clojure that needs to bend, it's you to adapt to the interop
restrictions/conventions.
If Java expects an Integer object somewhere make sure you are providing it.
Luc P.
--
(Integer/parseInt "1") returns an int - which Clojure promotes to long
(since it only has 64-bit primitives) and class takes an Object so
long is boxed to Long.
> user=> (class (Integer/valueOf "1"))
> java.lang.Integer
(Integer/valueOf "1") returns an Integer - which is already an Object
so (class Integer-value) returns Integer.
You're only going to get 32-bit ints if you are calling into Java and
that API expects an int - and you coerce the (64-bit primitive long)
Clojure primitive to a 32-bit int (again, as I understand the 1.3
numerics).
If Java gives Clojure an int, it will be treated as a 64-bit long. If
Java gives Clojure an Integer, it will be treated as an Object. I
rather like the simplicity of 1.3's numeric handling: there are only
longs and doubles - but you can coerce them to whatever you need for
interop. It's performant by default, it's simple and consistent (in my
eyes) and yet still flexible.
If you box Ints and Longs separately then collection keys stop working, as I said at the start of this thread.
There is no such thing as intuitive. The behavior above follows from a clear rule that can be followed locally in all cases. What you propose, in addition to breaking collections, requires more contextual reasoning to understand code in a bunch of scenarios.
Could we meet on IRC and discuss this later today? Feel like we are going in circles.
Stu
"Concrete code examples" cannot be a replacement for consistent rules when
designing software and especially a prog. language.
Since the beginning of this thread, you have been exposed to two of these:
a) make collections consistent
b) make computations efficient in Clojure
These are 2 major reasons why promoting to long is rational. No need for code snippets.
"Intuitive" is relative, it's not an objective criteria. What may seem
intuitive to you can be counter intuitive to others. It all depends on the background
of individuals.
If you know the rules relative to numerics in Clojure and why they have been
chosen, then its perfectly rational.
Read carefully:
http://dev.clojure.org/display/doc/Documentation+for+1.3+Numerics
They is nothing else to say about this subject.
Luc
If Clojure's primary objective were Java interop, I might agree with you. However, it's not, and it's bizarre to see someone argue that this is not broken:user=> (.equals (Integer. -1) (Long. -1))
false
Sure, not broken according to the Java object model and its equality semantics, but damn well broken if your standard is something other than what was canonized in java.lang.Object 20 years ago. 1 == 1 all the time — or, it should — regardless of the containers such integers incidentally find themselves within.
Thus, Clojure's notion of equivalence, which leads to pleasantly consistent behaviour, e.g. (== (Integer. 1) 1 1N). Which, of course, doesn't preclude one using .equals if you truly want Java math semantics instead of = or == (neither of which have ever been advertised as adhering to the hashcode/.equals contract, at least since Clojure 1.0 IIRC).
If there are some common rough spots in the interop for certain use cases, perhaps those can be smoothed out with a library, maybe contributed by someone that acutely feels that pain.
That's a too common mistake. Integer vs Long, Byte, ... are Java creations.
They have nothing to do with the JVM primitive data types.
Clojure implements a semantic different than Java on top of the JVM, why not ?
That's the whole idea of having the JVM around. Abstracting the metal.
Clojure reuses Java strings as is but it could have implemented its own on top of the
char primitive type at the expense of less transparent interop. This is an implementation choice.
It does not tie Clojure to Java.
These are Clojure centric decisions. Lets get out Java out of this discussion.
Clojure is not Java and even if it provides a "soft" bridge
to reuse Java code, its feature set is certainly not Java centric.
A Clojure persistent map is a ... Clojure data structure, not a Java data structure.
Interfaces like java.util.Map have nothing to do with the content of the map itself.
If they help make interop calls smoother fine. But do not tie their Java semantic to
the Clojure semantic. It's unrelated outside of the interop domain.
I do not care about Java centric stuff. I adopted Clojure to get away from Java
ASAP.
Luc P.
--
Java != JVM.
That's a too common mistake. Integer vs Long, Byte, ... are Java creations.
They have nothing to do with the JVM primitive data types.
Clojure implements a semantic different than Java on top of the JVM, why not ?
That's the whole idea of having the JVM around. Abstracting the metal.
Clojure reuses Java strings as is but it could have implemented its own on top of the
char primitive type at the expense of less transparent interop. This is an implementation choice.
It does not tie Clojure to Java.
These are Clojure centric decisions. Lets get out Java out of this discussion.
Clojure is not Java and even if it provides a "soft" bridge
to reuse Java code, its feature set is certainly not Java centric.
A Clojure persistent map is a ... Clojure data structure, not a Java data structure.
Interfaces like java.util.Map have nothing to do with the content of the map itself.
If they help make interop calls smoother fine. But do not tie their Java semantic to
the Clojure semantic. It's unrelated outside of the interop domain.
I do not care about Java centric stuff. I adopted Clojure to get away from Java
ASAP.
b) The way Java interprets equality based on the hash code is a Java specific behavior.
It's defined by the Java API.
There's nothing in the jvm spec that defines how a hash code should be used. It's a reference.
Nothing more.
All the contracts you mention are language centric, each of them defined their contract according
to their own needs. Clojure should have the right to do so.
Clojure prefers to avoid having different rules than Java regarding number handling ? Be so, it's legitimate.
If people like you were free to take these decisions we would end up with three different languages, one on the
jvm, one on CLR and one on JS. Nonsense. Having to deal with three different interops and trying to
unify them a bit is enough work by itself.
Interop stuff is low level and should remain there. If a single interop implementation starts to influence
the Clojure language and result in such short term and narrow scope decisions, we will all have a problem
in the future.
Luc P.
--
All the contracts you mention are language centric, each of them defined their contract according
to their own needs. Clojure should have the right to do so.
Clojure prefers to avoid having different rules than Java regarding number handling ? Be so, it's legitimate.
If people like you were free to take these decisions we would end up with three different languages, one on the
jvm, one on CLR and one on JS. Nonsense. Having to deal with three different interops and trying to
unify them a bit is enough work by itself.
Interop stuff is low level and should remain there. If a single interop implementation starts to influence
the Clojure language and result in such short term and narrow scope decisions, we will all have a problem
in the future.
It's not a platform issue, it's a Java issue. Equality is implemented by Java for objects.
It has nothing to do with Clojure which uses primitive types like long for numeric representation.
user=> (= 1 (int 1))
true
user=> (.equals 1 (int 1))
true
user=> (.equals (long 1) (int 1))
true
user=> (.equals (Long. "1") (Integer. "1"))
false
Where's the contract breach here ? Don't mention breaking the Java contract in the two first
examples. You are in Clojure's playground here, arithmetic primitive types (not classes) are getting promoted
according to Clojure's rules.
The third one starts in Clojure's playground were the same promotion occurs and
both values get boxed to Long for interop purposes (.equals on java objects) and the result
satisfies the Java contract.
The fourth one is not in Clojure's playground, it's pure Java and it also respects the Java contract
between comparing objects, not primitive jvm types.
Were's the problem ? The Java contract is respected as soon as you dive in Java.
It's the same here:
user=> (class (key (first { (int 1) :a})))
java.lang.Long
You create a Clojure map with a numeric key, the key gets promoted from primitive int to primitive long.
As soon as you jump in interop, you get a Long object key. Obviously, the map comes from the Clojure's
playground but java cannot cope with primitive types in keys, you need an object so Clojure boxes
accordingly to a Long object.
user=> (class (key (first { (Integer. 1) :a})))
java.lang.Integer
user=>
Here you decide to create a key type from the Java world (a Java object) and it gets preserved so you
ship the map to a Java call for interop.
Were are the contract breaches ? Aside from the fact that Clojure implements a contract of its own
and respects it, there are no breaches.
You have been mixing Java objects with primitive types defined by the JVM since you entered this
discussion. It's two different things.
--
Where's the contract breach here ?
user=> (def a (hash-map -1 :a))
#'user/a
user=> (def b (array-map -1 :a))
#'user/b
user=> (= a b)
true
user=> (= (key (first a)) (key (first b)) -1)
true
I said to myself, "Ok he's been throwing some ints in there":
user=> (def a (hash-map (int -1) :a))
#'user/a
user=> (def b (array-map -1 :a))
#'user/b
user=> (= a b)
true
user=> (= (key (first a)) (key (first b)) -1)
true
Still ok.
Now if you have been using Integer and Long objects as key, of course maps will not match.
You are using Java objects as keys, not primitive types. You're not in Clojure's playground
anymore, half of your map is in Java's own sandbox.
What's missing from your shortened example ?
--
Clojure defines equiv separately from dot-equals. dot-equals respects Java's rules.
> To be honest, I've sort-of lost the plot of how this is related to the boxing-ints-as-Longs issue, but that's probably due to both my lack of expertise in this area and to the generous glass of whiskey I had while watching Megamind with my kids this afternoon. But I digress. The point I think I was trying to back up is "if clojure changes equality semantics, it should change hashcodes to match". That sounds right to me.
Mmm, whiskey.
I am dropping off this thread now. At this point I think it would be more useful for me (or someone) to expand the notes about numerics into better documentation, rather than continuing this rambling point-by-point treatment without getting all of the considerations into play at once. I hope to get that done by conj.
Stu
What's missing from your shortened example ?
Is this the only known case ?
Luc
--
Ha ! Ok, I missed the digression here and I now understand the issue.
Considering that a PersistentArrayMap may eventually become a PersistentHashMap
this opens the door to *funny* bugs.
Is this the only known case ?
I am dropping off this thread now. At this point I think it would be more useful for me (or someone) to expand the notes about numerics into better documentation, rather than continuing this rambling point-by-point treatment without getting all of the considerations into play at once. I hope to get that done by conj.
CON2, CON3 and CON4 - No way, the current design choice is the good one.
So many languages have been plagued with numbers of different sizes/formats for ints and floating point values,
it's not a direction that Clojure should follow.
These distinct types are source of many problems (overflow handling, precision problems, ...).
The need for Clojure to support these things is similar to calling assembler
from C. You matter about bytes, shorts and similar things at the frontier,
when it's time to call a low level service, you need to be able to pass
these values.
By no means this implies that you have to support them in your language runtime.
It complects (;) everything including computations and makes your runtime much more harder to port.
It's an interop centric thing and interop is by essence not portable.
It does not belong to the core of Clojure. It's better to rely on cast operators
to call interop than to expect Clojure to box numeric values according to some interop
convention that may vary according to the platform Clojure runs on.
Luc P.
--
--
You received this message because you are subscribed to the Google
Groups "Clojure" group.
To post to this group, send email to clo...@googlegroups.com
Note that posts from new members are moderated - please be patient with your first post.
To unsubscribe from this group, send email to
clojure+u...@googlegroups.com
For more options, visit this group at
http://groups.google.com/group/clojure?hl=en
On Sun, 23 Oct 2011 20:31:51 +0200
Ivan Koblik <ivank...@gmail.com> wrote:
> Hello Luc,
>
> In all fairness I don't see how converting ints to Integers returned
> by class methods would break the abstraction. If you start talking
> about portability of Clojure code, then Long is as portable as
> Integer is. (In general they are not.)
It's simpler to use one representation to port the core. You can choose the
fastest/efficient one. You do not have to carry all these intermediate types
with you.
The day a 128 bits primitive type become available, there's little changes to do to support
that. If you keep mixed types, that adds another one to the babel tower.
The problem is not to choose between ints or longs, it has to do with carrying
all these intermediate types. Frankly aside from interop, how many are using
short ints in Clojure ? That's a leftover from the PDP-11 era.
>
> Could you explain your position on the fact that shorts get converted
> to Short? Why is it not possible to do the same for ints?
This should disappear. I think all the small primitive types including ints
should be promoted to long except when doing an interop call.
Rich can explain why it's been kept. Maybe a question of priority/effort
or something else.
>
> I don't think that there was anyone in this thread that would suggest
> keeping 32bit math in Clojure. For what it's worth, Integer can be
> converted to Long first time it is used in any computation.
>
That is unnecessary overhead, again lets split boxed values from primitive types.
If you compute in Clojure, keeping primitive ints/shorts/bytes around has no value.
You end up having type conversion to do depending on what is specified in the
expression.
When doing an interop call, this is when you need to be specific. Elsewhere
I see no value in keeping this scheme.
This way of thinking about primitive types has been sticking around for at least
35 years carrying 64/32/16/8 bit unsigned/signed int values. Maybe it's time we toss this away.
I have been writing a couple of hundred thousand lines of assembly code in my
professional life and I understand this model. Of course when you deal with
hardware in a device driver you need these things, but in Clojure ?
And with today's hardware, why stick with these data types ? To reduce memory footprint ?
Ha ! Ha !, I used to work on computers with 256K of physical memory.
This concern was legitimate in this prehistoric era. But today ?
If you need bit manipulation in Clojure, better write a lib for this than mangling with
these data types.
It's simpler to use one representation to port the core. You can choose the
fastest/efficient one. You do not have to carry all these intermediate types
with you.
When doing an interop call, this is when you need to be specific. Elsewhere
I see no value in keeping this scheme.
And with today's hardware, why stick with these data types ? To reduce memory footprint ?
Ha ! Ha !, I used to work on computers with 256K of physical memory.
This concern was legitimate in this prehistoric era. But today ?
If you need bit manipulation in Clojure, better write a lib for this than mangling with
these data types.
This reply is to the thread, not Luc specifically.
Thanks everyone for your feedback and input.
I have pushed 3 commits:
1) Fixes the inconsistency between the hash function used by Clojure maps (was .hashCode) and =. Thanks Paul for the report.
2) Changes core/hash to return the result of this hashing function. Thus, it returns a different value than does .hashCode for Integers, Shorts, Bytes and Clojure collections. Feedback welcome.
3) Only due to the first fix, it now becomes possible to box ints to Integers without much grief. This commit implements that for evaluation purposes, and is not a commitment to that policy. Note well that while in the first commit the answer is clear, on this point there is always going to be a tradeoff and there is no 'right' answer.
Here are the issues as I see them:
First, note there is no 'following' of Java semantics as an objective. Java semantics are that Integers are never equal to Longs, and I presume no one wants to go back to that.
Second, boxing is a change of type, period. There is no valid complaint that 'you changed my type'. int != Integer either.
Third, there are 2 scenarios in consuming things you box in Clojure from Java:
a) You control the Java. In this case, having Clojure make everything uniform (Longs) make things easier for you. There is no heterogeneousness regardless of the source or manipulation of numbers, and can always expect Longs.
b) You don't control the Java. In this case you must match consuming expectations i.e. conforming to Java promotion, types of generics etc. ***This will *always* require vigilance and explicitness due to arithmetic conversions etc***. Auto promotion is only one part. Note that this is true in Java as well - while type checker may scold you, you still have to cast/coerce on mismatch.
Even with the auto box change, you are only an arithmetic operation away from having the problem again. For instance in the original report, wrapping .getValue with dec generates an interop mismatch again:
(let [amap {1 (dec (.getValue obj))}] …)
There is no way we are going to 'fix' that by adopting Java's numeric tower, which is dangerous and requires static types. The bottom line is specific type requirements on the Java side require explicit boxing on order to have correct and non-brittle code.
The final consideration is collection equality. When Clojure autoboxes to Longs, you get homogeneous collection contents, and thus .equals is still true for the collection on the Java side, vs random - 'depends on where I got the contents from and what I did with them'.
FYI - there are the RT/box functions that box as per Java. These could be exposed in Clojure.
-----
In short, having autoboxing match Java does not really free you from your responsibility to create specific boxed types when you need them on the Java side. I.e., Clojure can't help you.
On the flip side, when you are in charge of the Java code, Clojure's being more consistent makes things more consistent on the other side and *does* give you less to do to make sure things work.
I prefer what we had (auto box to Longs), but I think it matters a somewhat less now with = consistent hashing. If we decide to revert to that we can discuss making auto boxing of short and byte consistent.
-----
In any case, those of you who still know how to use Clojure from Git can try these commits, and please provide feedback as to its actual effects on actual code. I think the opinion phase of this is now over :)
Thanks again for the feedback,
Rich
1) https://github.com/clojure/clojure/commit/b5f5ba2e15dc2f20e14e05141f7de7c6a3d91179
2) https://github.com/clojure/clojure/commit/b4a2216d78173bb81597f267b6025c74a508bd03
3) https://github.com/clojure/clojure/commit/a2e4d1b4eaa6dad26a1a96b9e9af129cccca9d10
Rich
and I imagine you can do something similar with maven, the main thing
is you need to add the sonatype snapshot repo.
but you can't access individual commits because the build machine
polls and gathers the latest commits together and does a build.
and the readme.txt has build instructions for ant and maven for those
who don't know how to build clojure from git.
--
And what is good, Phaedrus,
And what is not good—
Need we ask anyone to tell us these things?
Hi all,This reply is to the thread, not Luc specifically.
Thanks everyone for your feedback and input.
I have pushed 3 commits:
1) Fixes the inconsistency between the hash function used by Clojure maps (was .hashCode) and =. Thanks Paul for the report.
2) Changes core/hash to return the result of this hashing function. Thus, it returns a different value than does .hashCode for Integers, Shorts, Bytes and Clojure collections. Feedback welcome.
3) Only due to the first fix, it now becomes possible to box ints to Integers without much grief. This commit implements that for evaluation purposes, and is not a commitment to that policy. Note well that while in the first commit the answer is clear, on this point there is always going to be a tradeoff and there is no 'right' answer.
Here are the issues as I see them:
First, note there is no 'following' of Java semantics as an objective. Java semantics are that Integers are never equal to Longs, and I presume no one wants to go back to that.
Second, boxing is a change of type, period. There is no valid complaint that 'you changed my type'. int != Integer either.
Third, there are 2 scenarios in consuming things you box in Clojure from Java:
a) You control the Java. In this case, having Clojure make everything uniform (Longs) make things easier for you. There is no heterogeneousness regardless of the source or manipulation of numbers, and can always expect Longs.
b) You don't control the Java. In this case you must match consuming expectations i.e. conforming to Java promotion, types of generics etc. ***This will *always* require vigilance and explicitness due to arithmetic conversions etc***. Auto promotion is only one part. Note that this is true in Java as well - while type checker may scold you, you still have to cast/coerce on mismatch.
Even with the auto box change, you are only an arithmetic operation away from having the problem again. For instance in the original report, wrapping .getValue with dec generates an interop mismatch again:(let [amap {1 (dec (.getValue obj))}] …)
There is no way we are going to 'fix' that by adopting Java's numeric tower, which is dangerous and requires static types. The bottom line is specific type requirements on the Java side require explicit boxing on order to have correct and non-brittle code.
The final consideration is collection equality. When Clojure autoboxes to Longs, you get homogeneous collection contents, and thus .equals is still true for the collection on the Java side, vs random - 'depends on where I got the contents from and what I did with them'.
FYI - there are the RT/box functions that box as per Java. These could be exposed in Clojure.
-----
In short, having autoboxing match Java does not really free you from your responsibility to create specific boxed types when you need them on the Java side. I.e., Clojure can't help you.On the flip side, when you are in charge of the Java code, Clojure's being more consistent makes things more consistent on the other side and *does* give you less to do to make sure things work.
I prefer what we had (auto box to Longs), but I think it matters a somewhat less now with = consistent hashing. If we decide to revert to that we can discuss making auto boxing of short and byte consistent.
-----