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'!*
*(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?
All the best
MA
... ok. Sorry.
CURRY does it.
MA
On Wed, Feb 21, 2024 at 10:10 PM Marco Antoniotti < marco.antoniotti@unimib.it> wrote:
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'!*
*(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?
All the best
MA
-- Marco Antoniotti, Professor tel. +39 - 02 64 48 79 01 DISCo, Università Milano Bicocca U14 2043 http://dcb.disco.unimib.it Viale Sarca 336 I-20126 Milan (MI) ITALY
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.
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__
Assuming CURRY1 returns a function, then that is not portable. The predicate-name given to SATISFIES is expected to be a symbol with a global function definition and there is no way to pass extra arguments.
LispWorks TYPEP is not checking for this (but SUBTYPEP does).
I was afraid of that.
Another wart in the CLHS. Even if the Type Police will tell me that "it's for my own good". :)
Cheers -- MA
On Thu, Feb 22, 2024 at 12:38 PM Martin Simmons martin@lispworks.com wrote:
Assuming CURRY1 returns a function, then that is not portable. The predicate-name given to SATISFIES is expected to be a symbol with a global function definition and there is no way to pass extra arguments.
LispWorks TYPEP is not checking for this (but SUBTYPEP does).
-- Martin Simmons LispWorks Ltd http://www.lispworks.com/
On Thu, 22 Feb 2024 11:09:02 +0100, Marco Antoniotti said:
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__
Haha, you guys remind me of what we used to do at ITA, when we dynamically defined types for strings of known size (exact, up to max, or arbitrary interval) and use that for our ORM.
We tried to define the predicate functions directly in the deftype... except (1) the deftype can be expanded within an arbitrarily deep scope, the wrong place to expand into a function definition. (2) the deftype is never guaranteed to be expanded at either compile-time or runtime, and can be expanded many times, so it is the wrong place to define the function by side-effect, especially if you want it compiled. (3) the side-effects from deftype will NOT be persisted into the FASL anyway, so next time you build your system and load the FASL, the function will be referenced but not defined. Oops.
At first, we used the discipline of requiring programmers to manually check that their functions are defined, if not, define them by hand, and de-duplicate by hand also.
Eventually, I took over ASDF, added the :around-compile hook, could implement the asdf-finalizers extension, and then the deftypes would be able to add forms to FINAL-FORMS in eval-when's as well as evaluating them immediately, and all the developer would have to do is write (expand-final-forms) at the end of the file—and the compiler would warn him if he needed to and failed to do it. See the list-of example in asdf-finalizers.
Sadly, QRes was discontinued by Google not long afterwards. Sad. The infrastructure (QUUX and the ORM, QUAKE) was eventually open-sourced, but remains unmaintained with few bits scavenged.
On Wed, Feb 21, 2024, 17:45 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__