Closed guicho271828 closed 9 years ago
I think this can be achieved by generalizing guard pattern. One possible generalization could be extending guard pattern (guard subpattern test-form)
can take another optional subpattern to match the result of TEST-FORM
, like (guard subpattern1 form subpattern2)
. So (guard subpattern test-form)
is equivalent to (guard subpattern test-form (not null))
.
Sounds interesting, I interpreted your idea as something like this:
(match 'function
((guard notused '* x) x))) ; --> '*
but I'm afraid that it would be not as intuitive as the original guard pattern. test-form
of guard pattern is supposed to return a boolean.
Also, it does not allow more than 2 subpatterns without consing. Suppose mathing against array type specifier:
(match 'array
((guard notused (list '* '*) (list element-type dimensions))
;;; ^^^^^^^^^ this is consing!
(vector element-type dimensions))) ;; -> #(* *)
;;; we can also do this hack below, but it doesnt look good
(match 'array
((and (guard notused '* element-type)
(guard notused '* dimensions))
(vector element-type dimensions))) ;; -> #(* *)
Instead, how about this alternative: (guard subpattern test-form {generator-form subpattern}*)
(match 'function
((guard x (eq x 'function) '* y) (vector x y)))) ; --> #(function *)
;; cons-pattern impl
(match '(1 2)
((guard x (consp x)
(car x) car
(cdr x) cdr) (vector car cdr)))) ; --> #(1 2)
;; well, with <>-pattern, above guard pattern is equivalent to
(match '(1 2)
((and (guard x (consp x))
(<> car x (car x))
(<> cdr x (cdr x))) (vector car cdr)))) ; --> #(1 2)
(match 'array
((guard 'array t '* element-type '* dimensions)
(vector element-type dimensions))) ;; -> #(* *)
BTW, good to know that the 1st argument of guard
pattern is a subpattern, not merely a variable.
You can write the code like below even now on?
(guard (cons a b) (= (* a 2) b))
introduction
binding-pattern
(<> pattern arg value)
works just aslet
. the value to be matched is put intoarg
.value
evaluates and returns some value, then the return value is matched againstpattern
.Consider you want to write a query that matches the type specifier
function
, and destruct it into the types of arguments and the types of return values.This is not sufficient, because Common lisp allows "dropping" the type specifier arguments. e.g.
function
,(function)
,(function ())
,(function () (values &rest *))
are all valid type specifiers.Ideally, above code should work as below, since the default type for function return value is
*
.background
One currently approved methodology to achieve this effect is to use the optima's internal functions and structures. To implement such a pattern, we have to implement all these structure and methods below. THIS IS AWFUL.
THIS IS AWFUL.
binding-pattern
Now we introduce a binding pattern. It can bind arbitrary value obtained from the matched element, then assign it against the subpattern.
This is a refinement of the previous posts about accessor-pattern in #90. for example,
cons
pattern, currently implemented with nativeconstructure-pattern
, can be implemented as below:Related works
Providing accessors and use the feature in optima that parse the structure pattern prefix like
(foo- bar)
and(defun foo-bar ()...)
and(defun foo-p () ...)
is another way around, but binding-pattern supersedes this idea. By separatingfoo-p
andfoo-bar
, it only results in redundunt and inefficient code. Consider the following example:It runs the same destructuing twice and ineffficient. The approache should be in reverse --- Actually, it should be the pattern that defines an accessor. For example, using a macro like
defpattern-with-accessors
, we can write:https://github.com/Bike/compiler-macro provides sufficiently smart type extraction, and I am refactoring their functions with optima. However, given that lots of custom patterns should be written using optima's internal structure, for each of type specifiers in common lisp, like
array
,(array fixnum (*))
etc, it is almost impossible to achieve this without binding-type.