Opened 10 years ago

Last modified 10 years ago

#1020 accepted enhancement

matchable: Add conversion pattern(s)

Reported by: Moritz Heidkamp Owned by: Alex Shinn
Priority: not urgent at all Milestone: someday
Component: extensions Version: 4.8.x
Keywords: matchable Cc:
Estimated difficulty:

Description (last modified by Moritz Heidkamp)

The attached patch adds two new pattern types to matchable:

(-> convert pat-1 … pat-n)
(-?> convert pat-1 … pat-n)

These patterns work like the (? predicate pat-1 … pat-n) pattern
in that convert must be an expression evaluating to a single
argument function which is applied to the corresponding
value. However, in contrast to ?, the following patterns
pat-1 … pat-n are applied to the value _returned_ by
convert. In addition, -?> only matches when convert
does not return #f, so it can be expressed as combination of
-> and ? like this:

(-> convert (? (lambda (x) x) pat-1 … pat-n))

The rationale is that it allows to match against a converted value and
at the same time being able to bind that value to a variable instead
of having to apply the conversion again in the body. One might argue
that -?> is not really necessary so I'd be happy with only
adding ->, too. It should be noted that this patch is not
backwards compatible.

Some examples:

(match '("foo" "10")
  (("foo" (-> string->number x)) (+ x x)))

;; => 20

(match '("foo" "bar")
  (("foo" (-> string->number x)) (+ x x)))

;; Error: (+) bad argument type: #f

(match '("foo" "10")
  (("foo" (-?> string->number x)) (+ x x))
  (else 'nothing))

;; => 20

(match '("foo" "bar")
  (("foo" (-?> string->number x)) (+ x x))
  (else 'nothing))

;; => nothing

Attachments (1)

arrows-and-questionmarks.diff (1.7 KB) - added by Moritz Heidkamp 10 years ago.

Download all attachments as: .zip

Change History (7)

Changed 10 years ago by Moritz Heidkamp

comment:1 Changed 10 years ago by Moritz Heidkamp

Description: modified (diff)

comment:2 Changed 10 years ago by Moritz Heidkamp

Another nice use is destructuring records using slot accessors rather
than the positional ($ struct pat-1 … pat-n) pattern:

(define-record point x y)

(match (list (make-point 1 2) (make-point 3 4))
  (((-> point-x xs) ...) xs))

;; => (1 3)

comment:3 Changed 10 years ago by Alex Shinn

Status: newaccepted

Interesting. I believe -> is equivalent to the existing =.

-?> can be achieved by combining ? and =.
I'm not sure if the use cases are common enough to
warrant the shortcut, but I'll consider it.

comment:4 Changed 10 years ago by Moritz Heidkamp

Ah, you are right, = is almost the same, except that it only allows for exactly one following pattern. I didn't notice it because the documentation is a bit misleading (suggesting that it only works on fields, although this is only one common case). Maybe I can think of something to improve that. Should you consider adding a shortcut, perhaps =? would be a good name. How about extending = to allow for more than one following pattern in any case? This would even be backwards compatible.

comment:5 Changed 10 years ago by Alex Shinn

Multiple patterns can be done with an and. This could
be made implicit, at which point the only disadvantage is
loss of portability.

The -?> is also just a matter of nesting ?:

(match '("foo" "10")

(("foo" (= string->number (? values x))) (+ x x)))

This isn't too much more verbose and no less efficient.

In general the idea is to do more with less, and try
to keep the match syntax simple - it's already complex
enough that people overlook things like the = syntax :)

If you have real-world examples that show -?> as
a likely common idiom I can consider it, but I'm more
likely to just add a single extension mechanism and
stop adding any new fixed syntax.

BTW, "field accessor" comes from the original description
in Wright's paper. Suggestions for improvement are

comment:6 Changed 10 years ago by Moritz Heidkamp

Right, implicit `and' makes for more convenient patterns but agreed,
loss of portability is an issue, too. I'm also aware of the fact that
-?> can be expressed by nesting ? (see the original ticket
description) but I found it a bit verbose. Nice trick to use `values'
for identity, by the way, I shall keep that one in mind. Anyhow, the
use case I encountered for -?> is indeed what I gave as an
example. This is especially handy when matching on
(command-line-arguments) where some arguments are numbers. OTOH, maybe
it is better to use = in that case, too, so that one can give a more
meaningful error message rather than falling through to no matching

I am much more in favor of a general extension mechanism, though, so
that we can stop piling features on top :-) Do you have anything in
mind how this could be accomplished? I'd gladly try my hand at
implementing that!

Note: See TracTickets for help on using tickets.