libera/#commonlisp - IRC Chatlog
Search
15:42:42
gilberth
I'm having a question about ~A. In 22.3.4.1 it says "If arg is a string, its characters will be output verbatim." <https://novaspec.org/cl/22_3_Formatted_Output#sec_22_3_4_1> However of the Lisp implementation that I have all but CLISP and ECL invoke *print-circle* processing with ~A, even when the argument is a string. I get <https://termbin.com/bv53>
15:44:12
gilberth
The question is: Is it me reading the spec in a too naïve way, or is it the Lisp implementations being buggy here. Perhaps just saying PRINC with ~A.
15:58:07
splittist
Aren't the implementations differing in how (many times) PRINT-OBJECT is called, not in how ~A works?
15:59:48
gilberth
This issue concerns me a bit. For two reasons, at times I am not brave enough for FORMAT and punt like (format stream "~S is ~A integer." x (if signed "a signed" "an unsigned")) This breaks with *print-circle*. The other is: You often see idioms like (format nil "~{~A~^, ~}" strings) to join a list of strings by commas. The latter also would break.
16:01:03
gilberth
splittist: The question is whether PRINT-OBJECT is invoked on ~A an argument at all, when that argument is a string.
16:02:21
gilberth
That is whether pprint dispatch happens, whether *print-circle* processing happens. Or whether to take the spec verbatim and just invoke write-string with ~A when the argument is a string. Which is how I read that. But I'm not confident here, which is why I ask.
16:05:40
gilberth
And all those uses of FORMAT to craft strings from strings, e.g to generate some external representation would need to be wrapped around with-standard-io-syntax.
16:07:12
gilberth
Let me put this question somewhat differently. Sould (format stream "~A" (the string x)) always be the same as (write-string (the string x) stream) or not?
16:08:25
gilberth
Or should it rather be the same as (let ((*print-escape* nil) (*print-readably* nil)) (prin1 (the string x) stream)) ?
16:09:42
scymtym
there seems to be a contradiction: ~A is supposed to output a string argument verbatim but ~S is supposed to be "just like ~A". i don't think both can be true since outputting strings verbatim prevents print-read consistency
16:17:44
gilberth
Anyhow, I'm at a loss here. Do I fix my code or do I fix my Lisp implementation? Who's to blame?
16:20:03
gilberth
I first came across this with (format nil "~{~A~^, ~}" strings), which given that I'm lazy, I use a lot. I needed to turn on *print-circle* and my code broke.
17:06:33
gilberth
Here is another test, this time with ~:A printing NIL. Spec says "the colon modifier (~:A) will cause an arg of nil to be printed as ()". Doesn't happen either: https://termbin.com/y06j
17:30:50
gilberth
This is funny: <https://termbin.com/ro7kb> ;Note how SBCL special cases for (format nil "~a") and does not consult the pprint dispatch, while "~{~A~^, ~}" does. So someone, somewhen with SBCL share my interpretation. At least for some special case.
17:36:45
gilberth
Yet the spec says that when the argument is a string that string should be printed verbatim. It also says that when the colon is present, NIL should print as (). This doesn't happen.
17:38:14
yitzi
I don't think that is contradiction. It is just "pre-processing" by FORMAT's directives.
17:40:27
gilberth
It is. Not both claims "is printed with PRINC" and "If arg is a string, its characters will be output verbatim" can be true. Neither can the claim about NIL be true.
17:42:22
gilberth
I read "An arg, any object, is printed without escape characters (as by princ). If arg is a string, its characters will be output verbatim." like "printed as with PRINC, but a string is printed verbatim." Likewise I read the following about ~:A printing NIL as () as an exception.
17:47:08
yitzi
After digging around in the plumbing of the printer a fair amount I have come to the conclusion that there are some odd things that were brought into the spec from the XP printers, and at the same time almost no details of the pretty printer were brought into the spec. For example, there is no statement about what is in the initial dispatch table. It is very underspecified, IMHO.
17:48:52
NotThatRPG
Anyone out there using cl-async? I am facing some code that uses it and am lost. The code invokes `cl-asyn:start-event-loop` which, as far as I can tell, should return, but instead it hangs.
17:49:29
gilberth
The pretty printer dispatch isn't what concerns me here too much. It's *print-circle* that concerns me. And the use of format to stich strings together by means of ~A. The assumption that (format nil "~{~A~}" strings) would be like (concatenate 'string strings) is true or false depending on how you read the ~A spec.
17:49:30
NotThatRPG
I mean by "hangs" if I invoke it in the REPL, it never returns, not that it literally hangs.
17:55:10
NotThatRPG
oh, I see: that code is just wrong. The event-loop won't terminate, and shouldn't.
19:19:04
gilberth
It gets funnier. https://novaspec.org/cl/v_print-circle says "If a user-defined print-object method prints to a stream other than the one that was supplied, then circularity detection starts over for that stream." Doesn't happen with SBCL, CMUCL, or ABCL. <https://termbin.com/504d>
19:28:00
yitzi
gilberth: I'd be surprised if any implementation does that. They all use a single group of dynamic variables to control it. If one implementation is different it could be CLISP. All the other implementations use either the original XP code or CMUCL's rewrite.
19:29:18
HamzaShahid
I wanted to create permutations of a list like (0 1 2) to ((0 1 2) (0 2 1) (1 0 2) (1 2 0) ...) lexecographically
19:40:41
yitzi
I'm not sure about CCL, ECL and ACL. CLASP is based on ECL it doesn't attempt make circle detection stream specific.
19:44:36
gilberth
So with CMUCL^SBCL, ECL, ABCL circle printing also is ineffective when you print somewhere else. Again CCL, CLISP, ACL getting it right. IMHO.
19:49:53
pjb
HamzaShahid: it's the same code to create permutations of 2 values as for n values! Just modify my function (trivially).
19:50:54
pjb
HamzaShahid: (defun all-symbol-combinations (p n) (if (zerop n) '(()) (let ((rests (all-bit-combinations (- n 1)))) (mapcan (lambda (bit) (mapcar (lambda (rest) (cons bit rest)) rests)) (iota p))))) (all-symbol-combinations 4 3) #| --> ((0 0 0) (0 0 1) (0 1 0) (0 1 1) (1 0 0) (1 0 1) (1 1 0) (1 1 1) (2 0 0) (2 0 1) (2 1 0) (2 1 1) (3 0 0) (3 0 1) (3 1 0) (3 1 1)) |#
19:51:41
pjb
HamzaShahid: sorry, bad renaming; here it is: (defun all-symbol-combinations (p n) (if (zerop n) '(()) (let ((rests (all-symbol-combinations p (- n 1)))) (mapcan (lambda (bit) (mapcar (lambda (rest) (cons bit rest)) rests)) (iota p))))) (all-symbol-combinations 4 3) #| --> ((0 0 0) (0 0 1) (0 0 2) (0 0 3) (0 1 0) (0 1 1) (0 1 2) (0 1 3) (0 2 0) (0 2 1) (0 2 2) (0 2 3) (0 3 0) (0 3 1) (0 3 2) (0 3 3) (1 0 0) (1 0 1) (1 0 2) (1 0 3) (1 1
19:51:41
pjb
0) (1 1 1) (1 1 2) (1 1 3) (1 2 0) (1 2 1) (1 2 2) (1 2 3) (1 3 0) (1 3 1) (1 3 2) (1 3 3) (2 0 0) (2 0 1) (2 0 2) (2 0 3) (2 1 0) (2 1 1) (2 1 2) (2 1 3) (2 2 0) (2 2 1) (2 2 2) (2 2 3) (2 3 0) (2 3 1) (2 3 2) (2 3 3) (3 0 0) (3 0 1) (3 0 2) (3 0 3) (3 1 0) (3 1 1) (3 1 2) (3 1 3) (3 2 0) (3 2 1) (3 2 2) (3 2 3) (3 3 0) (3 3 1) (3 3 2) (3 3 3)) |#
19:53:30
pjb
HamzaShahid: since it can grow fast, you may want to do it incrementally. have a look at: https://gitlab.com/com-informatimago/com-informatimago/-/blob/master/common-lisp/cesarum/combination.lisp
20:18:42
gilberth
So it looks like SBCL abandoms *print-circle* processing when doing output to another stream altogether, but for the top-level object printed to that other stream. I can see why they have chosen to do so. Watch <https://termbin.com/y6fs>. This time using ~S. When asking for ~20S however, CCL fails to do *print-circle* processing because it first prints the argument to a string.
20:19:43
gilberth
However, completely abandoning *print-circle* may make you face some infinite recursion. I don't like that.