Pattern matching as computation

49 views
Skip to first unread message

David Storrs

unread,
Jun 27, 2019, 2:56:48 PM6/27/19
to Racket Users
This works:

(match (vector 1 2 3 4)
 [(vector a b (app add1 c) d)
  (list a b c d)])
; => '(1 2 4 4)

Suppose instead I wanted to have a pattern like so (this does not work):

(match (vector 1 2 3 4)
  [(vector a (app + b ..2 x) c) (list a b c x)]
; => '(1 (2 3) 4 5))  ;  NB:  does not work

Is there a way to do this?


Less trivially, I'd like to be able to do something like this:

#lang racket
(struct person (name age) #:prefab)
(struct horse (color owner) #:prefab)

; The following does not work
(match (vector 'brown 'bob 23)
  [(vector the-color (app (curry apply person) args ... the-owner))
   (horse the-color the-owner)])

I've been through the pattern matching docs in some detail and tried various things, but my brain-fu is weak.

Eric Griffis

unread,
Jun 27, 2019, 5:16:44 PM6/27/19
to David Storrs, Racket Users
On Thu, Jun 27, 2019 at 11:56 AM David Storrs <david....@gmail.com> wrote:
>
> Suppose instead I wanted to have a pattern like so (this does not work):
>
> (match (vector 1 2 3 4)
>   [(vector a (app + b ..2 x) c) (list a b c x)]
> ; => '(1 (2 3) 4 5))  ;  NB:  does not work

We have a few problems here.

The pattern (vector a ? c) does not match the shape of the val-expr
(vector 1 2 3 4).

In the second sub-pattern (app + b ..2 x), Racket complains `..2' is an
"incorrect use of ... in pattern" because every sub-pattern of `app' must
independently match the result of (+ 2).

To get the expected result '(1 (2 3) 4 5) from the actual result (list a b
c x)
, we'd need the following bindings:

a --> 1
b --> '(2 3)
c --> 4
x --> 5

At this point, I'm not sure what you're trying to do. Maybe you're trying
to bind `b' to '(2 3), then `x' with the sum (+ 2 3), then `c' with 4.

Here's one way to do it from within a single pattern:

(match (vector 1 2 3 4)
  [(and (vector a _ _ c)
        (app (λ (vec)
               (match vec
                 [(vector _ p q _)
                  (list (list p q) (+ p q))]))
             (list b x)))
   (list a b c x)])

> Less trivially, I'd like to be able to do something like this:
>
> #lang racket
> (struct person (name age) #:prefab)
> (struct horse (color owner) #:prefab)
>
> ; The following does not work
> (match (vector 'brown 'bob 23)
>   [(vector the-color (app (curry apply person) args ... the-owner))
>    (horse the-color the-owner)])

Here's an adaptation of the above technique that works:

(match (vector 'brown 'bob 23)
  [(and (vector the-color _ _)
        (app (λ (vec)
               (match vec
                 [(vector _ name age)
                  (person name age)]))

             the-owner))
   (horse the-color the-owner)])

Incidentally, Algebraic Racket is designed for this:

#lang algebraic/racket/base

(require (prefix-in algebraic- algebraic/racket/base/forms))

(algebraic-case (vector 1 2 3 4)
  [#(a p q c)
   #:with b (list p q)
   #:with x (+ p q)
   (list a b c x)])

(algebraic-case (vector 'brown 'bob 23)
  [#(the-color name age)
   #:with the-owner (person name age)
   (horse the-color the-owner)])

If a #:with directive fails, the overall match fails:

(algebraic-case (vector 'brown 'bob 23)
  [#(the-color name age)
   #:with the-owner #f
   #:with (person _ _) the-owner
   (horse the-color the-owner)])

Eric

David Storrs

unread,
Jun 27, 2019, 6:31:21 PM6/27/19
to Eric Griffis, Racket Users
On Thu, Jun 27, 2019 at 5:13 PM Eric Griffis <ded...@gmail.com> wrote:
On Thu, Jun 27, 2019 at 11:56 AM David Storrs <david....@gmail.com> wrote:
>
> Suppose instead I wanted to have a pattern like so (this does not work):
>
> (match (vector 1 2 3 4)
>   [(vector a (app + b ..2 x) c) (list a b c x)]
> ; => '(1 (2 3) 4 5))  ;  NB:  does not work

We have a few problems here.

The pattern (vector a ? c) does not match the shape of the val-expr
(vector 1 2 3 4).

In the second sub-pattern (app + b ..2 x), Racket complains `..2' is an
"incorrect use of ... in pattern" because every sub-pattern of `app' must
independently match the result of (+ 2).

To get the expected result '(1 (2 3) 4 5) from the actual result (list a b
c x)
, we'd need the following bindings:

a --> 1
b --> '(2 3)
c --> 4
x --> 5

At this point, I'm not sure what you're trying to do. Maybe you're trying
to bind `b' to '(2 3), then `x' with the sum (+ 2 3), then `c' with 4.

Here's one way to do it from within a single pattern:

(match (vector 1 2 3 4)
  [(and (vector a _ _ c)
        (app (λ (vec)
               (match vec
                 [(vector _ p q _)
                  (list (list p q) (+ p q))]))
             (list b x)))
   (list a b c x)])

> Less trivially, I'd like to be able to do something like this:
>
> #lang racket
> (struct person (name age) #:prefab)
> (struct horse (color owner) #:prefab)
>
> ; The following does not work
> (match (vector 'brown 'bob 23)
>   [(vector the-color (app (curry apply person) args ... the-owner))
>    (horse the-color the-owner)])

Here's an adaptation of the above technique that works:

(match (vector 'brown 'bob 23)
  [(and (vector the-color _ _)
        (app (λ (vec)
               (match vec
                 [(vector _ name age)
                  (person name age)]))
             the-owner))
   (horse the-color the-owner)])

Makes sense -- it's two matches,  but still within one sexp.  Thanks.


Incidentally, Algebraic Racket is designed for this:

#lang algebraic/racket/base

(require (prefix-in algebraic- algebraic/racket/base/forms))

(algebraic-case (vector 1 2 3 4)
  [#(a p q c)
   #:with b (list p q)
   #:with x (+ p q)
   (list a b c x)])

(algebraic-case (vector 'brown 'bob 23)
  [#(the-color name age)
   #:with the-owner (person name age)
   (horse the-color the-owner)])

If a #:with directive fails, the overall match fails:

(algebraic-case (vector 'brown 'bob 23)
  [#(the-color name age)
   #:with the-owner #f
   #:with (person _ _) the-owner
   (horse the-color the-owner)])


That is drop-dead sexy.  Thanks, Eric.


Eric

Sorawee Porncharoenwase

unread,
Jun 27, 2019, 7:26:57 PM6/27/19
to David Storrs, Eric Griffis, Racket Users

If you want only one match, it seems what you really want is ~seq-like form from syntax/parse. E.g.,

#lang racket

(require syntax/parse)

(define ->value syntax->datum)

(define (apply-lift op args)
  (datum->syntax #f (apply op (->value args))))

(syntax-parse (vector 1 2 3 4)
  [#(a {~and {~seq {~between b 2 2} ...}
             {~bind [x (apply-lift + #'(b ...))]}} c)
   (->value #'(a (b ...) c x))])
;; => '(1 (2 3) 4 5)

(struct person (name age) #:prefab)
(struct horse (color owner) #:prefab)

(syntax-parse (vector 'brown 'bob 23)
  [#(the-color {~and {~seq args ...}
                     {~bind [the-owner (apply-lift person #'(args ...))]}})
   (->value (apply-lift horse #'(the-color the-owner)))])
;; => '#s(horse brown #s(person bob 23))




--
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...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/racket-users/CAE8gKocxGYu0vKvUzW%3Dfc1X7acXmZjCmz6mPKErDWvNmjKLmZw%40mail.gmail.com.
For more options, visit https://groups.google.com/d/optout.

David Storrs

unread,
Jun 27, 2019, 10:03:27 PM6/27/19
to Sorawee Porncharoenwase, Eric Griffis, Racket Users
Oh, neat.  Thank you, Sorawee.
Reply all
Reply to author
Forward
0 new messages