I'm confused by the behavior of pattern matching in some cases.  I'll 
discuss a few sets of examples involving segment patterns, then a mix of 
`and`, `or`, and `not` patterns.


Here is a basic segment pattern matching the entire contents of a list:
> (match '(1 2 3)
     ((list x ...) `(result: ,x)))
'(result: (1 2 3))

Let's add a prefix to the scrutinized list, and match it with a separate 
pattern variable:
> (match '((1 2 3) 1 2 3)
     ((list w x ...) `(result: ,w ,x)))
'(result: (1 2 3) (1 2 3))

Now let's constrain the prefix to be the same as the matched segment:
> (match '((1 2 3) 1 2 3)
     ((list x x ...) `(result: ,x)))
'(result: (1 2 3))

Everything seems fine so far.  Let's make the segment pattern more 
interesting.

This time, we'll have our segment pattern match elements that are lists of 
two elements:
> (match '((a 1) (b 2) (c 3))
     ((list (list k v) ...) `(result: ,k ,v)))
'(result: (a b c) (1 2 3))

We can add a prefix as before, corresponding to the first element of each 
two-element list in the remaining segment, and match it with a separate 
pattern variable:
> (match '((a b c) (a 1) (b 2) (c 3))
     ((list j (list k v) ...) `(result: ,j ,k ,v)))
'(result: (a b c) (a b c) (1 2 3))

So far so good.  Now let's try constraining the prefix to be the same as 
the first element of each two-element list in the segment pattern:
> (match '((a b c) (a 1) (b 2) (c 3))
     ((list k (list k v) ...) `(result: ,k ,v)))
; k113: unbound identifier;
;  also, no #%top syntax transformer is bound
;   in: k113
; [,bt for context]

Something went wrong.  Is this behavior expected?  This error doesn't look 
like the kind you'd expect a user to see.


Let's try another example with segment patterns:
> (match '(1 2 3 : 1 2 3)
     ((list x ... ': y ...) `(result: ,x ,y)))
'(result: (1 2 3) (1 2 3))

We're using : as a divider, allowing both the x and y segments to be 
non-empty.  In this case, they are bound to the same values.

What happens if we try to constrain the two segments to be identical by 
repeating the variable?:
> (match '(1 2 3 : 1 2 3)
     ((list x ... ': x ...) `(result: ,x)))
non-linear pattern used in `match` with ... at #<syntax:4:19 x> and 
#<syntax:4:10 x>
'(result: (1 2 3))

It looks like we got the right result (see the next example), but it looks 
like an error message was printed beforehand.  Is this message intended as 
a warning?  Should it actually be raising an error instead?  It almost 
looks like debug logging.

Why might an error be the expected behavior?  It seems we get the right 
result only coincidentally.  If we remove the divider and use separate 
variables, we can see the greedy behavior of segment patterns:
> (match '(1 2 3 1 2 3)
     ((list x ... y ...) `(result: ,x ,y)))
'(result: (1 2 3 1 2 3) ())

When we try to constrain the segment by repeating the same variable, we get 
the printed warning followed by an unexpected result:
> (match '(1 2 3 1 2 3)
     ((list x ... x ...) `(result: ,x)))
non-linear pattern used in `match` with ... at #<syntax:8:16 x> and 
#<syntax:8:10 x>
'(result: (1 2 3 1 2 3))

Is this greedy behavior expected even when the segment pattern variable is 
constrained in this way?  It seems like we should at least get an error.


Moving on, here's an example using `and` and `not` that behaves reasonably:
> (match (list 1 2 3)
   ((and (list a _ _) (not (list _ a _))) `(result: ,a)))
'(result: 1)

We can see how the scrutinized list is being constrained to not repeat the 
same first two elements:
> (match (list 1 1 3)
     ((and (list a _ _) (not (list _ a _))) `(result: ,a)))
; match: no matching clause for '(1 1 3) [,bt for context]

If we swap the order of the sub-patterns, expecting the same behavior as 
the first example, we get a surprise:
> (match (list 1 2 3)
     ((and (not (list _ a _)) (list a _ _)) `(result: ,a)))
; match: no matching clause for '(1 2 3) [,bt for context]

It seems that the order of sub-patterns in an `and` pattern matters, and 
determines logical scoping of pattern variables.  In the first pattern, we 
are getting the behavior of the logical proposition:
(exists (a)
  (and (== VALUE (list a _ _)) (not (== VALUE (list _ a _)))))

While in the second example, we seem to be getting the behavior of the 
logical proposition:
(exists (a)
  (and (== VALUE (list a _ _)) (not (exists (a) (== VALUE (list _ a _))))))
Or, pushing the `not` inward slightly:
(exists (a)
  (and (== VALUE (list a _ _)) (forall (a) (not (== VALUE (list _ a _))))))

Is this non-commutative behavior of `and` intended?


Here's an example involving `and` and `or` that attempts to ensure that a 
three-element list either has identical first and second elements, or 
identical second and third elements:
> (match (list 1 2 2)
   ((and (list a b c) (or (list _ a _) (list _ _ b))) 'success))
; readline-input:2:47: match: variable not bound in all or patterns
;   at: b
;   in: (or (list _ a _) (list _ _ b))
; [,bt for context]

Given the binding behavior we saw with `and` in the previous example, I 
figured this one would work, since we're just reusing the same pattern 
variables.  But instead we get an error about a variable not being bound in 
all `or` patterns.

The documentation suggests something like this might be the case, 
"instances of an id in different or and not sub-patterns are independent. 
The binding for id is not available in other parts of the same pattern."

But this seems inconsistent with the successful `(and ... (not ...))` 
example.  Why does that one work, but this one not?

What makes this really perplexing is that a slightly different set of 
examples, where we make sure to mention the same pattern variables in each 
disjunct, does work, this time ensuring that the first element is repeated 
in at least one of the other two positions:
> (match (list 1 2 3)
     ((and (list a b c) (or (list _ a _) (list _ _ a))) 'success))
; match: no matching clause for '(1 2 3) [,bt for context]
> (match (list 1 2 1)
    ((and (list a b c) (or (list _ a _) (list _ _ a))) 'success))
'success
> (match (list 1 1 3)
     ((and (list a b c) (or (list _ a _) (list _ _ a))) 'success))
'success


Here's an example involving `not` and `or` that ensures the scrutinized 
three-element list does not repeat consecutive elements:
> (match (list 1 2 3)
     ((not (or (list a a _) (list _ a a))) 'success))
'success

If we change the pattern variable in the second disjunct, the intended 
logical meaning is the same, but the example breaks:
> (match (list 1 2 3)
     ((not (or (list a a _) (list _ b b))) 'success))
; readline-input:13:34: match: variable not bound in all or patterns
;   at: b
;   in: (or (list a a _) (list _ b b))
; [,bt for context]

I can understand this behavior in the case of `or` appearing in a positive 
context (since otherwise we could introduce ambiguous bindings on the 
right-hand-side), but when negated, bindings for these pattern variables 
won't be available on the right-hand-side anyway.  Is this behavior 
intended for simplicity of implementation?

-- 
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.

Reply via email to