Re: [racket-users] Using match on hash tables with optional keys

2018-09-01 Thread Greg Hendershott
>(define not-found (gensym 'not-found))
>(define (not-not-found? x) (not (eq? x not-found)))
>  ...
> #'(app (lambda (h) (hash-ref h key not-found))
>(? not-not-found? value-pattern)))

Oh dang. I shouldn't not have thought of that.

Nice!

-- 
You received this message because you are subscribed to the Google Groups 
"Racket Users" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to racket-users+unsubscr...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.


Re: [racket-users] Using match on hash tables with optional keys

2018-08-31 Thread David Storrs
Summary of the thread:

"Hey, here's this thing I'd like to do.  Is there a way to do it?"

T = :  "Nope.  Here's some suggestions, though."

T = 22 hours:  "Here, let me add that feature to the language."

Man, I love this community.  Thank you to everyone.


On Fri, Aug 31, 2018 at 5:46 AM, Ryan Culpepper  wrote:

> On 8/31/18 4:28 AM, Greg Hendershott wrote:
>
>> A general trick for optional values with match is something like (or
>> pat (app (λ _ default-value) pat)). But that doesn't work for
>> hash-table which uses [pat path] for each mapping. (At least I
>> couldn't see how.)
>>
>> Here's _a_ way you could write this as a match pattern:
>>
>> (define ((hash-has-keys? keys) ht)
>>   (for/and ([key (in-list keys)])
>> (hash-has-key? ht key)))
>>
>> (match (hash 'a 0 'b 1)
>>   [(? (hash-has-keys? '(a b))
>>   (app (λ (ht)
>>  (values (hash-ref ht 'a)
>>  (hash-ref ht 'b)
>>  (hash-ref ht 'c 'c-default)))
>>a b c))
>>(list a b c)])
>>
>> [I really don't like the hash-has-keys? business, and the resulting
>> double lookups. But (=> id) is available only to bodies, and
>> `failure-cont` didn't work for me. So I don't know how else to handle
>> the case where it should _not_ match.]
>>
>> Obviously the above is tedious. But, it works solely in term of match
>> patterns, and it could be the kind of syntax that a
>> define-match-expander emits for some nicer surface syntax. Where each
>> mapping is either required [k v], or, optional and you must supply a
>> default [k v d]. []
>>
>
> Here's an implementation:
>
>   (define not-found (gensym 'not-found))
>   (define (not-not-found? x) (not (eq? x not-found)))
>
>   (begin-for-syntax
> (define-syntax-class kvpat
>   #:description "hash key-value pattern"
>   ;; Note: be careful to evaluate key expr only once!
>   (pattern [key:expr value-pattern]
>#:with pattern
>#'(app (lambda (h) (hash-ref h key not-found))
>   (? not-not-found? value-pattern)))
>   (pattern [key:expr value-pattern default:expr]
>#:with pattern
>#'(app (lambda (h) (hash-ref h key (lambda () default)))
>   value-pattern
>
>   (define-match-expander hash-table*
> (syntax-parser
>   [(_ kv:kvpat ...)
>#'(? hash? kv.pattern ...)]))
>
>   (match (hash 'a 1 'b 2)
> [(hash-table* ['a a] ['c c 3])
>  (+ a c)])
>   ;; => 4
>
> Ryan
>

-- 
You received this message because you are subscribed to the Google Groups 
"Racket Users" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to racket-users+unsubscr...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.


Re: [racket-users] Using match on hash tables with optional keys

2018-08-31 Thread Ryan Culpepper

On 8/31/18 4:28 AM, Greg Hendershott wrote:

A general trick for optional values with match is something like (or
pat (app (λ _ default-value) pat)). But that doesn't work for
hash-table which uses [pat path] for each mapping. (At least I
couldn't see how.)

Here's _a_ way you could write this as a match pattern:

(define ((hash-has-keys? keys) ht)
  (for/and ([key (in-list keys)])
(hash-has-key? ht key)))

(match (hash 'a 0 'b 1)
  [(? (hash-has-keys? '(a b))
  (app (λ (ht)
 (values (hash-ref ht 'a)
 (hash-ref ht 'b)
 (hash-ref ht 'c 'c-default)))
   a b c))
   (list a b c)])

[I really don't like the hash-has-keys? business, and the resulting
double lookups. But (=> id) is available only to bodies, and
`failure-cont` didn't work for me. So I don't know how else to handle
the case where it should _not_ match.]

Obviously the above is tedious. But, it works solely in term of match
patterns, and it could be the kind of syntax that a
define-match-expander emits for some nicer surface syntax. Where each
mapping is either required [k v], or, optional and you must supply a
default [k v d]. []


Here's an implementation:

  (define not-found (gensym 'not-found))
  (define (not-not-found? x) (not (eq? x not-found)))

  (begin-for-syntax
(define-syntax-class kvpat
  #:description "hash key-value pattern"
  ;; Note: be careful to evaluate key expr only once!
  (pattern [key:expr value-pattern]
   #:with pattern
   #'(app (lambda (h) (hash-ref h key not-found))
  (? not-not-found? value-pattern)))
  (pattern [key:expr value-pattern default:expr]
   #:with pattern
   #'(app (lambda (h) (hash-ref h key (lambda () default)))
  value-pattern

  (define-match-expander hash-table*
(syntax-parser
  [(_ kv:kvpat ...)
   #'(? hash? kv.pattern ...)]))

  (match (hash 'a 1 'b 2)
[(hash-table* ['a a] ['c c 3])
 (+ a c)])
  ;; => 4

Ryan

--
You received this message because you are subscribed to the Google Groups "Racket 
Users" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to racket-users+unsubscr...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.


Re: [racket-users] Using match on hash tables with optional keys

2018-08-30 Thread Greg Hendershott
A general trick for optional values with match is something like (or
pat (app (λ _ default-value) pat)). But that doesn't work for
hash-table which uses [pat path] for each mapping. (At least I
couldn't see how.)

Here's _a_ way you could write this as a match pattern:

(define ((hash-has-keys? keys) ht)
  (for/and ([key (in-list keys)])
(hash-has-key? ht key)))

(match (hash 'a 0 'b 1)
  [(? (hash-has-keys? '(a b))
  (app (λ (ht)
 (values (hash-ref ht 'a)
 (hash-ref ht 'b)
 (hash-ref ht 'c 'c-default)))
   a b c))
   (list a b c)])

[I really don't like the hash-has-keys? business, and the resulting
double lookups. But (=> id) is available only to bodies, and
`failure-cont` didn't work for me. So I don't know how else to handle
the case where it should _not_ match.]

Obviously the above is tedious. But, it works solely in term of match
patterns, and it could be the kind of syntax that a
define-match-expander emits for some nicer surface syntax. Where each
mapping is either required [k v], or, optional and you must supply a
default [k v d].  It might also be nice to have syntax like id, no
brackets, meaning the hash key is 'id and the pattern id is #'id.


How Clojure handles destructuring maps, IIRC, is to supply defaults as
`:or {k v ...}` separate from the main matching map. (I don't love
that separation).  If you don't supply a default, it does the typical
Clojure thing where a missing value isn't an error it is nil. (I also
don't love that.)

Clojure does however have the nice thing where :key is bound to a key
identifer (like that 3rd variation with no brackets I just mentioned)
to avoid repetition.

-- 
You received this message because you are subscribed to the Google Groups 
"Racket Users" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to racket-users+unsubscr...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.


Re: [racket-users] Using match on hash tables with optional keys

2018-08-30 Thread George Neuner


On 8/30/2018 11:36 AM, David Storrs wrote:

I'd like to be able to write something like this:

(match (hash 'a 1 'b 2)
  [(hash-table ('a a) ('b b) ('c c))  (list a b c)])

...except with something that says "if 'c isn't present, that's fine.  
Use this value instead."


I've gone through the page on pattern matching and been unable to find 
anything.  I've tried various things with #:when but that doesn't seem 
to do what I need -- it only controls whether the match succeeds or fails.


I could write multiple clauses or do the defaulting before the match, 
but that's a lot of bother and it feels like there should be a more 
elegant solution.  Have I missed anything?




Match doesn't allow dynamic patterns.  The only way I can think you 
might do it is to do a wildcard match, check the values manually, and 
then force match to backtrack if necessary.  You can use *failure-cont* 
if you want to continue trying other matches in the same context.


George




--
You received this message because you are subscribed to the Google Groups "Racket 
Users" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to racket-users+unsubscr...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.


Re: [racket-users] Using match on hash tables with optional keys

2018-08-30 Thread David Storrs
On Thu, Aug 30, 2018 at 12:20 PM, Matthew Butterick  wrote:

>
> On Aug 30, 2018, at 8:36 AM, David Storrs  wrote:
>
> I'd like to be able to write something like this:
>
> (match (hash 'a 1 'b 2)
>   [(hash-table ('a a) ('b b) ('c c))  (list a b c)])
>
> ...except with something that says "if 'c isn't present, that's fine.  Use
> this value instead."
>
>
>
> Perhaps move 'c out of the match pattern?
>
> (let ([h (hash 'a 1 'b 2)])
>   (match h
> [(hash-table ('a a) ('b b))
>  (list a b (hash-ref h 'c (λ () 'other-val)))]))
>

Yep.  Or, alternatively, I could do it up front:

(define h (hash 'a 1))
(match (hash-set h 'b ((lambda (h) (hash-ref h 'b #f)) h))
  [(hash-table ('a a) ('b b)) (list a b)])

=>  '(1 #f)

That works, but it's pretty ugly.

-- 
You received this message because you are subscribed to the Google Groups 
"Racket Users" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to racket-users+unsubscr...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.


Re: [racket-users] Using match on hash tables with optional keys

2018-08-30 Thread David Storrs
Cool, thank you.

On Thu, Aug 30, 2018 at 11:38 AM, Sam Tobin-Hochstadt 
wrote:

> No, there isn't something here that you're missing. An addition here
> would probably be useful, though -- you might take a look at how
> Clojure does this, since they pattern match on a lot of dictionaries.
>
> Sam
> On Thu, Aug 30, 2018 at 11:36 AM David Storrs 
> wrote:
> >
> > I'd like to be able to write something like this:
> >
> > (match (hash 'a 1 'b 2)
> >   [(hash-table ('a a) ('b b) ('c c))  (list a b c)])
> >
> > ...except with something that says "if 'c isn't present, that's fine.
> Use this value instead."
> >
> > I've gone through the page on pattern matching and been unable to find
> anything.  I've tried various things with #:when but that doesn't seem to
> do what I need -- it only controls whether the match succeeds or fails.
> >
> > I could write multiple clauses or do the defaulting before the match,
> but that's a lot of bother and it feels like there should be a more elegant
> solution.  Have I missed anything?
> >
> >
> > --
> > You received this message because you are subscribed to the Google
> Groups "Racket Users" group.
> > To unsubscribe from this group and stop receiving emails from it, send
> an email to racket-users+unsubscr...@googlegroups.com.
> > For more options, visit https://groups.google.com/d/optout.
>

-- 
You received this message because you are subscribed to the Google Groups 
"Racket Users" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to racket-users+unsubscr...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.


Re: [racket-users] Using match on hash tables with optional keys

2018-08-30 Thread Sam Tobin-Hochstadt
No, there isn't something here that you're missing. An addition here
would probably be useful, though -- you might take a look at how
Clojure does this, since they pattern match on a lot of dictionaries.

Sam
On Thu, Aug 30, 2018 at 11:36 AM David Storrs  wrote:
>
> I'd like to be able to write something like this:
>
> (match (hash 'a 1 'b 2)
>   [(hash-table ('a a) ('b b) ('c c))  (list a b c)])
>
> ...except with something that says "if 'c isn't present, that's fine.  Use 
> this value instead."
>
> I've gone through the page on pattern matching and been unable to find 
> anything.  I've tried various things with #:when but that doesn't seem to do 
> what I need -- it only controls whether the match succeeds or fails.
>
> I could write multiple clauses or do the defaulting before the match, but 
> that's a lot of bother and it feels like there should be a more elegant 
> solution.  Have I missed anything?
>
>
> --
> You received this message because you are subscribed to the Google Groups 
> "Racket Users" group.
> To unsubscribe from this group and stop receiving emails from it, send an 
> email to racket-users+unsubscr...@googlegroups.com.
> For more options, visit https://groups.google.com/d/optout.

-- 
You received this message because you are subscribed to the Google Groups 
"Racket Users" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to racket-users+unsubscr...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.


[racket-users] Using match on hash tables with optional keys

2018-08-30 Thread David Storrs
I'd like to be able to write something like this:

(match (hash 'a 1 'b 2)
  [(hash-table ('a a) ('b b) ('c c))  (list a b c)])

...except with something that says "if 'c isn't present, that's fine.  Use
this value instead."

I've gone through the page on pattern matching and been unable to find
anything.  I've tried various things with #:when but that doesn't seem to
do what I need -- it only controls whether the match succeeds or fails.

I could write multiple clauses or do the defaulting before the match, but
that's a lot of bother and it feels like there should be a more elegant
solution.  Have I missed anything?

-- 
You received this message because you are subscribed to the Google Groups 
"Racket Users" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to racket-users+unsubscr...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.