Search code examples
racketcontractduck-typing

Do checks like `hash?` in Racket support duck-typing?


I'm a Python programmer new to Racket...

I have seen some code like:

(define table/c (and/c hash? (not/c immutable?)))

I understand this to be a use of Contracts, i.e. https://docs.racket-lang.org/reference/data-structure-contracts.html#%28def._%28%28lib._racket%2Fcontract%2Fbase..rkt%29._and%2Fc%29%29

So it means that anywhere in this library which accepts a table/c arg, it should be an object which is a mutable hash-table.

My question is... does Racket have any support for duck-typing here (specifically to satisfy the contract of hash?)?

Say I wanted to replace the basic hash-table with something backed by a k/v store... Is there a 'hash-table' interface I could implement that would allow my store-backed custom object to pass a hash? contract check?

The docs for contracts and this for the hash? check didn't help me much.


Solution

  • I think you really want to use dictionaries for this, but if you're stuck with a library which wants hash? for contract checks then you can use impersonators.

    Here's a function I wrote which tries to use a weak hash has a cache for some possibly-expensive computation represented by the (misnamed) filler function which will get called on a missing key.

    I'm rather unsure about the semantics of this sort of thing (and I'm not an expert on any of this) but it works in practice.

    (define (make-cache filler)
      (impersonate-hash
       (make-weak-hash '())
       (λ (ht key)
         ;; impersonate hash-ref by calling the filler
         (unless (hash-has-key? ht key)
           ;; I think this is safe as the key won't be dropped
           (hash-set! ht key (filler key)))
         (values key (λ (ht k v) v)))
       ;; Everything else is passed through
       (λ (ht k v)
         (values k v))
       (λ (ht k)
         k)
       (λ (ht k)
         k)))