eliot@phoenix.Princeton.EDU (Eliot Handelman) (08/04/88)
I notice that Steele comments, on pg 370: "It is specifically and purposely NOT required that a CL implementation be able to print an object of type hash-table, readtable, package, stream or function in a way that can be successfully read back in by read; the use of #< syntax is especially recommended for the printing of such objects." What's the motive behind this, or is there one? Isn't one of the main things about lisp the uniformity of data and procedures?
barmar@think.COM (Barry Margolin) (08/04/88)
In article <3387@phoenix.Princeton.EDU> eliot@phoenix.Princeton.EDU (Eliot Handelman) writes: >"It is specifically and purposely NOT required that a CL implementation >be able to print an object of type hash-table, readtable, package, stream or >function in a way that can be successfully read back in by read; the >use of #< syntax is especially recommended for the printing of such objects." > >What's the motive behind this, or is there one? Mostly it is because the Lisp implementations that Common Lisp was derived from didn't print these things readably, and there was no strong reason to make them readable. There has been some recent discussion about defining a readable printed representation for hash tables, though. In general, though, printed representations are defined for objects that a user might be likely to type in manually, or which might be written to a text file and read back in in another session. Neither is likely for a stream object, and users are not likely to want to type in the others. They are frequently quite large and/or complex; for example, the printed representation of a package would have to include all the symbols in the package, and the printed representation of a stream would have to include all its state (some of which would be obsolete if you read it in after doing some I/O to the stream). A compiled function's printed representation would have to be designed so that it could be linked in properly when read back in. If you were to type in the printed representation of a stream connected to a file that is not currently open, would it open the file? >Isn't one of the main things >about lisp the uniformity of data and procedures? What does the uniformity of data and procedures have to do with printed representations? The point of the uniformity feature is that a program can construct other programs and then execute them, i.e. procedures (usually lambda expressions) are made out of the same stuff that data is. Why do you think that this implies that you should be able to type in objects of all data types? Barry Margolin Thinking Machines Corp. barmar@think.com {uunet,harvard}!think!barmar
eliot@phoenix.Princeton.EDU (Eliot Handelman) (08/05/88)
In article <24911@think.UUCP> barmar@kulla.think.com.UUCP (Barry Margolin) writes: >In article <3387@phoenix.Princeton.EDU> eliot@phoenix.Princeton.EDU (Eliot Handelman) writes: >>"It is specifically and purposely NOT required that a CL implementation >>be able to print an object of type hash-table, readtable, package, stream or >>function in a way that can be successfully read back in by read; the >>use of #< syntax is especially recommended for the printing of such objects." [some discussion concerning print representations of streams, hash-tables and compiled functions.] Barry, I agree that I, personally, can't think of any reason why I would want to read the print representation of a stream, but maybe somebody else could. My problem is not having access to certain types of objects, like lambda-bindings. >>Isn't one of the main things >>about lisp the uniformity of data and procedures? >What does the uniformity of data and procedures have to do with >printed representations? The point of the uniformity feature is that >a program can construct other programs and then execute them, i.e. >procedures (usually lambda expressions) are made out of the same stuff >that data is. Why do you think that this implies that you should be >able to type in objects of all data types? The problem is reading a lambda expression, or treating it as a piece of data, not what gets printed out when I type in a definition. In KCl, for example, I can treat #'foo as a list. I haven't found out how to do so in Lucid, which uses #< syntax, because lambda-bindings are treated as objects of type procedure. I think you'll agree that occassionaly you might like to look at a definition from the interpreter, particularly if it had been written by some other procedure. Or you may even want to print the definition to file. If the lambda-binding can be treated as a list, even if it's been syntactically altered (making the car of the list LAMBDA-BLOCK, for example), I can always hack it so that it becomes readable, and either look at it or whatever. But #< does not allow me to touch it. Here is a fabricated example that ought to show just what I mean, in Franz Lisp, KCl, and a #< syntax lisp. The problem is to replace the global 'DOG with 'CAT in a procedure called DOG. Franz Lisp, Opus 38.79 -> (defun dog () 'DOG) dog -> (putd 'dog (subst 'CAT 'DOG (getd 'dog))) (lambda nil 'CAT) -> (dog) CAT ---- KCl (Kyoto Common Lisp) June 3, 1987 > (defun dog () 'dog) DOG > #'dog (LAMBDA-BLOCK DOG () 'DOG) > (defun getd (f) `(lambda ,@(cddr (symbol-function f)))) GETD > (getd 'dog) (LAMBDA () 'DOG) > (setf (symbol-function 'dog) (subst 'CAT 'DOG (getd 'dog))) (LAMBDA () 'CAT) > (dog) CAT ---- The next example is from a lisp that uses #< syntax. > (defun dog () 'dog) DOG > #'dog #<Interpreted-Function (NAMED-LAMBDA DOG NIL (BLOCK DOG 'DOG)) 4BF8D7> > (cdr #'dog) >>Error: #<Interpreted-Function (NAMED-LAMBDA DOG NIL (BLOCK DOG (QUOTE DOG))) 4BF8D7> should be of type LIST ---- Of course, the compiler, at least, has to be able to treat #'dog as a readable object (I assume). That means that in some way it is readable, and that I'm arbitrarily being prevented from having it read by one of my own procedures, or by knowing what secret the compiler uses to get at this data. - Eliot
pierson@encore.UUCP (Dan Pierson) (08/06/88)
In article <3394@phoenix.Princeton.EDU> eliot@phoenix.Princeton.EDU (Eliot Handelman) writes: >The problem is reading a lambda expression, or treating it as a piece of >data, not what gets printed out when I type in a definition. In KCl, >for example, I can treat #'foo as a list. I haven't found out how to do >so in Lucid, which uses #< syntax, because lambda-bindings are treated >as objects of type procedure. There are two issues here: 1. Must interpreted functions be represented as lists beginning with the symbol LAMBDA (or NAMED-LAMBDA, or ...)? The incipient "official" answer is no. X3J13 has just voted to require that objects that are subtypes of FUNCTION not also be subtypes of LIST (among other types). This is part of a larger proposal to clean up the meaning of the type FUNCTION. 2. Should users and programs be able to access the source code of interpreted (and maybe compiled) functions? The answer is clearly yes. The X3J13 cleanup subcommittee is discussing a proposal to do this. Lucid supports such access with the function SOURCE-CODE as shown below. >The next example is from a lisp that uses #< syntax. > >> (defun dog () 'dog) >DOG > >> #'dog >#<Interpreted-Function (NAMED-LAMBDA DOG NIL (BLOCK DOG 'DOG)) 4BF8D7> > >> (cdr #'dog) >>>Error: #<Interpreted-Function (NAMED-LAMBDA DOG NIL (BLOCK DOG (QUOTE DOG))) 4BF8D7> should be of type LIST Alternatively: > (defun dog () 'dog) DOG > #'dog #<Interpreted-Function (NAMED-LAMBDA DOG NIL (BLOCK DOG 'DOG)) 4BF8D7> > (cdr (source-code #'dog)) (DOG NIL (BLOCK DOG (QUOTE DOG))) -- In real life: Dan Pierson, Encore Computer Corporation, Research UUCP: {talcott,linus,necis,decvax,ihnp4}!encore!pierson Internet: pierson@multimax.arpa
bouma@cs.purdue.EDU (William J. Bouma) (08/06/88)
In article <3394@phoenix.Princeton.EDU> eliot@phoenix.Princeton.EDU (Eliot Handelman) writes: >In article <24911@think.UUCP> barmar@kulla.think.com.UUCP (Barry Margolin) writes: >>In article <3387@phoenix.Princeton.EDU> eliot@phoenix.Princeton.EDU (Eliot Handelman) writes: [ junk about being able to read all Lisp objects deleted ] I agree with barmar that there is a bunch of Lisp data structures which should never need to be printed/read by any normal lisp user. Typing in hash tables does not sound practical or appealing. But that doesn't seem to be the real issue here: > >The problem is reading a lambda expression, or treating it as a piece of >data, not what gets printed out when I type in a definition. In KCl, >for example, I can treat #'foo as a list. I haven't found out how to do >so in Lucid, which uses #< syntax, because lambda-bindings are treated >as objects of type procedure. > If you want to treat a lambda list as a piece of data, make it one explicitely: (setq plusl '(lambda (x y) (+ x y))) Once you allow whatever interpreter you are using to get hold of it, there is no guarentee that the internal representation it uses will make any sense to a human: (setq plusf #'(lambda (x y) (+ x y))) I believe a funcall on either of these will work. But the price you pay is slower execution speed for plusl since it first has to be converted into the internal rep before it can be run. Of course the Lisp you are using could keep around the original lambda list that the function came from and just print that out when you asked to print the function. Or it could even have a "de-interpreter" that changes the internal format back to one it can print. But there is nothing in lisp that says it HAS to do this. >I think you'll agree that occassionaly you might like to look at a definition >from the interpreter, particularly if it had been written by some other >procedure. Or you may even want to print the definition to file. If >the lambda-binding can be treated as a list, even if it's been syntactically >altered (making the car of the list LAMBDA-BLOCK, for example), I can >always hack it so that it becomes readable, and either look at it or whatever. >But #< does not allow me to touch it. Here is a fabricated example that Again, what you want is some kind of de-compiling tool. KCL either just keeps its functions in this list format or does a de-compiling for you when it prints it out. If you want to have functions that modify other functions you should keep them as lambda LISTs. Why would you want to modify someone elses function at such a low level? Try modifying the source code. >Of course, the compiler, at least, has to be able to treat #'dog as a readable >object (I assume). That means that in some way it is readable, and that I'm >arbitrarily being prevented from having it read by one of my own procedures, >or by knowing what secret the compiler uses to get at this data. It is no secret, and it is only arbitrary in the sence that any lisp implementation is free to this its own way. The compiler can probably read the internal representation and make sense out of it. This is something you, being a mere mortal, cannot yet do. -- Bill <bouma@medusa.cs.purdue.edu> || ...!purdue!bouma
barmar@think.COM (Barry Margolin) (08/07/88)
In article <3394@phoenix.Princeton.EDU> eliot@phoenix.Princeton.EDU (Eliot Handelman) writes: >The problem is reading a lambda expression, or treating it as a piece of >data, not what gets printed out when I type in a definition. In KCl, >for example, I can treat #'foo as a list. I haven't found out how to do >so in Lucid, which uses #< syntax, because lambda-bindings are treated >as objects of type procedure. First of all, you are confusing two completely unrelated concepts. The fact that some types are printed with #< syntax has absolutely nothing to do with whether there are mechanisms for accessing the components of the object. Hash tables print with #< syntax, but you can still access the contents. And there are types with readable printed representations that DON'T allow you to access the contents (RANDOM-STATE is the one I can think of). >Franz Lisp, Opus 38.79 >-> (defun dog () 'DOG) >dog >-> (putd 'dog (subst 'CAT 'DOG (getd 'dog))) >(lambda nil > 'CAT) >-> (dog) >CAT And what do you expect this to do if DOG is compiled before the PUTD? One of Common Lisp's goals was to define a language that produces equivalent programs whether they are compiled or interpreted. The implementations that use an special type for interpreted functions are actually helping you out, since the interpreter will catch an error that might have remained unnoticed until you tried the program compiled. In fact, it might not even be caught in compiled code, because the compiled code for SUBST might not do any type checking; you might just dump core or something. Even among implementations that store interpreted definitions as lists in the function cell there can be much variety. On a Symbolics machine, DEFUN will store a list beginning with SI:DIGESTED-LAMBDA, and the actual lambda expression will be just one element. There is currently no portable way to access the interpreted definition of a function. If you have programs that construct interpreted definitions and then want to be able to access these definitions, you should have them store them somewhere else in addition to (or instead of) the function cells of symbols. The function cell is not a general-purpose storage location. Barry Margolin Thinking Machines Corp. barmar@think.com {uunet,harvard}!think!barmar
eliot@phoenix.Princeton.EDU (Eliot Handelman) (08/07/88)
In article <25254@think.UUCP> barmar@kulla.think.com.UUCP (Barry Margolin) writes: [I bring up the problem of getting at a lambda-binding in #< lisps.] >First of all, you are confusing two completely unrelated concepts. >The fact that some types are printed with #< syntax has absolutely >nothing to do with whether there are mechanisms for accessing the >components of the object. Hash tables print with #< syntax, but you >can still access the contents. And there are types with readable >printed representations that DON'T allow you to access the contents >(RANDOM-STATE is the one I can think of). That's absolutely true. The difference between hash tables and procedures is that Steels tells me how to access hash tables and doesn't indicate how I can access lambdas. I don't care how an object prints. But I have to know how to read it. All that I'm saying is that I think it's contrary to the nature of lisp to decide who's going to use it, and why, and what they're going to do with it. My code may be very idiosyncratic, but so what? So what if I do things in a way that earlier lisps with greater flexibilty allowed, but all of a sudden not CL? I happen to like walking code -- what's wrong with that? >>Franz Lisp, Opus 38.79 >>-> (defun dog () 'DOG) >>dog >>-> (putd 'dog (subst 'CAT 'DOG (getd 'dog))) >>(lambda nil >> 'CAT) >>-> (dog) >>CAT > >And what do you expect this to do if DOG is compiled before the PUTD? Steele says of DISASSEMBLE: "This ... is often of use to the novice who wishes to understand the workings of compiled code." It's interesting that CL forsaw having a function whose use was purely pedadogical. (He also says that this is useful for debugging the compiler, but that alone wouldn't justify putting the function in USER.) If I may answer your question quite modestly -- if DISSASSEBLE exists then GRINDEF (or whatever) ought to be made public too, if only for purely pedagogical reasons. I think this point is too important to overlook. Winston and Horn have their M-LISP interpreter at the end of their book. Can you write decent lisp code without *really* understanding how (say) the read-eval-print loop works? Isn't there a near isomorphy between lisp implementors and lisp programmers? To answer your question directly, I don't try to walk compiled code, but I see that as my problem. Why is a system that can analyze what another lisp system would do IF it were run inconceivable? I think you SHOULD be able to analyze compiled code, too. >One of Common Lisp's goals was to define a language that produces >equivalent programs whether they are compiled or interpreted. Why, was it ever the case that compiled programs behaved differently from interpreted? What lisp in particular? >The >implementations that use an special type for interpreted functions are >actually helping you out, since the interpreter will catch an error >that might have remained unnoticed until you tried the program >compiled. In fact, it might not even be caught in compiled code, >because the compiled code for SUBST might not do any type checking; >you might just dump core or something. Ok, but I don't see what this has to do with #< notation. >Even among implementations that store interpreted definitions as lists >in the function cell there can be much variety. On a Symbolics >machine, DEFUN will store a list beginning with SI:DIGESTED-LAMBDA, >and the actual lambda expression will be just one element. There is >currently no portable way to access the interpreted definition of a >function. That's true. Point is, what difference should it make what you call the first element, LAMBDA-BLOCK, NAMED-LAMBDA, DIGESTED-LAMBDA or whatever? Why not push for standardization? I mean, everybody has already agreed on the lambda part, which is of no more than historical significance, anyway. >If you have programs that construct interpreted definitions and then >want to be able to access these definitions, you should have them >store them somewhere else in addition to (or instead of) the function >cells of symbols. Of course I could do that, but it's ugly. I think what that leads to is writing my own interpreter, with my own version of anything that does surgery, so that I can keep an accessible copy around. Then if I want to make changes, I have to make twice as many as before. It's a trivial problem, but it's an improvised solution to somthing that ought to handled more generally. >The function cell is not a general-purpose storage location. A procedure is a piece of data, whether or not it has its own special set of accessors. Besides which, I would imagine that this view is anathema to your company's ideology, that of the identity of the processor and memory. >Barry Margolin >Thinking Machines Corp. Eliot Handelman Dep't of Music Princeton University
barmar@think.COM (Barry Margolin) (08/09/88)
In article <3425@phoenix.Princeton.EDU> eliot@phoenix.Princeton.EDU (Eliot Handelman) writes: >That's absolutely true. The difference between hash tables and procedures >is that Steels tells me how to access hash tables and doesn't indicate >how I can access lambdas. This is because of the differences in purposes of the two data types. The purpose of a hash table is to store data, so there must obviously be a way to extract that data. The purpose of a procedure is to represent an executable program, and the primitive that is necessary for this data type is APPLY. The data that is represented by a procedure is abstract, not concrete. >All that I'm saying is that I think it's contrary to the nature of lisp >to decide who's going to use it, and why, and what they're going to do with >it. My code may be very idiosyncratic, but so what? So what if I do things >in a way that earlier lisps with greater flexibilty allowed, but all of >a sudden not CL? I happen to like walking code -- what's wrong with that? CL is not trying to be fascist, it is trying to allow Lisp implementors the flexibility they need. Some Lisp implementations don't even have interpreters (they compile everything immediately), so they never store lambda-expressions in procedure objects. Code that assumes that it can access the source code, modify it, and have that do something useful will not port to such implementations. >if DISSASSEBLE exists then GRINDEF (or whatever) ought to >be made public too, if only for purely pedagogical reasons. I agree that GRINDEF should have been included. I'm surprised that it wasn't, since it was in the predecessors to CL. However, it wouldn't have solved your problem, since GRINDEF merely PRINTS the source code, it doesn't return it in some data structure that your application can get at. >I think this point is too important to overlook. Winston and Horn have their >M-LISP interpreter at the end of their book. Can you write decent lisp code >without *really* understanding how (say) the read-eval-print loop works? >Isn't there a near isomorphy between lisp implementors and lisp programmers? I don't think you really need to know more about the read-eval-print loop than what is said in CLtL. All you need to know about DEFUN, for example, is that it puts *something* in the symbol's function cell, and when that *something* is APPLYed it will execute the specified code. What that *something* looks like is not really important when writing Lisp code, especially since it is likely to be different depending on whether the function was compiled or interpreted. >To answer your question directly, I don't try to walk compiled code, >but I see that as my problem. Why is a system that can analyze what another >lisp system would do IF it were run inconceivable? I think you SHOULD be able >to analyze compiled code, too. Just as a portable assembler is an oxymoron (claimed implementations notwithstanding), so is a portable compiled code analyzer. >>One of Common Lisp's goals was to define a language that produces >>equivalent programs whether they are compiled or interpreted. > >Why, was it ever the case that compiled programs behaved differently from >interpreted? What lisp in particular? Maclisp. In interpreted code, all variables were special; in compiled code, variables were local, unless declared special. >>The >>implementations that use an special type for interpreted functions are >>actually helping you out, since the interpreter will catch an error >>that might have remained unnoticed until you tried the program >>compiled. In fact, it might not even be caught in compiled code, >>because the compiled code for SUBST might not do any type checking; >>you might just dump core or something. > >Ok, but I don't see what this has to do with #< notation. I don't see what this whole discussion has to do with NOTATION. We are talking about data types, not notation. What I said was that since you can't access the internal structure of compiled code, the interpreter is being reasonable in disallowing access to the internal structure of interpreted code, because the interpreter is expected to catch errors that might go undiagnosed in compiled code. >>Even among implementations that store interpreted definitions as lists >>in the function cell there can be much variety. On a Symbolics >>machine, DEFUN will store a list beginning with SI:DIGESTED-LAMBDA, >>and the actual lambda expression will be just one element. There is >>currently no portable way to access the interpreted definition of a >>function. > >That's true. Point is, what difference should it make what you call >the first element, LAMBDA-BLOCK, NAMED-LAMBDA, DIGESTED-LAMBDA or >whatever? Why not push for standardization? I mean, everybody has >already agreed on the lambda part, which is of no more than historical >significance, anyway. The point is that each implementation must be free to put whatever data it needs in the procedure object. It would not be right for the Common Lisp standard to specify how procedures should be represented. >>If you have programs that construct interpreted definitions and then >>want to be able to access these definitions, you should have them >>store them somewhere else in addition to (or instead of) the function >>cells of symbols. > >Of course I could do that, but it's ugly. I think what that leads to is writing >my own interpreter, with my own version of anything that does surgery, If you're writing a code walker, you essentially have to do that anyway. Even if the function cell were required to contain a lambda-expression, you couldn't depend on it containing only recognizable functions and special forms (DEFUN might have done some implementation-dependent transformations before storing it). As someone else already pointed out, there is a group in X3J13 preparing a proposal for a SOURCE-CODE function, which will return the lambda-expression of an interpreted function. Until then, your best bet when writing a code walker is to have it read the source files itself, rather than trying to extract procedures from the function cells. This is one reason why Lisp source uses the same syntax as Lisp data. >>The function cell is not a general-purpose storage location. > >A procedure is a piece of data, whether or not it has its own special >set of accessors. Besides which, I would imagine that this view >is anathema to your company's ideology, that of the identity of the processor >and memory. I'm not sure what this means. Connection Machine software is quite cognizant of the difference between processors and memory. In fact, procedures can't even be stored in CM memory (procedures are executed by a serial front-end computer, which sends commands to the CM when it wants to perform parallel operations, so you can't execute a parallel set of procedures). Barry Margolin Thinking Machines Corp. barmar@think.com {uunet,harvard}!think!barmar