Summary
matchable: Add conversion pattern(s)
Metadata
- Id: 79a824a6b818c7821655d8be3d8150251b915d7b
- Trac id: 1020
- Type: enhancement
- Reporter: syn
- Owner: ashinn
- Cc:
- Status: accepted
- Component: extensions
- Estimated difficulty:
- Resolution:
- Priority: not urgent at all
- Milestone: someday
- Version: 4.8.x
- Changetime: 2013-07-13 11:49:34 UTC
- Created: 2013-07-12 10:48:47 UTC
- Keywords: matchable
Attachments
- 79a824a6b818c7821655d8be3d8150251b915d7b/attachments/arrows-and-questionmarks.diff
Description
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
{{{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
Changes and comments
[2013-07-12 10:49:05 UTC] syn attached arrows-and-questionmarks.diff (description=#f)
[2013-07-12 10:53:02 UTC] syn changed description
[2013-07-12 11:12:28 UTC] syn wrote:
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)
[2013-07-12 15:10:41 UTC] ashinn changed status from new to accepted
[2013-07-12 15:10:41 UTC] ashinn wrote:
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.
[2013-07-12 15:20:38 UTC] syn wrote:
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.
[2013-07-12 15:29:59 UTC] ashinn wrote:
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.
[2013-07-13 11:49:34 UTC] syn wrote:
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!