freenode/#sicl - IRC Chatlog
Search
12:06:57
beach
Here is an interesting idea. Since I can write (defclass symbol (t) ((%name .. :reader symbol-name) (%package ... :reader symbol-package)) (:metaclass built-in-class)), it might be desirable to define those slot readers to be a special kind of generic function that would allow only a single reader method. Not that it matters much, but it's a cute idea.
12:15:35
beach
I can imagine a system implementer who wants symbol-name to behave like an ordinary function, i.e. disallow auxiliary methods on it.
12:18:46
beach
... namely that you are not allowed to define methods that are applicable to only standardized classes.
12:22:44
heisig
The problem of allowing subclasses of built-in-classes is that it tempts users to write code that is not portable across implementations.
12:26:33
heisig
You may be right. It is really nice to actually have the choice whether to allow subclasses of built in classes or not :)
12:28:34
beach
I guess my idea came from the excitement about using the CLOS machinery for built-in classes.
12:31:43
beach
Heh: (declass null (symbol list) () (:metaclass built-in-class) (:default-initargs (:name "NIL" :package (find-package '#:common-lisp))))
12:36:13
heisig
You know it is 2018 when you see :default-initargs for the class NULL. This is truly amazing!
12:38:33
heisig
I am just trying to figure out how an implementor could then use the old tricks of encoding NIL efficiently. But I am sure it can be done (using a subclass like client-package::built-in-null-class?).
12:41:48
heisig
Maybe you want to encode NIL as a hybrid object that is also a cons with itself in its car and cdr. To speed up cl:car and cl:cdr.
12:43:49
beach
Besides, I break down CAR and CDR into (defun car (object) (if (consp object) (primop:car object) (if (null object) object (error ...))))
12:46:26
beach
That "optimization" might have been worthwhile in very old code where it was common to rely on the fact that (CAR NIL) => NIL and (CDR NIL) => NIL. I seriously doubt that is common these days.
12:48:23
heisig
Comparing these techniques sounds like an excellent addition for my benchmarking library. I should write such a benchmark...
12:49:36
beach
I should add that stassats did a benchmark for SBCL (not sure about the details) and concluded (of course) that the fact that SBCL requires two tests per iteration when traversing a list had negligible performance impact.
12:51:11
beach
It is interesting, though, that requiring two tests is a direct result of that "optimization" for NIL.
12:56:23
beach
For those who don't see why: Suppose you want to traverse a list and you want to check for the end of the list. You can test for the list being EQ to NIL if you like. If it is, it's the end of the list. But suppose it is not NIL. Then it might be a CONS or some other object (an improper list). Only if it is a CONS are you allowed to take the CAR, so you have to make a second test.
12:57:43
beach
Without that optimization, you would just test for CONSP first. Then you would be sure to have a CONS (which is not the case WITH that optimization), and you can safely take the CAR, and that will be the case for all elements except the last one.
12:58:08
beach
If you don't have a CONS, you may have to make a second test, but that will be necessary only at the end of the list.
12:59:47
beach
In summary, then, that "optimization" introduces lots of special cases in the code, and it makes list traversal slower.
13:17:01
jcowan
CAR NIL = CDR NIL = NIL was a foreign element introduced as a sop to the Interlispers, who did not appreciate it much.
13:18:06
jcowan
in Interlisp-D, NIL was at address 0, and was a two-word object both containing 0, so it only had to be special-cased for symbol ops
13:48:38
pfdietz
I have grown cold on NIL punning. For that matter, I've grown cold on improper lists.
13:50:31
pfdietz
I put in a bug report for SBCL, where ELT with indexes far beyond the end of the list takes a very long time to signal the error. Why:? Because the loop is not checking for NIL, except after the index counts down to zero.
13:57:44
beach
Yes, I think it is safe to assume that very little modern code relies on (CAR NIL) => NIL.
14:16:34
beach
I think I should document what several operators "mean" in the different bootstrapping environment. Like what does it "mean" to call MAKE-INSTANCE in each environment? What classes are acceptable to MAKE-INSTANCE in that environment? Where does it look up its class if a symbol is given? Same thing for other operators that look things up, like ENSURE-CLASS, ENSURE-GENERIC-FUNCTION, etc.
14:17:14
beach
By having those written down, whenever I get confused, I can just read the description, rather than trying to figure out the information.
14:31:00
jcowan
I thought of another application of improper lists besides lazy sequences: a poor man's persistent hash table consisting of an a-list with a hash table in the tail.
14:31:50
jcowan
you initialize the hash table with your "global" keys and values, and then wrap it in a cons and treat it as a normal hash table, aconsing new keys and values as needed