Im am still busy converting my old PCB rubberband router from Ruby to PCB. While getting it to compile was already some work, getting it to really work is a task for which I can not estimate the effort still...
One larger issue are the tables -- I can not remember how they work in Ruby, and I still do not really know how they work in Nim :-)
import tables
type
V0 = ref object of RootRef
i: int
var h: Table[V0, int]
var v1 = V0(i: 7)
var v2 = V0(i: 7)
echo v1 == v1 # true
echo v1 == v2 #false
echo (v1, v1) == (v1, v1) # true
echo (v1, v2) == (v2, v1) #false
h[v1] = 14
echo h[v1]
I think == for references compares addresses of instances only by default, not the content of the fields. Fine. But then, why do the last two lines does not compile:
/home/salewski/Nim/lib/pure/collections/hashcommon.nim(64, 12) Error: type mismatch: got <V0>
but expected one of:
proc hash(sBuf: string; sPos, ePos: int): Hash
When == is defined for references by default, why would a user has to define his own hash proc for references? Of course I know how to define needed hash proc, I did it for the router code already. For value objects it is a different story, comparing field content is difficult, so it makes sense that user hash to define his own == and hash().
And no, not a April joke this time, miram.
Well a table is what is called a hash map. Essentially it has an array internally with N spots, then it hashes all the keys you want to put in and inserts the element (along with the hash or the key) into the array at position hash mod N. When you do a lookup the key is hashed again, you find where an item of that hash would live, and then check the stored hash/key if it is indeed the right element before returning. This explanation obviously glosses over what happens if there is already an item there.
But that is essentially the reason why you need a hash proc for your keys. Not sure if adding a hashing proc for arbitrary reference objects have been discussed, but it might just be that it's more complex than doing a simple reference equality check and maybe you didn't even want to use all the fields of an object for the hash.
Sorry, I was not really asking what a hash map it. But thanks for the short summary.
The core point is. A references can be seen as a managed pointer, which can be seen as an address, which is basically an integer value, with some restrictions maybe. So when hash() proc is predefined for integers, why not for references. why do I have to type myself
proc hash(v: V0): Hash =
var h: Hash = 0
h = h !& addr(v[]).hash
result = !$h
copied from manual. Or in other words: May for special references a more clever hash proc would make sense? If yes, then of course there is a reason do define it myself again and again.
Not really related to my initial question, but Nim hashes works exactly like I assumed:
import tables, hashes
type
V0 = ref object of RootRef
i: int
proc hash(v: V0): Hash =
var h: Hash = 0
h = h !& addr(v[]).hash
result = !$h
var h: Table[(V0, V0), int]
var v1 = V0(i: 7)
var v2 = V0(i: 7)
h[(v1, v2)] = 14
h[(v2, v1)] = 41
v1.i += 1 # modify a field
echo h[(v1, v2)]
echo h[(v2, v1)]
./t
14
41
Modifying field contents does have no effect on table as stored values are references, and I even do not have to define one more hash proc for the tuple.
So my current router app issue must have different cause...
No. In my initial post I explained that default equality operator == for references use the address of the instances, not the fields content, if we use == for two references.
So it should be expected that for hashes of references, the content does not matter by default!