I would say to stick with "reference types" and just explain what is
meant by the term. I have a feeling that introducing new, undefined
terms will just confuse things more. In fact, it seems to me that both
Java and Clojure have a pretty similar definition for the term. More
or less, a reference type is a type whose value is a reference to a
data structure on the heap. I think that by taking a sentence or two
to point out the similarities and differences between the ways Java
and Clojure use the term, any confusion will be greatly reduced.
Regards,
- J.
Speaking of these, your article mentions and describes only Var, Ref and
Agent in your "Reference Types" section.
> ...
Randall Schulz
First off, let me say that I've started looking through what you've
written and I really like what I've read so far, so good job and
thanks!
WRT the "reference type" vs. "reference object" terms, yes, it's
unfortunate that the "reference type" term is overloaded. However, it
seems like it is overloaded for a good reason in this case. Var's and
Ref's are types in Clojure, they are not objects, so I'm not sure that
"reference object" is accurate and that is more confusing to me than
the overloading of the term. Especially when I would expect that
anyone coming to Clojure would be reading the official docs in
parallel with your intro. and the official docs refer to "reference
types", so not using the same terminology can confuse things.
Again, I really like where you are going with this and you have
explained a lot of stuff better than I could, so you may very well be
right that someone new to Clojure will be less likely to stumble over
"reference objects". I just wanted to point out that, at least to
some, the other term carries more meaning, even if it is overloaded.
Regards,
- J.
I don't think they are misnamed. Naming is hard, but Clojure does
decent job of it, and has lots of considered names. The real problem
is that there are only so many good names/words, and, if they have
been used before, people familiar with the old usage will have some
adjustment to do. 'Type allocated on the heap' is a semantic for
reference that doesn't deserve to stand forever.
All of the reference types can vary, and all are references in the
sense that they refer to something else, their value. Vars have an
historic tie-in to CL's dynamic variables, and when thread-locally
bound, have the closest semantics to ordinary variables in other
languages, supporting unmitigated set! Ref is short for transactional
reference, a mouthful, and tref, which wasn't any more immediately
meaningful than plain ref, and has a tie to ML's ref. Atoms and Agents
hint pretty well at their semantics, which still must be explained.
As far as the ease of learning, well, sometimes the easiest thing to
do is not discuss something until later, i.e. why talk about
thread-local binding and vars to a beginner?
The bottom line is that there won't be a one-to-one mapping for a
beginner since they are not likely to be coming from a language that
requires all mutation to be done via controlled locations, never mind
one with multiple semantics for such locations. It's simply going to
be something new to learn, with it's own nomenclature.
Ideally beginners should avoid all the reference types until they get
comfortable with FP.
Rich
> btw, you'll see a few notes I left in the text in square brackets
> where I wasn't sure on some point. If someone could address those
> questions, I'd appreciate it.
> [hmm, what are the chances of a false positive due to hash
> collision? are the odds astronomical and therefore acceptable?
A correct use of hash codes in equality testing would be only to
enable returning false (indicating not equal) quickly. Hash codes
cannot be used to ensure that objects are equal. If two objects have
the same hash code, they need to be checked in more detail to
determine whether or not they are also equal.
For the Java take on this, please see:
http://java.sun.com/j2se/1.5.0/docs/api/java/lang/Object.html#hashCode()
In particular:
The general contract of hashCode is:
* Whenever it is invoked on the same object more than once during
an execution of a Java application, the hashCode method must
consistently return the same integer, provided no information used in
equals comparisons on the object is modified. This integer need not
remain consistent from one execution of an application to another
execution of the same application.
* If two objects are equal according to the equals(Object)
method, then calling the hashCode method on each of the two objects
must produce the same integer result.
* It is not required that if two objects are unequal according to
the equals(java.lang.Object) method, then calling the hashCode method
on each of the two objects must produce distinct integer results.
However, the programmer should be aware that producing distinct
integer results for unequal objects may improve the performance of
hashtables.
The chances of a false positive on an equality test due to hash
collision is zero.
> and what about Java strings; is it simply a call to equals?]
A non-trivial call to clojure.core/= reduces to a series of one or
more calls to clojure.core/= with two arguments. The two argument case
is handled by clojure.lang.Util.equals. After a quick return of "true"
if the objects are identical and some check whether this is a
comparison involving nil or numbers (which get a bit of special
treatment), the general case eventually calls the equals method of the
first object with the second as its argument. If the first object is a
string, that does boil down to a call to the string's equals method.
--Steve
It is.
Furthermore, keywords have the fascinating property of being functions
over maps, effecting look-up of their entry, if any, in the map to
which they're applied. Symmetrically, and more conventionally, maps are
functions of keywords.
Both cute _and_ clever. And functional.
Randall Schulz