2
> (define h #hash((a . 11) (b . 0)))
> (define (f h key)
    (match h
      [(hash-table (key value)) value]
      [_ 'do-something-else]))
> (f h 'a)
'do-something-else  ;; expect 11

How should I modify the match pattern so the function above returns the same as (hash-ref h key 'do-something-else)?

One problem seems to be that match is auto-quoting key, so that it literally matches 'key rather than the value of the key local variable. A second problem is that match hash-table seems to want to match the entire hash table rather than just one key-value pair.

Ben Kovitz
  • 4,920
  • 1
  • 22
  • 50
  • To clarify: The point is not to duplicate `hash-ref`. This is just a [Minimal, Complete, Verifiable Example](https://stackoverflow.com/help/mcve). More realistically, I'd have multiple match clauses followed by a catch-all at the end. In effect, "destructure the first key/value pair that matches". Or who knows? There's no telling when I might want to match a local variable rather than a literal. – Ben Kovitz Sep 10 '18 at 09:13
  • 1
    See also [Can `match` in Racket have patterns with variables from an outer scope?](https://stackoverflow.com/questions/29978583/can-match-in-racket-have-patterns-with-variables-from-an-outer-scope) – Alex Knauth Sep 10 '18 at 12:34

1 Answers1

2

How to duplicate the behavior of hash-ref

To duplicate the behavior of hash-ref, you need to do two things:

(1) Use a key pattern that only matches things that are equal to the value of the argument key. You can do this with the == pattern:

(hash-table ((== key) value))

(2) Match hash tables where there are other entries other than key. You can do this by adding _ ... to match the other entries.

(hash-table ((== key) value) _ ...)

In context:

(define (f h key)
  (match h
    [(hash-table ((== key) value) _ ...) value]
    [_ 'do-something-else]))

Makes (f h key) behave like (hash-ref h key 'do-something-else).

Why your first attempt didn't work

Your pattern:

(hash-table (key value))

Does not autoquote the key so that it literally matches 'key. Instead it matches any hash table that has only one entry:

> (match (hash 'anything-goes 11)
    [(hash-table (key value)) value]
    [_ 'do-something-else])
11

That's because key is interpreted as a pattern, and an identifier as a pattern matches anything. You can see this more clearly if you name it to something else and use it in the body:

> (define (f h key-arg)
    (match h
      [(hash-table (key-pat value))
       (printf "key-arg = ~v\n" key-arg)
       (printf "key-pat = ~v\n" key-pat)
       value]))
> (f (hash 'anything-goes 11) 'a)
key-arg = 'a
key-pat = 'anything-goes
11

When you write an identifier pattern that "happens" to have the same name as a local variable, it shadows it, just like this example:

> (let ([x "local variable"])
    (match (list 1 2 3)
      [(list 1 2 x)
       x]))
 3

The x in the pattern matches anything, so it's matched against the 3. The pattern shadows the x, so when the body uses x, it's referring to the 3, and not the local variable. The local variable was never actually used because of this shadowing.

Alex Knauth
  • 8,133
  • 2
  • 16
  • 31
  • 1
    Thank you! I'd been staring and staring at the table [here](https://docs.racket-lang.org/reference/match.html?q=match#%28form._%28%28lib._racket%2Fmatch..rkt%29._match%29%29) and not finding a way—because that's not where it was! :) And thanks for straightening out my false theory about autoquoting. I'm glad to see that `==` is so simple and general (thanks to its optional second argument). – Ben Kovitz Sep 10 '18 at 12:08
  • 1
    It's hidden in the grammer for `pattern` where it says `derived-pattern`, since `==` is defined outside of the "core" match form. However, from a user's perspective this shouldn't matter very much, so [this pull request](https://github.com/racket/racket/pull/2145) has proposed to add `==` to the match grammar to make it more discoverable. – Alex Knauth Sep 10 '18 at 12:26