How do ephermerons and weak hashes work?

I apparently don't understand the concept of unreachability as it applies to the garbage collector.

(define h1 (make-ephemeron-hash))
(define h2 (make-weak-hash))

(struct thing (val) #:transparent)

(let ()
  (hash-set! h1
             (~a "foo" "bar") (thing  1) ; strings created with ~a are not interned                           
             )
  (hash-set! h2
             (~a "bar" "baz") (thing  2))

  ; inside the `let`, each hash should contain one k/v pair                                                   
  (pretty-print h1)
  (pretty-print h2))

(sleep 2) ; give it time for unreachability to be noticed                                                     
(collect-garbage) ; force a GC pass, just to be sure                                                          

; both hashes should be empty                                                                                 
(pretty-print h1)
(pretty-print h2)

; Output: 
; (hash "foobar" (thing 1))
; (hash "barbaz" (thing 2))

I guess the values are being retained because they are accessible through h1 and h2? That surprises me, since I thought the entire point of these things was to ensure that those hashes didn't count.

I get empty hashes for the second printout when I try your program.

Note that the sleep is not needed; nothing concurrent with your program is happening. However, additional garbage collections can cause more things to be unreachable via ephemerons, if the unreachable key was noticed to be unreachable after the ephemeron was visited earlier by the GC.

Ohhhkay... I reliably get NON-empty hashes. I'm on Racket 8.4 on macOS 10.15.5, would that matter? If not, where should I be looking?

You should try adding a second (or third) call to collect-garbage and see if that changes the result.

Tried it, no change.

With Racket 8.5 CS on x86_64-linux, I also get consistently empty hashes.

@dstorrs How are you running the program?

Why I ask:

I get the same result as everyone except you -- empty hash-tables -- when running using command-line Racket, DrRacket, and Racket Mode with racket-pretty-print set to nil.

But running using Racket Mode with racket-pretty-print set to t, I see the non-empty hash-tables. What is retaining a reference to the keys? I suspect this. I'll open an issue.

1 Like

The fix might be as simple as using a weak hash-table there. But I'll give it a think.

Aha. Yes, that was the issue. When I run it from the command line it works as expected and as soon as I set racket-pretty-print to nil it also worked.

Thanks, Greg.

I merged a fix yesterday, so that racket-pretty-print enabled works, too.

It should already be available on MELPA, whenever you might want to update Racket Mode.

I uninstalled racket-mode as per the directions, then went to reinstall it, and emacs told me "Hey, I can't connect to elpa! Stinks to be you!" This sent me down a rabbit hole of googling to figure out why emacs wasn't able to connect when Firefox could. One solution said that it's an issue with the user-agent string. Another said that I needed to downgrade (!) my OpenSSL library, which is a thing that macOS makes extremely difficult, which is sad since the one that comes on macOS 10.15 is OpenSSL 1.1.0-pre5-dev xx XXX xxxx which holy heckfire, the current version is 3.0!!

In the end I simply copied the racket-mode-20211130.1748 directory back into .emacs.d/elpa and called it a day. :confused: It seems to be working, which is bizarre since I reverted the racket-pretty-print to be non-nil. I'm going to chalk this one up to "computers are weird, networks are awful, GNU software is user-hostile, and ain't nobody got time for this."

Thanks for pointing me to the fix. If I wanted to git clone the latest racket-mode, what would the appropriate version string be so that I could drop it into .../elpa ?

1 Like

That sounds frustrating. Emacs and TLS isn't my wheelhouse, so my advice for a problem connecting with MELPA would be their guide, and if still no joy, open an issue there?

1 Like