Re: Hashing a procedure object reliably

Maxime Devos <[email protected]> Sun, 13 Apr 2025 19:03:56 +0200
Newsgroups gmane.lisp.guile.user
Message-ID <[email protected]>
Spacing appears to have been messed up, so here's the same thing with 
extra newlines


On 13/04/2025 18:55, Maxime Devos via General Guile related discussions 
wrote:
>
> On 11/04/2025 15:49, Olivier Dion wrote:
>> My goal here is that I have GOOPS object. The object is used to produce
>> a pure result which I can store in a cache on the disk, given the hash
>> of the object.  I can then re-fetch the result on disk in another Guile
>> process if the hashes match.  As you can see in the above code, GOOPS
>> instances get hashed by folding over their slots, which can include
>> procedures.
>
> The usual solution to this kind of thing, is to:
>
> (1) hash procedures by pointer (not consistent across processes, not 
> applicable for your case) 



> (2) don't hash procedures



> (3) Allow classes to override how hashing is performed. Instead of 
> (define (hash value) [...]), you could have (define-method (hash 
> value) [...]). (Also be sure to define equality in a way such that 'a 
> = b -> hash(a)=hash(b)' - doesn't have to be '=' or 'equal?', but it 
> does need to be the equality procedure used for hash table things.) 
> (Also consider (hash value n) instead, so implementers have a clue for 
> reasonable output size.)



> (3)(a) if a field is a thunk merely as a way to do laziness (and 
> forcing the lazy isn't expected to be computationally expensive), 
> force the lazy and hash the result



> (3)(b) if the procedure field is irrelevant to the considered problem, 
> don't hash it (and adjust your expectations to make it not an element 
> of surprise)



> (3)(c) if there is a limited set of procedures it could be, do 
> something like hashing by name (specifics depend on specific situation)



> (3)(d) if the procedure field is relevant, and there is no apparent 
> way to hash something else instead, change this situation



> (4) if nothing appears applicable for the problem at hand, change the 
> problem.
>
> If you do go for bytecode hashing, you could consider looking at the 
> closure and hashing things in there as well, to reduce hash collisions.
>
>> I can then re-fetch the result on disk in another Guile process if 
>> the hashes match.
>
> You can't (at least not with the mentioned hash), because of likely 
> hash collisions. If the 'hash' function doesn't have collisions, then 
> either the input space is small (finite) and hence the 'hash' isn't 
> general-purpose (not always an issue, but in your case it seems like 
> it would be), or the output space is infinite, in which case it has 
> lost its function as a hash.
>
> Typical hashing of the non-cryptographic kind aren't designed to 
> virtually eliminate hash collisions, rather they are designed to be 
> 'good enough', and hash collisions are not expected to be eliminated, 
> instead they are accounted for in some way. In your case, the disk 
> acts as a hash table, so you could make a bucket list (so you would 
> need to also save the unhashed _keys_ instead of only their hash - 
> hashes then aren't to identify things on their own, but rather to 
> speed things up a lot).
>
> Best regards, Maxime Devos