Hi Pascal
this works in LW
CL-USER 21 > *(deftype msb (&optional (bits 8)) `(satisfies ,(curry1 'msb-test bits)))* *MSB*
CL-USER 22 > *(typep 42 '(msb 6))* ; Less than or equal to 6, fail. *NIL*
CL-USER 23 > *(typep 42 '(msb 7)) *; Greater than or equal to 7, succeed.
*T*
The function *CURRY1* just curries one argument and *MSB-TEST* does the check, taking two arguments, the second being the object to *typep* (details irrelevant; trust me: it works).
I know: done too much Haskell recently.
MA
On Wed, Feb 21, 2024 at 11:45 PM Pascal Bourguignon pjb@informatimago.com wrote:
Le 21/02/2024 à 22:10, Marco Antoniotti a écrit :
Hi
I just stumbled upon this and I need confirmation.
You cannot do anything like this, can you?
*(defstruct foo x)*
*(deftype foo-with-x (x) (satisfies 'foo-with-x-p)) ; No `x'!
deftype is like defmacro really.
So you could write:
(deftype foo-with-x (x) `(satisfies ',(intern (format nil "FOO-WITH-~S-P" x)))
But you would need to have defined also the predicate with the same name at compilation-time (or, when the type is used).
cl-user> (deftype foo-with-x (x) `(satisfies ,(intern (with-standard-io-syntax (format nil "FOO-WITH-~S-P" x))))) FOO-WITH-X cl-user> (defstruct foo x) FOO cl-user> (defstruct bar y) BAR cl-user> (defun foo-with-x-p (o) (typep o 'foo)) FOO-WITH-X-P cl-user> (defun foo-with-y-p (o) (typep o 'bar)) FOO-WITH-Y-P cl-user> (typep (make-foo) '(foo-with-x x)) T cl-user> (typep (make-foo) '(foo-with-x y)) NIL cl-user> (typep (make-bar) '(foo-with-x y)) T cl-user> (typep (make-bar) '(foo-with-x x)) NIL cl-user>
*(typep (make-foo :x 42) '(foo-with-x 42))*
I.e., there is no way to pass the *x* to *foo-with-x-p*, is there?
Nope. foo-with-x-p ie. the function given to satisfies must be the name of a predicate of the whole object.
For something like: (typep (make-foo :x 42) '(foo-with-x 42)) you'd have to do:
cl-user> (deftype foo-with-x (value) `(satisfies ,(intern (with-standard-io-syntax (format nil "FOO-WITH-X=~S-P" value))))) FOO-WITH-X cl-user> (defun foo-with-x=42-p (s) (= (foo-x s) 42)) FOO-WITH-X=42-P cl-user> (typep (make-foo :x 42) '(foo-with-x 42)) T cl-user> (typep (make-foo :x 33) '(foo-with-x 42)) NIL cl-user>
Of course, what we'd want, is to generate the function or closure when the deftype is expanded. This is difficult, since we need to provide satisfies a function name, and it must be defined at compilation-time.
I had a CLRFI that proposed to have lambda in satisfies, and that shows an example of what has to be done without a lambda. Cf. attachments.
Note: this clrfi is not completely thought out, since for forms such as:
(deftype restricted-list (element-type) `(and (satisfies proper-list-p) (satisfies ,(lambda (list) (every (lambda (item) (typep item ',element-type)) list)))))
there is no closure (we build a new lambda expression for each use of the type.
If we allowed (satisfies (lambda (...) ...))) we'd have to indicate in what lexical environment the lambda expression would be evaluated (assumedly, the lexical environment of the deftype body). I have not thought about the consequences of that.
-- __Pascal Bourguignon__