Skip to content

Tutorial 4: Composition

Mark Cox edited this page Sep 18, 2017 · 1 revision

The template function system is capable of automatically instantiating template functions that are dependencies of other template functions.

A keen reader of Tutorial 3: Optional and Rest Arguments would have noticed that the compute-mean function is not a template function.

We fix this in the example below to illustrate automatic instantiation.

The code below is the compute-mean template function.

(eval-when (:compile-toplevel :load-toplevel :execute)
  (defun make-compute-mean-lambda-form (argument-specification)
    (destructuring-argument-specification (<array>) argument-specification
      `(lambda (array)
         (declare (type ,<array> array))
         (let* ((mean (coerce 0 (array-element-type array)))
                (count (array-total-size array)))
           (when (zerop count)
             (error "Array length is zero."))
           (dotimes (i count)
             (incf mean (row-major-aref array i)))
           (/ mean count)))))

  (defun make-compute-mean-function-type (argument-specification)
    `(function ,argument-specification number)))

(define-template compute-mean (array)
  (:lambda-form-function #'make-compute-mean-lambda-form)
  (:function-type-function #'make-compute-mean-function-type))

We now define the nremove-mean template function.

(eval-when (:compile-toplevel :load-toplevel :execute)
  (defun make-nremove-mean-lambda-form (argspec)
    (destructuring-argument-specification (<array> &key ((:mean <mean>))) argspec
      `(lambda (array &key mean)
         (declare (type ,<array> array)
                  (type (or null ,<mean>) mean))
         (let* ((mean (coerce (or mean (compute-mean (the ,<array> array)))
                              (array-element-type array))))
           (dotimes (i (array-total-size array))
             (decf (row-major-aref array i) mean)))
         (values))))

  (defun make-nremove-mean-function-type (argspec)
    `(function ,argspec (values))))

(define-template nremove-mean (array &key mean)
  (:lambda-form-function #'make-nremove-mean-lambda-form)
  (:function-type-function #'make-nremove-mean-function-type))

Note that the compute-mean template function is invoked inside the lambda form of nremove-mean.

We now instantiate specialisation of the remove nremove-mean template.

(require-instantiations (nremove-mean (array)
                                      ((simple-array double-float))
                                      ((simple-array single-float))))

We can now use the meta-object protocol to see how many instantiations of the compute-mean template function there are.

(defun show-instantiations (template-function-name)
  (format t "~&Instantiations for ~A:" template-function-name)
  (loop
    for instantiation in (instantiations (find-template-function template-function-name))
    for index from 1
    do
       (format t "~&~d: ~A" index (instantiation-argument-specification instantiation)))
  (terpri))

(show-instantiations 'compute-mean)
(show-instantiations 'nremove-mean)

;; Instantiations for compute-mean:
;; 1: (array)
;; 2: ((simple-array double-float))
;; 3: ((simple-array single-float))
;; Instantiations for nremove-mean:
;; 1: (array)
;; 2: ((simple-array double-float))
;; 3: ((simple-array single-float))
Clone this wiki locally