Hacker News new | past | comments | ask | show | jobs | submit login

> What wrong with that?

If you mean, "What is wrong with anonymous classes?", I have no answer. I do not mind the idea, so long as I can maintain the integrity of my data's interfaces.

If you mean, "What is wrong with implementing anonymous classes as hash maps?", then I point you towards issues of efficiency in memory and speed. Hash maps are intended to make key look-up quick for a variable set of not 100% known keys. If all you want is known key-value pairs, then you have some options that do the trick much more efficiently (examples --):

- structs (key is compiled out to be a quick pointer addition)

- classes (on top of the struct, you now have some abstraction capabilities that will help out future maintenance issues)

If you do not quite know what you want in the structure, but you have a small number of keys, an associative array with a decent accessor API is all you need. The few key comparisons will generally be faster than running them through a typical hashing algorithm.

When you provide a construct like this:

    { :foo 1  :bar 2 }
you are implying that you have a known set of keys and values. With a hash map, I would expect that someone would later add more key-value pairs, but in practice, code that starts with this initialization pattern treats it like a class rather than a hash, just modifying the values of the :foo and :bar attributes.

I have also seen instances where developers would -- because it was easy to do with these hash maps -- slap in another key-value pair in some subfunction and then suffer interface issues down the road when they assume its presence in the wrong places. Something a little stricter would have at least given them a warning.

So just make it a struct -- anonymous if you need -- at minimum. That is the most generic, all-purpose form that could benefit from this kind of {} syntax.

> which by the way manages efficiency concerns on your behalf

Can you elaborate on this? If this means that there is a magical conversion to a stricter struct underneath the hood, then I have no place to complain about {} in Clojure's case. I have not encountered this notion before (other than as faith).




Clojure has that.

Clojure is built on abstractions. Most operations in clojure source code convert to calls to java interfaces. For example, a simple function call (map foo bar) compiles to a call to .invoke() method on map, a java object that implements clojure.lang.IFn. Similarly, all of clojure's datastructure operations boil down to calls to objects implementing the java interfaces clojure.lang.IPersistentCollection, c.l.IPersistentMap, c.l.IPersistentSet, etc.

To add a new datastructure, simply create a new class that implements the appropriate interfaces, and you'll get literal syntax support.

To answer your question, Clojure records are named classes that implement IPersistentMap so they're a drop-in replacement for "normal" maps, that also implement an additional interface so the compiler can generate java field lookups to access items without the cost of a hash lookup.

For example

    (defn foo [map]
       (println (:a map)))

    (defrecord MyObj [a])

    (foo {:a 1})
    (foo (MyObj. 1))
Both calls work exactly the same, except the MyObj doesn't have hash lookups for the fields defined in the class.


Clojure records are named classes that implement IPersistentMap so they're a drop-in replacement for "normal" maps, that also implement an additional interface so the compiler can generate java field lookups to access items without the cost of a hash lookup.

Awesome! Thank you for that. I saw people referring to it as a hash when you point out it is not, so I was confused. I sit corrected as well as happier with the Clojure design.


>> which by the way manages efficiency concerns on your behalf

> Can you elaborate on this?

Sure thing. As a start the form `{:a 1, :b 2}` would start as an array-map because it's really fast to populate while the access efficiency is reasonable. At some point during the use of that map it will cross some critical threshold and become a hash-map providing (effectively) constant time lookup while still maintaining persistence. Additionally, behind the scenes Clojure uses mutable versions of the map for efficiency while again maintaining persistence.

Clojure provides different types of maps for use in different scenarios, but manages implementation efficiencies for you.




Consider applying for YC's W25 batch! Applications are open till Nov 12.

Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: