"Jeff Read" bitwize@snet.net wrote: Smalltalk appears to borrow a lot from LISP in this regard, at least in terms of philosophy if not implementation. In Scheme, for instance, the keyword define means "Add this symbol to your currently running global environment, and bind it to the following value..."
Actually, it doesn't. Any Scheme block (Algol sense) may contain "defines", and they add to the LOCAL scope, not the GLOBAL scope. The Scheme standards (both ISO Scheme and RnRS Scheme) explain how (<stuff> (define x1 e1) ... (define xn en) <more stuff>) is equivalent to (<stuff> (letrec ((x1 e1) ... (xn en)) <more stuff>))
For example, (define (sort Xs) (define (merge Xs Ys) ...) ... ) does NOT introduce a global definition for merge.
The Scheme standards define entire Scheme programs in which ALL functions are explicitly present when compilation starts (of course Scheme has closures constructed at run time, but the lambda-expressions they are constructed from are explicitly present at compile time).
The difference, if I surmise correctly, is that every Scheme knows what define means; the base semantics for the keyword are standardized -- whereas each Smalltalk has a different protocol for creation of classes and adding methods to them. ANSI Smalltalk defines a "declarative" representation for classes and global variables. It is intended as an interchange format between different Smalltalk implementations.
I've read this discussion with great interest...on Friday I was just updating the declarative representation of my Swiki.net re-write...what a pain in the rump!
I agree with Allen's points regarding the need for declarative program representation. My feeling is that you essentially need to construct programs imperatively and deliver them declaratively.
It's interesting to note that in the computing world outside of Smalltalk, there is a sort of symbiotic relationship that exists between imperative and declarative systems. The most commonly used operating systems are imperative, and the most commonly used languages are declarative. If you don't believe me, try reconstructing the disk image bit for bit of a Windows system after a year of normal usage without mirroring the drive. ;)
I can only imagine the reaction that normal users would have to a declarative operating system. Yikes!
So, what do we really want Squeak's future to look like? Is it an operating system, or is it a programming language? I guess I've always felt that Squeak should evolve into something that incorporates the best aspects of operating systems, languages and databases into a unified approach.
Thus, I think the only logical conclusion is that Squeak needs to accommodate both forms of usage. And, it should allow both forms of usage without sacrificing the benefits of either.
- Stephen
I hesitate to comment here -- because I don't have the energy to handle lots of replies -- but ...
We first have to ask ourselves about "meaning" and "interpretation", and whether they are really separate concepts. For example, what meaning does "a declarative spec" actually have without some interpreter (perhaps *us*) being brought to bear? In practical terms, how does anyone know whether a declarative spec is consistent and means what it is purported to mean? IOW, *any* kinds of representations can be at odds with their purpose, and this is why they have to be debugged. This is just as true with "proofs" in mathematics. For example, Euler was an incredible guesser of theorems but an indifferent prover (not by his choice), so generations of PhD theses in math have been generated by grad students taking an Euler theorem, finding what was wrong with Euler's proof, and then finding a better proof!
I think the real issues have to do with temporal scope of changes and "fences" for metalevels. All languages' meanings can be changed by changing their interpretation systems, the question is when can this be done, and how easy is it to do? The whole point of classes in early Smalltalk was to have a more flexible type system precisely to extend the range of meanings that could be counted on by the programmer. This implies that there should be fences around such metastructures and it should not be easy to willfully change these meanings willynilly at runtime. Some languages make this easy for programmers by not allowing such changes to be intermingled with ordinary programs. Smalltalk is reflective and so it needs more perspective and wisdom on the programmer's part to deal with the increased power of expression. I also think that the system should have many more fences that warn about metaeffects.
However, I don't see anything new here. It was pretty clear in the 60s that a Church-Rosser language was very safe with regard to meaning. If we think of variables as being simple functions, then it is manifest that putting assignment into a language is tantamount to allowing a kind of function definition and redefinition willynilly at runtime. IOW, assignment is "meta". All of a sudden there are real problems with understanding meanings and effects. Some of the early work I did with OOP was to try to confine and tame assignment so it could be used more safely. Ed Ashcroft's work on LUCID (growing from Strachey's and Landin's explication of "what LISP means") provided a very nice way to do extremely safe and understandable assignment-style programming. You have something very pretty when you combine these two approaches.
If you want to write a debugger, etc., in the very language you are programming in *and* want things to be safe, then you have to deal with fences for metalevels, etc. But, if you are also a real designer, then you will want to think of these areas as having different privileges and different constraints.
The bottom line, to me at least, is that you want to be able to look at a program and have some sense of its meaning -- via what the program can tell you directly and indirectly. This is a kind of "algebraic" problem. However, one should not be misled into thinking a paper spec that uses lots of Greek letters is necessarily any more consistent or has any more meaning than a one page interpreter that can be run as a program.
Cheers,
Alan
--
Alan,
Thank you for taking the time to write that reply. Your perspective on this topic is really valuable (in advancing my own thinking, and I'm sure to many others on this list). The rest of this email is simply an attempt to regurgitate what you've just stated in the hopes that someone might point out any flaws in my understanding.
I think the real issues have to do with temporal scope of changes and "fences" for metalevels.
Definitely. There are and have been a lot of solutions that attempt to bring some measure of control over the temporal evolution of a system. A "a declarative spec" is but one of those solutions. Transactional systems, Croquet's T-Time, and even the distinction between "code" and "data" are others.
We first have to ask ourselves about "meaning" and "interpretation", and whether they are really separate concepts. For example, what meaning does "a declarative spec" actually have without some interpreter (perhaps *us*) being brought to bear?
In my recent efforts at updating my "declarative spec" of Swiki.net, my "interpreter" is the base squeak image (version 3.4) and the Squeak VM. My declarative spec is the script that brings all of the code packages into that base image.
But, it's also interesting to note that the scope of objects that are covered by this declarative spec do not include instances of my domain model. And that is true even of the "declarative" languages where you typcially have a database and program that exist independently of one another. This is painfully clear whenever you have to deliver an upgrade to a program that has to migrate data in an existing database to a new schema. A system that could address this problem in the context of both "code" and "data" would certainly be useful.
In my Chango VM and DB, I find that I have to separate objects that "live on disk" and objects that "live in memory" because it's easiest to manage the evolution of objects that live in memory (which are typically metamodel objects) using a declarative program specification model while managing the evolution of objects on disk (typically domain objects) is easiest using a transactional model. I briefly thought of managing even metamodel objects using the transactional system, but quickly vanquished the idea out of fear of the gut-wrenching changes to Squeak that would be required and the realization that having a transaction open while I write code would result in ridiculously long running transactions and impose a severe burden on the transaction system. ;)
In a database system, the declarative spec might be the full transaction logs that could be used to recreate the data. If the logs were sufficiently general (i.e. they had no direct schema knowledge)...then, you could conceivably upgrade a database by simply creating a new db with the new schema and then applying the logs.
A declarative spec is nothing more (or less) than a sequence of operations that transition a system from one state to another (not unlike an atomic update in the database world). And, when given some beginning state, applying those operations will always yield the same final state.
Regarding "fences" for metalevels, I take this to mean that you want some measure of control over temporal evolution at the metalevel boundaries where these fences reside. That control might take a form that is similar to a declarative model, a transactional model, or otherwise.
Given the limitations of the systems of today, I do see a need for supporting a declarative model of program specification...however, looking forward, I can also envision systems that provide much more general and powerful means for managing the temporal evolution of a system.
- Stephen
On Monday, February 24, 2003, at 10:15 AM, Stephen Pair wrote:
In a database system, the declarative spec might be the full transaction logs that could be used to recreate the data. If the logs were sufficiently general (i.e. they had no direct schema knowledge)...then, you could conceivably upgrade a database by simply creating a new db with the new schema and then applying the logs.
A declarative spec is nothing more (or less) than a sequence of operations that transition a system from one state to another (not unlike an atomic update in the database world). And, when given some beginning state, applying those operations will always yield the same final state.
This is tricky stuff, so I may not be grasping the subtleties of your explanation. As I understand it, however, "a sequence of operations that transition a system from one state to another" would be an imperative specification.
A declarative specification would be a description of the final state of (part of) the system, with the actual sequence of operations required to bring that state about left as an exercise for the reader. That reader might be a progammer such as myself, a simple program loader like SqueakMap, or a version control system like Envy.
For me, the hard part of this is that we're really talking about "meaning" which is a very difficult thing to pin down. In my own work I try to be very clear about what the abstractions that I'm dealing with actually mean. Usually this involves making sure that my OO model corresponds the real world in some meaningful way. A step up the scale of reflection though, it also requires some concept of what's going on inside the machine, not at a bit level, but at an abstract level that's both very clear and difficult to describe. Maybe I need more math.
As Alan mentioned, a program, whether specified imperatively or declaratively, is only meaningful in relation to some interpreter. The tricky thing about Smalltalk is that that interpreter, the VM, is as small and simple as possible. The semantics of interpretation are largely moved into the program its self; the program is the whole image.
Now, in practice we don't deal with the whole image during development. It's to big to comprehend, and most of it doesn't change much anyway. So the Smalltalk programs that we develop and distribute are only the set of classes and methods that implement the additional functionality needed for the program we're interested in.
From a tool-making perspective, however, these programs are difficult to deal with in a safe way, because their meaning relies on an interpreter with variable semantics. They have the ability to modify the way they are interpreted. If the interpreter is a human reader, that's not a problem, because we're (generally) smart enough to notice this and take it into account as we manipulate the program.
If we want to have tools to help us manipulate the program, though, we need to make some distinction between regular operations that follow the "conventional" interpretation, and meta-level operations which may alter the semantics of interpretation. Better fences, as Alan called them, would be very helpful for several projects in the Squeak community - Monticello, Islands/Squeak-E, SqueakMap etc.
It seems to me that a declarative representation for Smalltalk programs is a step in the right direction, though it's not a complete solution. The program object model that Allen's paper presents would also be quite useful for manipulating programs. I haven't been able to digest the ideas behind Squeak-E yet, but is seems like the E folks are a long way down the road the solving these problems. You can't have security without safety, so it will be interesting to see what sort of fences get put up as a result.
Coln
Colin Putney wrote:
On Monday, February 24, 2003, at 10:15 AM, Stephen Pair wrote:
In a database system, the declarative spec might be the full transaction logs that could be used to recreate the data. If the logs were sufficiently general (i.e. they had no direct schema
knowledge)...then,
you could conceivably upgrade a database by simply creating a new db with the new schema and then applying the logs.
A declarative spec is nothing more (or less) than a sequence of operations that transition a system from one state to another (not unlike an atomic update in the database world). And, when
given some
beginning state, applying those operations will always
yield the same
final state.
This is tricky stuff, so I may not be grasping the subtleties of your explanation. As I understand it, however, "a sequence of operations that transition a system from one state to another" would be an imperative specification.
Yes, I guess the point here is that the real difference between an imperative and declarative program construction is that the imperative approach is not capturing the operations that evolve the system from a beginning to a final state. It would be sort of like applying a declarative spec, and then throwing away that spec. And, even if the imperative approach did capture the operations, there would be lots of intermediate steps captured which wouldn't be appropriate in a "declarative spec."
A declarative specification would be a description of the final state of (part of) the system, with the actual sequence of operations required to bring that state about left as an exercise for the reader.
But I think that's the problem. Many people think of a declarative spec in that way, but that is in fact an illusion. A declarative spec cannot possibly describe final state in the absence of the "interpreter" of that declarative spec. I think that was Alan's point.
You could think of a declarative spec as a single operation rather than a sequence. It makes no difference...the declarative spec is an instruction given the interpreter to make it transform the state of system.
The benefit of having a declarative spec is that it makes the state transition repeatable given some interpreter of that spec. There are other ways to accomplish similar results and we should be mindful that there may be a common approach that accomodates this and other use cases.
The tricky thing about Smalltalk is that that interpreter, the VM, is
as
small and simple as possible. The semantics of interpretation are largely moved into the program its self; the program is the whole image.
Yes, "interpreter" does not mean the VM...it means the VM plus the current state of the image. For example, a VM alone cannot comprehend a Smalltalk class definition.
- Stepehn
This is tricky stuff, so I may not be grasping the subtleties of your explanation. As I understand it, however, "a sequence of operations that transition a system from one state to another" would be an imperative specification.
Many interpreters are going to do the following: "parse a series of changes from this file, and then apply the changes in sequence". So it's not necessarily so different.
Everyone is missing Alan's point. A "declarative" syntax still requires that you have an interpreter to make sense of it. Thus the difference is pretty subtle, at least in theory. It's the difference between interpreter+language, versus interpter1+interpreter2+language.
Let me get more pragmatic for a moment. In *practice*, imperative versus declarative is about the same. First, you can perfectly well read a Squeak fileout, in practice, as if it were declarative. There are only 5-10 forms that you must support. Second, you can go to the trouble, if you like, of making up a Smalltalk environment where code changes don't directly modify the system, but instead log themselves into the currently active changeset.
-Lex
On Sun, 2 Mar 2003, Lex Spoon wrote:
Let me get more pragmatic for a moment. In *practice*, imperative versus declarative is about the same. First, you can perfectly well read a Squeak fileout, in practice, as if it were declarative. There are only 5-10 forms that you must support.
Which is exactly what DVS does, for example.
Hi lex and alan
I think that it would be good have a list of the changes that should be made in squeak to have a declarative syntax.
The obvious one is to have a - GlobalVariable declaration, PoolDictionary, instead of Smalltalk at: #MyVar put: 0 We should have GlobalVariable named: #MyVar initialized: '0' - InstanceVariable declaration would be good too.
- ClassVariable
- I have doubt for classDefinition because even if this is currently done via a message we can interpret it as a declaration.
Alan (I know that you are busy) but what would be your point items for such a list.
I think that having such a list is important for the people such as Avi that could use a much more declarative syntax for DVS.
Stef
On Sunday, March 2, 2003, at 06:56 AM, Lex Spoon wrote:
This is tricky stuff, so I may not be grasping the subtleties of your explanation. As I understand it, however, "a sequence of operations that transition a system from one state to another" would be an imperative specification.
Many interpreters are going to do the following: "parse a series of changes from this file, and then apply the changes in sequence". So it's not necessarily so different.
Everyone is missing Alan's point. A "declarative" syntax still requires that you have an interpreter to make sense of it. Thus the difference is pretty subtle, at least in theory. It's the difference between interpreter+language, versus interpter1+interpreter2+language.
Let me get more pragmatic for a moment. In *practice*, imperative versus declarative is about the same. First, you can perfectly well read a Squeak fileout, in practice, as if it were declarative. There are only 5-10 forms that you must support. Second, you can go to the trouble, if you like, of making up a Smalltalk environment where code changes don't directly modify the system, but instead log themselves into the currently active changeset.
-Lex
Prof. Dr. Stéphane DUCASSE (ducasse@iam.unibe.ch) http://www.iam.unibe.ch/~ducasse/ "if you knew today was your last day on earth, what would you do different? ... especially if, by doing something different, today might not be your last day on earth" Calvin&Hobbes
Hi Stef --
Just an historical note ...
My first attempt at dynamic OOP was called Flex, and I tried to do two main things: extension in all areas and the abstraction of assignment in all areas -- both of these worked out pretty well.
Later, after seeing Carl Hewitt's Planner and some other work at MIT (such as Pat Winston's concept learning system) and motivated by Papert's LOGO work, I got very interested in doing away with variables in the storage sense as much as possible. The never implemented Smalltalk-71 embodied these ideas. Smalltalk-72 allowed some of these ideas to get implemented and tested, but in a rudimentary way because its control metastructures were not present in a strong enough way.
The bottom line in these early stages was to try to have messages be requests for larger goals to be carried out, and *not* to simply simulate data-structures. Now, we realized that some programs would look more like data-structure and procedural programming than we wanted because we didn't yet know how to do a real object oriented version of them. We thought of a "good" program as being one that had no visible getter and setter methods to the outside world and that the interior would change behavior appropriately as the result of satisfying goals. (This later merged with some ideas about event-driven objects that would pretty much *not* send and receive messages, but would simply respond to general changes around them (could be thought of as weak message receipt, etc.) -- this never got implemented in the 70s).
I still have many of these prejudices. So I wince every time I set a g/setter that is not absolutely necessary -- every time I see a collection used nakedly, etc. I still think that Ed Ashcroft's way of looking at process in his language Lucid (itself very influenced by the POVs of Strachey and Landin) is a really nifty way to reconcile the two worlds -- and I still like "information algebras" (like APL) that allow projections to be created. Though they all have interpreters, all of these systems lean very much to trying to allow something more like mathematical mappings to be used as a programming paradigm.
As you know, Croquet is very interesting as a testbed for these ideas. The underlying "temporal algebra" by Dave Reed is a way to allow very powerful methods of description (if a surface form can be found that is usable and understandable by programmers) -- so quite a lot of the scripting design that Andreas Raab has been doing with the modeling ideas of Dave A Smith is to find a dynamic model that is both programmable and incorporates the higher level integrities of the larger sim worlds.
Cheers,
Alan
-------
At 11:43 AM +0100 3/3/03, Stephane Ducasse wrote:
Hi lex and alan
I think that it would be good have a list of the changes that should be made in squeak to have a declarative syntax.
The obvious one is to have a
- GlobalVariable declaration, PoolDictionary, instead of Smalltalk at: #MyVar put: 0
We should have GlobalVariable named: #MyVar initialized: '0'
InstanceVariable declaration would be good too.
ClassVariable
I have doubt for classDefinition because even if this is
currently done via a message we can interpret it as a declaration.
Alan (I know that you are busy) but what would be your point items for such a list.
I think that having such a list is important for the people such as Avi that could use a much more declarative syntax for DVS.
Stef
On Sunday, March 2, 2003, at 06:56 AM, Lex Spoon wrote:
This is tricky stuff, so I may not be grasping the subtleties of your explanation. As I understand it, however, "a sequence of operations that transition a system from one state to another" would be an imperative specification.
Many interpreters are going to do the following: "parse a series of changes from this file, and then apply the changes in sequence". So it's not necessarily so different.
Everyone is missing Alan's point. A "declarative" syntax still requires that you have an interpreter to make sense of it. Thus the difference is pretty subtle, at least in theory. It's the difference between interpreter+language, versus interpter1+interpreter2+language.
Let me get more pragmatic for a moment. In *practice*, imperative versus declarative is about the same. First, you can perfectly well read a Squeak fileout, in practice, as if it were declarative. There are only 5-10 forms that you must support. Second, you can go to the trouble, if you like, of making up a Smalltalk environment where code changes don't directly modify the system, but instead log themselves into the currently active changeset.
-Lex
Prof. Dr. Stéphane DUCASSE (ducasse@iam.unibe.ch) http://www.iam.unibe.ch/~ducasse/ "if you knew today was your last day on earth, what would you do different? ... especially if, by doing something different, today might not be your last day on earth" Calvin&Hobbes
--
Howdy,
Actually, the BlueBook introduces pools as a shared scope. Surprisingly, nowhere on pp 46-47 is the expression 'Pool Dictionary' to be found. Perhaps the declaration should be for a Pool and the variables with their initializers that it contains.
It's a small thing, but the point is to exorcise implementation details from the language of declaration.
John
On Monday, March 3, 2003, at 02:43 AM, Stephane Ducasse wrote:
Hi lex and alan
I think that it would be good have a list of the changes that should be made in squeak to have a declarative syntax.
The obvious one is to have a
- GlobalVariable declaration, PoolDictionary, instead of Smalltalk at: #MyVar put: 0
We should have GlobalVariable named: #MyVar initialized: '0'
InstanceVariable declaration would be good too.
ClassVariable
I have doubt for classDefinition because even if this is currently
done via a message we can interpret it as a declaration.
Alan (I know that you are busy) but what would be your point items for such a list.
I think that having such a list is important for the people such as Avi that could use a much more declarative syntax for DVS.
Stef
On Sunday, March 2, 2003, at 06:56 AM, Lex Spoon wrote:
This is tricky stuff, so I may not be grasping the subtleties of your explanation. As I understand it, however, "a sequence of operations that transition a system from one state to another" would be an imperative specification.
Many interpreters are going to do the following: "parse a series of changes from this file, and then apply the changes in sequence". So it's not necessarily so different.
Everyone is missing Alan's point. A "declarative" syntax still requires that you have an interpreter to make sense of it. Thus the difference is pretty subtle, at least in theory. It's the difference between interpreter+language, versus interpter1+interpreter2+language.
Let me get more pragmatic for a moment. In *practice*, imperative versus declarative is about the same. First, you can perfectly well read a Squeak fileout, in practice, as if it were declarative. There are only 5-10 forms that you must support. Second, you can go to the trouble, if you like, of making up a Smalltalk environment where code changes don't directly modify the system, but instead log themselves into the currently active changeset.
-Lex
Prof. Dr. Stéphane DUCASSE (ducasse@iam.unibe.ch) http://www.iam.unibe.ch/~ducasse/ "if you knew today was your last day on earth, what would you do different? ... especially if, by doing something different, today might not be your last day on earth" Calvin&Hobbes
squeak-dev@lists.squeakfoundation.org