Closed digikar99 closed 3 years ago
Using an uninterned symbol as a function name is very unusual. But assuming you really need to do this try wrapping the first line in an eval-when and try again:
(eval-when (:compile-toplevel :load-toplevel :execute)
(defparameter gs1 (gensym)))
There is no real difference in CCL between evaluation and compilation (everything in CCL is compiled) other than the binding environments in place at the time. That's why eval-when is probably the solution here.
If you are new to Common Lisp I'd gently suggest that using an uninterned symbol as a function name is a bad idea, and most likely an anonymous function is what you want instead.
The issue here is that you can't have anonymous satisfies
functions and a gensymed function is the closest you can do to emulate such behavior.
Yes indeed you're right. I had forgotten about that.
If you mean something like this:
(eval-when (:compile-toplevel :load-toplevel :execute)
(defparameter gs1 (gensym)))
(defun #.gs1 (a) (declare (ignore a)) t)
(deftype foo () `(satisfies #.gs1))
(defun bar11 (a) (typep a 'foo))
then, the results are the same - if I C-c C-c
each form one-by-one then I get the same error; if I C-x C-e
each form one-by-one, I do get the expected T
.
Hmm, a bit more pinpointing, again, each form, one-by-one (I guess without that there can be a separate read/compile error due to #.
, but that's a separate issue as far as I understand):
(defparameter gs1 (gensym))
(defun #.gs1 (a) (declare (ignore a)) t)
(fdefinition gs1) ;=> undefined function if previous form was C-c C-c; works fine if it was C-x C-e
an uninterned symbol as a function name is a bad idea, and most likely an anonymous function is what you want instead.
I'm looking to emulate (container element-type)
using satisfies
- and gensyms. Not sure if there's a better way.
I emulate the same thing using named predicates interned in a separate scratch package in phoe-toolbox.
(defpackage #:phoe-toolbox/list-of (:use))
(deftype list-of (type)
"A type specifier that matches a list whose all elements are of the given
type."
(check-type type symbol) ; Kludge - maybe we'll fix that later.
(let* ((package-name (package-name (symbol-package type)))
(symbol-name (symbol-name type))
(predicate-name (format nil "LIST-OF ~S ~S" package-name symbol-name))
(package (find-package '#:phoe-toolbox/list-of))
(predicate (intern predicate-name package)))
(setf (fdefinition predicate)
(lambda (list) (every (rcurry #'typep type) list)))
`(satisfies ,predicate)))
Because this is somewhat dirty, I'm also curious if you come up with a portable solution that uses gensyms.
I'd probably address this problem with #'assert.
(defun foo (a)
(assert (and (typep a 'array)
(eql 'single-float (array-element-type a))))
(print "Success!"))
(setq x (make-array '(3 5) :element-type 'single-float))
(foo x) --> Success!
(setf y (make-array 10))
(foo y) --> error
Of course the disadvantage of #'assert is that it's purely a runtime check, so the compiler won't be able to reason about it in advance. But if you're checking types in a container when that container is a list (as opposed to an array), no Lisp compiler will be able to reason about it anyway, i.e. it's always going to involve a runtime check.
Because this is somewhat dirty, I'm also curious if you come up with a portable solution that uses gensyms.
I don't get how that hampers portability; may be this is a bit more portable, with trivial-types:type-expand
and caching things in a hash-table; not sure:
(deftype array (&optional (element-type '* elt-supplied-p))
(if elt-supplied-p
(let ((element-type (type-expand (upgraded-array-element-type element-type))))
(unless (gethash element-type *element-type->checker-fn*)
(let ((fn-sym (make-symbol (concatenate 'string
"ARRAY-"
(let ((*package* (find-package :cl)))
(write-to-string element-type))))))
(compile fn-sym
`(lambda (array)
(and (arrayp array)
(type= ',element-type (array-element-type array)))))
(setf (gethash element-type *element-type->checker-fn*) fn-sym)))
`(satisfies ,(gethash element-type *element-type->checker-fn*)))
'dense-array))
About caching in the hash-table, seeing your example I recalled that SBCL only seems to expand the types once. Even CCL does it just once; however, just checked, ECL does it as many times as needed; not sure what CLHS says about this.
The array
here is in a different package and is intended as a replacement; but that's another separate issue.
Of course the disadvantage of #'assert is that it's purely a runtime check ...
Indeed! However, I also intend to provide some compiler-macros that's take advantage of the user-provided declarations in optimizing user-provided code - or signalling to the user why their code cannot be optimized. On SBCL, I'll need to check to what extent sb-c:deftransform
can do it; is there an equivalent of sb-c:deftransform
on CCL?
Also, this seems to be getting off-topic; may be we should continue at some other place, eg. https://www.reddit.com/r/lisp/comments/k6sjbm/monthly_whats_on_your_mind_discussion_thread/?
The main issue is that gensyms are not externalizable while maintaining identity. If you store a gensym into a FASL and then load the FASL back, then the resulting gensym is going to be similar but not identical to the original, meaning that all eq
checks - including funcalls of the previous gensym - will fail.
You could try avoiding this issue by using make-load-form
for the symbol instead, or a hash table at runtime, but AFAIK this is not a CCL-specific issue because the original code is not guaranteed to work portably.
I'm on
(lisp-implementation-version) ;=> "Version 1.12 (v1.12) LinuxX8664"
.#.
C-c C-c
andC-x C-e
refer to the slime commands slime-compile-defun and slime-eval-last-expression.POSSIBLE ISSUE 1
POSSIBLE ISSUE 2
The previous issue can be avoided by using
compile
(thanks to phoe for the reminder!). So,Backtrace:
Other similar functions, and those that do not involve gensyms work fine regardless of C-c C-c or C-x C-e: