The purely functional equivalent of weakhashmap?

Weak hash tables, such as a weak Java hash map, use weak references to track the collection of inaccessible keys by the garbage collector and remove the bindings with this key from the collection. Weak hash tables are typically used to implement routes from one vertex or edge in a graph to another, as they allow the garbage collector to collect unreachable portions of the graph.

Is there a purely functional equivalent to this data structure? If not, how can one be created?

This seems like an interesting challenge. An internal implementation cannot be clean, because it must collect (i.e. mutate) the data structure in order to remove unreachable parts, but I believe that it can provide a clean interface for a user who could never observe impurities, as they affect only on a piece of data is a structure that the user, by definition, can no longer achieve.

+4
source share
2 answers

This is an interesting concept. One of the main complications in a “purely functional” setting would be that the identity of the object is usually not observed in a “purely functional” sense. I.E., if I copy an object or create a new identical one, in Java he expected the clone to be not the original. But in the functional setting, it is expected that the new one will be semantically identical to the old one, although the garbage collector will treat it differently.

So, if we allow the identification of the object to be part of the semantics, it will sound, otherwise, probably not. In the latter case, even if the hack could be found (I was thinking about one described below), you will probably have a language implementation fighting you everywhere, because it is going to do all kinds of things to use this fact that the identification of the object should not be observable.

One “hack” that appeared in my head would be to use values ​​unique in construction as keys, so in most cases the value of equality will coincide with the reference equality. For example, I have a library that I personally use in Haskell with the following in my interface:

data Uniq s getUniq :: IO (Uniq RealWorld) instance Eq (Uniq s) instance Ord (Uniq s) 

The hash map, as you described, will most likely work with them as a key, but even here I can think about how it can break: suppose the user stores the key in a strict field of some data structure, and the compiler is optimized. Optimization " unbox-strict-fields ". If "Uniq" is just a newtype wrapper for an integer of a machine, there can no longer be an object to which the GC can point and say "what is the key"; therefore, when the user goes and unpacks his key to use it, the card may already have forgotten about it. (Edit: this specific example can obviously be bypassed, make the Uniq implementation something that cannot be unpacked as such: the fact is that it is complicated, because the compiler is trying to be useful in many ways, to expect)

TL DR: I would not say that it is impossible to do this, but I suspect that in many cases the “optimization” will either break or break into a weak implementation of the hash map, unless the identity of the object is provided with a first-class observable status.

+2
source

Purely functional data structures cannot change from the user's point of view. So, if I get the key from the hash card, wait and then get the same key again, I need to get the same value. I can hold the keys, so they cannot disappear.

The only way this can work is if the API gives me the next generation and the values ​​are not collected until all references to past versions of the container are released. It is expected that users of the data structure will periodically request new generations to release weakly held values.

EDIT (based on comment): I understand the behavior you want, but you cannot pass this test using a map that returns objects:

 FunctionalWeakHashMap map = new FunctionalWeakHashMap(); { // make scope to make o have no references Object o = new SomeObject(); map["key"] = o; } // at this point I lose all references to o, and the reference is weak // wait as much time as you think it takes for that weak reference to collect, // force it, etc Assert.isNotNull(map["key"]); // this must be true or map is not persistent 

I suggest that this test can pass

 FunctionalWeakHashMap map = new FunctionalWeakHashMap(); { // make scope to make o have no references Object o = new SomeObject(); map["key"] = o; } // at this point I lose all references to o, and the reference is weak in the map // wait as much time as you think it takes for that weak reference to collect, // force it, etc map = map.nextGen(); Assert.isNull(map["key"]); 
0
source

Source: https://habr.com/ru/post/1335837/


All Articles