[slime-devel] Re: Clicky things in the REPL.

Alan Ruttenberg alanralanr at comcast.net
Wed Dec 17 21:43:13 UTC 2003


I implemented something along these lines for MCL a while back and have 
been thinking of porting it to
openmcl. I ran into a number of issues that you've been discussing so I 
thought I'd share my decisions of the time.

My implementation was text based, relying on a reader macro to be able 
to read "unreadable" objects.  Since I wasn't using ascii i used #<= 
(where the <= was one character) so that the things printed looked 
almost  the same as they would normally. I also had a mouse click that 
would copy a pointed to lisp object (as text) to the input line. So you 
could C-click on one of these then hit return in the listener and voila 
- you had your object. I was thinking of using #<^ as the prefix where 
I have to stick to ascii. So one of these objects looks like this: 
#<^buffer-mark #x77FF3E>. One of the advantages of working with just 
text as the representation is that the method still worked when people 
would print things to strings before subsequently printing them to 
their final destination stream (does pretty-printing do this).

I implemented this for any unreadable object that used :identity t, 
meaning that it printed out its address as part of the representation 
and hooked into the system by overriding the implementation of 
print-unreadable-object. In addition, I redefined a number of 
print-object methods to ensure that they used print-unreadable-object 
and :identity t (instead of (format "#<..." ...)).

The address was used as a key to the object in a weak hash table. If 
you used one of these things after its object had gced then it 
evaluated to :already-gced. Conceivably you could control this 
behaviour (hold on to printed objects strongly or weakly) with a 
special variable.

If an object moves during gc then referencing by the old address was 
fine. The next time it printed it would be  saved using the new address 
as a key. In the case where an address of some object was the same as  
where some previously printed object was printed, I changed the printed 
representation of the new object so  it was unique, e.g. 
#<^binary-stream #x77FF3E-1>

Because stack objects are likely to be invalid soon, I used a low level 
call to check if the object I was about to print was stack allocated 
and didn't use this scheme if so.

Because people sometimes write print methods that have unreadable 
objects as part of their printed representation, the reader had to make 
sure it skipped over such things, e.g. #<^buffer-mark for #<^editor 
#x55DEFF> #x7788EE>

Of course you can do much more with presentations, but they are more 
work to implement. On the other hand it is conceivable that you could 
take the code in McClim and write a different backend for emacs and so 
benefit from their effort.

This doesn't work for arrays, structures, etc, or if the user writes 
their own print-object method that doesn't used 
print-unreadable-object.

On the other hand it is fairly simple to implement and helped a 
debugging immensely.

---

Looking forward I was thinking about other strategies. Here's one:

Wrap print-object with a method that makes a structures that have as 
one of their slots the textual representation that print-object would 
normally have written. Give these objects unique serial numbers 
(incrementing counter whenever one is printed). Keep a weak hash table 
mapping these ids to the original objects. Have the default print 
method simply spit out the stored textual representation. If you do 
this then I think everything looks the same as it does before.

Now make the print method for these things be dependent on a dynamic 
variable *print-clickably* or somesuch. When that variable is t the 
print method prints out something that when read evaluates to the 
object, using some newly defined reader macro. This thing looks 
something like #<^127>, for instance.

The process filter looks for these things and requests back to the lisp 
for the textual representation (on a dedicated stream/process) of the 
object, and prints a propertized version of that, which now can respond 
to clicks. Before passing text back to the lisp it finds these regions 
and substitutes back the #<^127> representation so they can be read and 
used.

I think this lets you read many more objects that my previous scheme 
could read, and gives the user some control, via *print-clickably*, 
over whether to use the mechanism. There might be some issues with 
pprint, if it does printing to measure how wide some object would be 
(underestimating the width).

-Alan

On Dec 17, 2003, at 2:25 PM, Brian Downing wrote:

> On Wed, Dec 17, 2003 at 04:22:13PM +0100, Luke Gorrie wrote:
>> Brian Downing <bdowning at lavos.net> writes:
>>> One question I would have is, how is the object accessed once it's
>>> clicked on?  I imagine CLIM just holds a (weak?) pointer to the 
>>> object
>>> in its presentation.  From emacs, you may see "#<FOO {40541D01}>", 
>>> and
>>> even know the address more concretely if its passed in some sort of
>>> object-info, but as soon as GC happens, you're likely screwed.
>>
>> If CLIM used weak references, it would have the same trouble. I would
>> guess they use normal references and normal GC, but I don't know.
>
> Not the way I was thinking about it.  What I meant is that the GC would
> probably move the object, not collect it, and if your reference to it
> was based on address, it would be broken even though the object still
> existed.
>
> Weak references make sense to me, even for CLIM, because I really
> wouldn't want all the objects I've created in the REPL to persist just
> because their presentations still exist in my scrollback.  That
> completely changes the semantics of working at the REPL.  Maybe 
> somebody
> who has worked on the LispM could explain what semantics presentations
> in its listener had with respect to garbage collection.
>
> If I print a piece of data that is part of a larger structure, and not
> garbage, I'd want the presentation to still be able to access it after
> it has been moved by the GC.  But I'd also like to be able to do
> (progn (print (make-instance 'two-hundred-megabyte-object)) nil)
> at the REPL and not saddle the GC with any limitations on how to 
> collect
> this data that nobody else is using, like waiting for it to "time out"
> or having another 200 presentations created after it.
>
> All IMHO, of course.
>
> -bcd
>
> _______________________________________________
> slime-devel site list
> slime-devel at common-lisp.net
> http://common-lisp.net/mailman/listinfo/slime-devel





More information about the slime-devel mailing list