Opened 4 years ago

Last modified 4 years ago

#1020 accepted enhancement

matchable: Add conversion pattern(s)

Reported by: syn Owned by: ashinn
Priority: not urgent at all Milestone: someday
Component: extensions Version: 4.8.x
Keywords: matchable Cc:
Estimated difficulty:

Description (last modified by syn)

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 syn 4 years ago.

Download all attachments as: .zip

Change History (7)

Changed 4 years ago by syn

comment:1 Changed 4 years ago by syn

  • Description modified (diff)

comment:2 Changed 4 years ago by syn

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 4 years ago by ashinn

  • Status changed from new to accepted

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 4 years ago by syn

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 4 years ago by ashinn

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

comment:6 Changed 4 years ago by syn

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

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.