Search code examples
typescommon-lispdeftype

How do you define a type specifier MULTIPLE-OF with a parameter?


In ANSI Common Lisp, by Paul Graham, page 234, there is an example type specifier :

(deftype multiple-of (n)
  `(and integer (satisfies (lambda (x)
                             (zerop (mod x ,n))))))

Unfortunately it seems to not be a valid ANSI Common Lisp as CLHS says SATISFIES only takes a symbol, it can't take a lambda :

CL-USER> (typep 12 '(multiple-of 4))
The SATISFIES predicate name is not a symbol: (LAMBDA (X)
                                                (ZEROP
                                                 (MOD X 4)))
   [Condition of type SIMPLE-TYPE-ERROR]

How can you create that (multiple-of n) type specifier ?

Update

More globally, how could you create a type specifier like (integer * *) that can optionally takes two or more parameters and run a function/lambda on it ?


Solution

  • Based on this excellent answer, here is how to implement MULTIPLE-OF type specifier :

    (defun make-mof (n)
      #'(lambda (x) (zerop (mod x n))))
    
    (defvar *mof-name-cache* (make-hash-table :test #'equal))
    
    (deftype multiple-of (n)
      (let ((predicate-name
             (or (gethash n *mof-name-cache*)
                 (let ((pn (make-symbol (with-standard-io-syntax
                                         (format nil "MULTIPLE-OF-~S" n)))))
                   (setf (symbol-function pn) (make-mof n)
                         (gethash n *mof-name-cache*) pn)))))
        `(satisfies ,predicate-name)))
    

    and now I can check the type like this:

    CL-USER> (typep 12 '(multiple-of 4))
    T
    CL-USER> (typep 13 '(multiple-of 4))
    NIL