Skip to content

Type based Destructuring Patterns

Masataro Asai edited this page Apr 20, 2017 · 16 revisions

This section contains standard, object-accessing patterns that are most commonly used.

Cons Pattern

Syntax
cons cdr car

cons checks if the object is of type cons, and then matches its subpatterns with car and cdr of that cons cell.

(match '(1 2 3)
  ((cons x y)
   (print x)
   (print y)))
;; |-> 1
;; |-> (2 3)

List, List* Pattern

Syntax
list &rest subpatterns

list* &rest subpatterns

Both patterns checks if the object is of type list, has the same length and then matches the contents to the subpatterns. list* also checks the elements against the subpatterns, however the last pattern is matched against nthcdr of the list. Thus, the code below returns 3.

(match '(1 2 . 3)
  ((list* _ _ x)
   x))

When list* pattern has a single subpattern x e.g. (list* x), however, the object type is not checked and the object always matches. This is related to the definition of list* in http://clhs.lisp.se/Body/f_list_.htm:

If list* receives only one object, that object is returned, regardless of whether or not it is a list. 

Pattern matching/destructuring is a dual operation of object construction. If a vector is created with a constructor form (vector 1 2 3), the pattern which matches the object should also resemble the form, e.g. (vector a b c). Since destructor & constructor are dual, it is better if the behavior of the pattern reflects the original function.

Both patterns can be derived from cons pattern. See also: ./defpattern

Vector, Vector* Patterns

Syntax
vector &rest subpatterns

vector* &rest subpatterns

vector checks if the object is a vector, if the lengths are the same, and if the contents matches against each subpatterns. vector* is similar, but called a soft-match variant that allows if the length is larger-than-equal to the length of subpatterns.

(match #(1 2 3)
  ((vector _ x _)
   x))
;; -> 2

(match #(1 2 3 4)
  ((vector _ x _)
   x))
;; -> NIL : does not match

(match #(1 2 3 4)
  ((vector* _ x _)
   x))
;; -> 2 : soft match.

There are several specialized subpatterns for vector/vector*. Using these variants properly will results in faster code.

<vector-pattern> : vector      | simple-vector
                   bit-vector  | simple-bit-vector
                   string      | simple-string
                   base-string | simple-base-string | sequence 
(<vector-pattern> &rest subpatterns)

Class Pattern / Structure Pattern

structure pattern is just a synonym to the class pattern.

Syntax
class class-name &rest slot-description*

structure class-name &rest slot-description*

class-name
A symbol which satisfies one of the following:
  • (find-class <class-name>) returns a class.
  • Symbol <class-name>-p in the same package is fbound. It should be a unary function, or the consequence is undefined.
  • Symbol <class-name>p in the same package is fbound. It should be a unary function, or the consequence is undefined.
slot-description
one of the following:
make-instance style plist
keyword subpattern
with-slots style
(slot-name subpattern)
with-accessors style
(accessor-name subpattern) . accessor-name is a symbol, where the corresponding accessor function should be fbound to one of the following symbols:
  • Symbol accessor-name itself. (common style for generic functions)
  • Symbol <class-name>-<accessor-name> in the same package. (common style for structure accessor functions)
  • Symbol <class-name><accessor-name> in the same package. (compatibility to Optima)
slot-name
the value returned by (slot-value <obj> '<slot-name>) is bound to the variable of the same name.
accessor-name
the value returned by the accessor is bound to the variable of the same name.

We just post an example for each of three style here.

(defstruct foo bar baz)
(defvar *x* (make-foo :bar 0 :baz 1)
(match *x*
  ((foo :bar a :baz b) ;; make-instance style
   (values a b))
  ((foo (bar a) (baz b)) ;; with-slots style
   (values a b))
  ((foo bar baz) ;; slot name
   (values bar baz)))

Type Pattern, Satisfies Pattern

Syntax
type type

satisfies predicate

type
type specifier, not evaluated.
predicate
a name of a boolean function of 1 argument, not evaluated.

type pattern matches if the object is of type. satisfies matches if the predicate returns true for the object. lambda form is acceptable.

Assoc, Property, Alist, Plist Pattern

Syntax
assoc item subpattern &key key test

property key subpattern &optional default foundp

All these patterns first checks if the pattern is a list. If that is satisfied, it then obtain the contents with (cdr (assoc item X key test)) (assoc pattern) or (getf key X) (property pattern) where X is bound the container. The value obtained by these accessors is then matched against subpattern.

Property pattern matches the default value to the pattern if supplied and the value was not found, and also binds t/nil to foundp.

Two patterns are derived from these patterns.

Syntax
alist &rest args

plist &rest args

alist and plist patterns expand into a collection of assoc and property patterns, respectively, connected by an and pattern.

Array, simple-array, row-major-array pattern

Syntax
array &key element-type adjustable has-fill-pointer displaced-to (displaced-index-offset 0) dimensions rank total-size (contents nil)

Matches against an array, its contents, and its meta-level information such as size, element-type.

  • CONTENTS is a matrix notation of patterns, i.e., a tree of patterns. For example, :contents ((A _ _) (_ B _) (_ _ C)) matches against (AREF X 0 0), (AREF X 1 1), (AREF X 2 2) of an array X and binds them to A, B, C respectively.
  • DIMENSIONS should be either
    • a quoted list of integers (e.g. ’(5 4 4) ),
    • an integer specifying a 1-dimensional array (e.g. 256),
    • or a list pattern (e.g. (list 5 A _)).
  • RANK should be an integer (e.g. 3) or a variable pattern (e.g. A, _).
  • The rank of the array should be deduced from DIMENSIONS or RANK itself, and the deduced rank and the specified RANK should be consistent when both are present. Otherwise, the compilation fails. Rank information is used to parse the subpatterns.
  • TOTAL-SIZE should be consistent with DIMENSIONS when all dimensions are fully specified (e.g. when DIMENSIONS = ’(5 4 4) and TOTAL-SIZE is a variable pattern, then it is bound to 80. When TOTAL-SIZE is an integer, it should be 80 or it signals an error.)
  • ELEMENT-TYPE is * unless specified.
  • If ADJUSTABLE, HAS-FILL-POINTER, DISPLACED-TO are all NIL, then it is a SIMPLE-ARRAY. Otherwise it’s an ARRAY.
Syntax
simple-array &key element-type dimensions rank total-size contents

Matches against a simple-array, its contents, and its meta-level information such as size, element-type. This is a wrapper around the base ARRAY pattern.

Syntax
row-major-array &key element-type adjustable has-fill-pointer displaced-to displaced-index-offset dimensions rank total-size contents

Same as ARRAY pattern, but it uses row-major-array to access the elements. CONTENTS is a list of patterns (just like in VECTOR pattern), rather than a matrix notation of the patterns in ARRAY pattern.”