The title is perhaps a bit misleading. I took at grad course in programming languages where we had to develop a LISP interpreter. Since then, I've been tinkering with it for fun, doing things to it. And since I like Scheme more than LISP, it's evolved to be more like Scheme than LISP. I'm betting that nothing I mention here is new, but on the other hand, I haven't found much about it, so please forgive me for being redundant. Since what I'm doing is very academic in nature, I'm not overly concerned with making a compilable language.
Some of you may laugh or point me to old arguments about functional programming versus object-oriented programming. While I'm interested in reading about that, I have a particular idea that I'm tinkering with that I'd like some suggestions on. I've looked at existing attempts to add OOP to LISP, and I don't like them. One of the problems for me is that they heavily pollute the namespace. If you have a simple data structure with three variables in it, you're going to get two functions (get/set) for each variable.
It occurred to me that it might be nice to CLEAN UP the namespace. The idea that occurred to me was to create a new data type that is a namespace. What it amounts to is a variable table that is bound to a variable in another namespace.
Say you wanted to load a socket library. It seems to me like it would be nice to group all of those calls together into their own namespace. The SOCKETLIB namespace would be bound to a variable in the global namespace. When you wanted to access a function in there, you reference it by namespace and by name. There are also some potential performance advantages (at least for an interpreter).
Object-oriented programming falls straight out of this. An object is just a namespace. For class definitions, you'd just have a template object with a "create" method; calling that would return a reference to a new namespace that is the object you wanted to create.
One of the wild applications I thought of for this is a MUD engine. In my interpreter, functions are nothing more than the list that describes them. You can dynamically generate code and then just execute it. Internally, when executing a function, I just do an eval on the name of the function; what comes back is expected to be a function body (with parameter list). The problem with a MUD engine is security. You want users to be able to write code, but you don't want them mucking about with things that don't belong to them. The solution to this is that functions executing within a given namespace cannot access any other namespace that they don't know about. Thus, a user would have his own namespace, allowing him access only to that, any he knows about, and the global namespace. Sensitive stuff (like the code of the MUD engine itself) would be in some other namespace. (Oh, and there's also read-only access to namespaces.)
As I've been adding features that I haven't found in other lisps, I have managed to come up with reasonably elegant ways to represent them syntactically. For instance, in LISP, there is a distinction between a function (which evaluates its arguments) and a special form (which does not). I have eliminated that distinction by allowing function definitions to dictate which parameters are to be evaluated. For instance: (defun func (eval_param 'uneval_param) body) In this case, uneval_param is quoted, indicating that the parameter in the function call should be taken literally. This made things like my internal definitions of DEFINE, DEFUN, QUOTE, and COND more elegant to implement.
Back to namespaces. It seems reasonable that a namespace could be treated, in a sense, like a function. That is, if you want to execute something in a namespace, you would use this syntax: (other_namespace (function_in_that_space parameters)) Or, if you wanted to get a variable from a namespace, it would be like this: (other_namespace var_in_that_space)
Now to my problem. Given this syntax, I can execute a function whose body and parameters are in another namespace: (other_namespace (function_there variable_there)) Here, 'variable_there' has to be in other_namespace. I could also call a function from another namespace and execute it in the calling namespace: ((other_namespace function_there) variable_here) Here, the parameter is evaluated in the calling namespace, but the the function is also executed in the calling namespace.
The trouble comes down to side-effects. I would like to be able to call a function from another namespace, passing it parameters evaluated in the calling namespace, and then execute it in the other namespace. This way, I can pass in the parameters I like, but side-effects are confined to the function's home namespace. This is necessary for good OOP.
I'm trying to come up with a way to represent this that is both syntactically elegant and easy to implement in my interpreter.
Ray Dillinger wrote: > pscho...@uci.edu wrote: > > How are you implementing inheritance? Inheritance may be one of the > > most useful features of any object system.
> Requirements vary. Not all OO systems have inheritance. > And not everybody believes it's a terribly useful feature.
True, but he has an excellent point.
Let me see if I can think up how to do inheritance without changing anything. How about this: The class (object template) knows about the class it's inheriting from. When the "make object" function creates an instance of the class, it will call the "make object" on its parent class first before setting up its own stuff (which amounts to creating references to functions and initializing variables). Voila, you have a composite of multiple objects, with the child class overriding things belonging to the parent class. Polymorphism isn't much of an issue, since the object's "type" is nothing more than what's contained in the namespace.
Let me demonstrate why inheritance is useful: There is a library class that supports posting to message boards, and it has a large number of methods, say 20, all supporting one basic post method. Now I want to write a class that appends a sig onto each of my posts. If I don't have inheritance I write a class with 19 methods, each of which is simply passes on the message to an instance of the library class. Then I write a set-sig method and override the post method so that it appends the sig. If I have inheritance I simply inherit from the posting class and write the add-sig method and override the post method. Without inheritance re-use of old classes become extremely ugly. This is why inheritance is vital for projects that build on earlier work.
One problem with your fix theo: what about multiple inheritance? If you are inheriting from two objects, both of which have a method with the same name then they will end up stepping on each others' toes. Admittedly multiple inheritance isn't as important as regular inheritance, but it is still something you see people using on occassion. For example Java doesn't support real multiple inheritance, and occassionally you see some nasty hacks to get arround it.
Also in your system how do namespaces access themselves? For example how would a method in your object be able to pass the object its self to some other function. Some object systems resolve this by having the first parameter to a method call be the object, others use an implicit this/self reference. If you want to see how I implemented such a system I can refer you to my blog, which contains source, and later today will have a description of 2 ways of implementing inheritance (and object cloning): http://pschombe.wordpress.com/
pscho...@uci.edu wrote: > One problem with your fix theo: what about multiple inheritance? If > you are inheriting from two objects, both of which have a method with > the same name then they will end up stepping on each others' toes.
I thought about that. In this case, there's nothing much different between one class inheriting from multiple classes, or one class inheriting from another class which inherits from another, etc. All that makes any difference is that whichever constructor gets called last takes presidence, whenever there's a conflict.
And it's a depth-first construction, so if two classes you inherit from inherit from the same class, you may get weird results. Well, you can predict the results, but in any case, you won't get two of the duplicate base class. Everything's just thrown together into one namespace.
Of course, none of this is going to matter until I decide how to deal with my little parameter-passing issue.
Somehow, I need to develop a clean way of doing these three things:
[1] Call function B::F in namespace A, evaluating parameters in namespace A [2] Call function B::F in namespace B, evaluating parameters in namespace B [3] Call function B::F in namespace A, evaluating parameters in namespace A
The first two already work. But the third option is important. One solution is to not have namespace B take over until we've entered the body of the function being called, but that would overload the syntax I'm already using for [2].
At the moment, [1] looks like this: ((B F) params) And [2] looks like this: (B (F params))
Actually, now that I think about it, if we used [2]'s syntax for [3], we could do [2] like this: (B (F (B param) (B param2)))
pscho...@uci.edu wrote: > Also in your system how do namespaces access themselves? For example > how would a method in your object be able to pass the object its self > to some other function. Some object systems resolve this by having the > first parameter to a method call be the object, others use an implicit > this/self reference. If you want to see how I implemented such a > system I can refer you to my blog, which contains source, and later > today will have a description of 2 ways of implementing inheritance > (and object cloning): http://pschombe.wordpress.com/
If you don't specify a namespace, then you're working in your local namespace. This looks just like regular LISP code. I guess this is an implicit this reference. In addition, although I haven't added it yet, I'm going to provide a built-in function that returns a reference to the local namespace that you can pass as a parameter to function calls.
I think you missed my point a little. Let us say you had two classes, one of which used the variable x to store a list of names, and the other used the variable x to store a number. If you inherit from both of these classes there will be problems, for example if one class has a method that computes (+ x 2.5) and another that does (set! x (append x '(b))) ugly errors will arrise. At least it seems to me that is what would happen.
pscho...@uci.edu wrote: > I think you missed my point a little. Let us say you had two classes, > one of which used the variable x to store a list of names, and the > other used the variable x to store a number. If you inherit from both > of these classes there will be problems, for example if one class has a > method that computes (+ x 2.5) and another that does (set! x (append x > '(b))) ugly errors will arrise. At least it seems to me that is what > would happen.
Ok, I get it. Do you have any suggestions about how to deal with this? I'll have to give it some thought.
I'm wondering. What does C++ do about it?
class x { public: int x;
};
class y { public: string x;
};
class z : public x, public y { ....
};
z my_z;
What does my_z.x give you? A compile-time error?
For that matter, I don't handle this properly either:
class x { public: int x; int something(void) { return x; }
};
class y : public x { public: string x; string anotherthing(void) { return x; }
};
y my_y;
My equivalent to my_y.something() will behave incorrectly.
Note that my LISP implementation has lexical scope across namespace boundaries but dynamic scope over function calls within the same namespace. This weirdness is probably going to cause some trouble of its own.
Yes my_z.x gives you an ambigious reference compile time error. You could fix that with either: my_z.x::x; or my_z.y::x; which will tell the compiler which one you want. Most compiler books give a description of how multiple inheritance is implemented, but I'll give a really brief one here: In memory the class z will be laid out as XXXXYYYYZZZ where X and Y represent memory allocated for storing data belonging to classes x and y. Also each member function of a class has a hidden first parameter that is the offset of the class in memory, so so access a member variable the function uses: offset of class + constant values to find it. Thus when class z invokes an X method it passes the start of the x data in memory, and for y data it passes the start of y in memory. Virtual functions add some complications, I can give you a description of those too if you want. In my implementation of multiple inheritance basically I build a copy of each parent object and dispatch unhandled messages to each of the parents in turn to see if they can handle them. Also virtual functions mean that I have to jump through some extra hoops too. once again see my blog for implementation details.
theo...@gmail.com writes: > pscho...@uci.edu wrote: >> I think you missed my point a little. Let us say you had two classes, >> one of which used the variable x to store a list of names, and the >> other used the variable x to store a number. If you inherit from both >> of these classes there will be problems, for example if one class has a >> method that computes (+ x 2.5) and another that does (set! x (append x >> '(b))) ugly errors will arrise. At least it seems to me that is what >> would happen.
> Ok, I get it. Do you have any suggestions about how to deal with this? > I'll have to give it some thought.
> I'm wondering. What does C++ do about it?
Well, C++ is statically typed. Having a member x of type int in one mixin and a member x of type char* in another would be a problem if those x weren't in different namespaces.
But in lisp, since we're dynamically typed, you can easily unify same named slots. Or do whatever you want. You could keep both slots.
CLOS unify them:
[4]> (defclass a () (x)) #<STANDARD-CLASS A> [5]> (defclass b () (x)) #<STANDARD-CLASS B> [8]> (defclass c (a b) ()) #<STANDARD-CLASS C> [9]> (inspect (make-instance 'c)) #<COMMON-LISP-USER::C #x204F93B6>: standard object type: COMMON-LISP-USER::C 0 [X]: |#<unbound>| INSPECT-- type :h for help; :q to return to the REPL ---> :q
Only one slot named X in a C instance.
But it's even more different. In C++, you have this idea of data hidding, and methods belong to classes.
In CLOS, for example, but I believe in general in lisp too, there's less data hidding going on: the programmers are considered adult, with responsibility to have a look at the mixins sources and to act responsably. In CLOS, methods are not attached to the classes, but to generic functions.
First, instead of accessing the slot directly with slot-value, you usually go thru an accessor function. Either with naming conventions or the use of packages, slot names and accessors are usually easily distinguished. But assuming you keep the same slot name:
[13]> (defclass a () ((x :accessor a-x))) #<STANDARD-CLASS A :VERSION 1> [14]> (defclass b () ((x :accessor b-x))) #<STANDARD-CLASS B :VERSION 1> [15]> (defclass c (a b) ((x :accessor c-x))) #<STANDARD-CLASS C :VERSION 3> [16]> (setf o (make-instance 'c)) #<C #x205222E6> [17]> (setf (a-x o) 1) 1 [18]> (b-x o) 1 [19]> (setf (b-x o) '(a b c)) (A B C) [20]> (a-x o) (A B C) [21]> (c-x o) (A B C)
We have therefore three accessors for the same unified slot. But we can write our own accessor, to dispatch to one of the mixin accessors:
which can be indicated when these mixin accessors do more than merely assigning the slot, and when only one aspect of that slot is used at a time.
Otherwise, if all the methods working on the mixin classes access the slot x thru the corresponding accessors (a-x or b-x), you can merely redefine these accessor. Well actually this doesn't "redefining" them, this only add a new method to the corresponding generic function:
[24]> (defclass c (a b) ((a-x :accessor a-x) (b-x :accessor b-x))) WARNING: The generic function #<STANDARD-GENERIC-FUNCTION C-X> is being modified, but has already been called. WARNING: Removing method #<CLOS:STANDARD-READER-METHOD (#<STANDARD-CLASS C :VERSION 3>)> in #<STANDARD-GENERIC-FUNCTION C-X> WARNING: DEFCLASS: Class C (or one of its ancestors) is being redefined, instances are obsolete WARNING: The generic function #<STANDARD-GENERIC-FUNCTION A-X> is being modified, but has already been called. WARNING: The generic function #<STANDARD-GENERIC-FUNCTION (SETF A-X)> is being modified, but has already been called. WARNING: The generic function #<STANDARD-GENERIC-FUNCTION B-X> is being modified, but has already been called. WARNING: The generic function #<STANDARD-GENERIC-FUNCTION (SETF B-X)> is being modified, but has already been called. #<STANDARD-CLASS C :VERSION 4> [25]> (setf o (make-instance 'c)) #<C #x205411CE> [26]> (inspect o) #<COMMON-LISP-USER::C #x205411CE>: standard object type: COMMON-LISP-USER::C 0 [X]: |#<unbound>| 1 [A-X]: |#<unbound>| 2 [B-X]: |#<unbound>| INSPECT-- type :h for help; :q to return to the REPL ---> :q
[27]> (setf (a-x o) 1 (b-x o) "one") "one" [28]> (inspect o) #<COMMON-LISP-USER::C #x205411CE>: standard object type: COMMON-LISP-USER::C 0 [X]: |#<unbound>| 1 [A-X]: 1 2 [B-X]: "one" INSPECT-- type :h for help; :q to return to the REPL ---> :q
[29]> (a-x o) 1 [30]> (b-x o) "one" [31]> (setf oa (make-instance 'a)) #<A #x2054EFAE> [32]> (setf (a-x oa) 42) 42 [33]> (inspect oa) #<COMMON-LISP-USER::A #x2054EFAE>: standard object type: COMMON-LISP-USER::A 0 [X]: 42 INSPECT-- type :h for help; :q to return to the REPL ---> :q
[34]>
So in conclusion, in lisp you have the opportunity to implement whatever object system you want, and you would be well advised to check other object models than that of C++. Have a look at CLOS (Common Lisp), at KR, at Smalltalk! ( You can find KR in Garnet http://sf.net/projects/garnetlisp ). And don't forget to google for: scheme object
ADVISORY: There is an extremely small but nonzero chance that, through a process known as "tunneling," this product may spontaneously disappear from its present location and reappear at any random place in the universe, including your neighbor's domicile. The manufacturer will not be responsible for any damages or inconveniences that may result.
pscho...@uci.edu wrote: > Let me demonstrate why inheritance is useful: There is a library class > that supports posting to message boards, and it has a large number of > methods, say 20, all supporting one basic post method. Now I want to > write a class that appends a sig onto each of my posts. If I don't > have inheritance I write a class with 19 methods, each of which is > simply passes on the message to an instance of the library class. Then > I write a set-sig method and override the post method so that it > appends the sig. If I have inheritance I simply inherit from the > posting class and write the add-sig method and override the post > method. Without inheritance re-use of old classes become extremely > ugly. This is why inheritance is vital for projects that build on > earlier work.
and why is this an extension to the 20 different post methods rather than an extension to the one edit method? Because you have analyzed the problem badly.
The argument against inheritance is that it allows bad methodology and problem analysis to create complex, difficult-to-maintain "spaghetti classes" - in this particular example trading 20 different inheritance paths for one.
Each and every inheritance path is a logical path that someone will have to navigate someday when they try to maintain the code; automating their creation in such a way that it happens casually or carelessly, especially when it is used instead of good design, increases the burden on maintainers.
Ray Dillinger wrote: > Each and every inheritance path is a logical path that someone > will have to navigate someday when they try to maintain the code; > automating their creation in such a way that it happens casually or > carelessly, especially when it is used instead of good design, > increases the burden on maintainers.
Ray Dillinger wrote: > and why is this an extension to the 20 different post methods rather > than an extension to the one edit method? Because you have analyzed > the problem badly
No, the assumption is that multiple methods may call the one post method. Not all of them may be the edit method. The point is that we are building on an object with functionality implemented for other pourposes, we are engaging in code re-use vs. rewriting it from scratch. It seems like you want to simply rewrite the class from scratch, as you haven't proposed a better method. Let me make the example more concrete: The class contains methods post, which does the sending of text, and well as create-new-post and respond-to-post, both of which call post internally. I am suggesting that we would inherit from this class, so that we only have to change the post method in our new class to append our sig. I am willing to hear other ideas that would let us do the same thing wihtout rewriting the class from scratch. Basically if you are going to use objects in your programming at some point you will want inheritance if you wish to reuse exising code without writing it over again.
Pascal, I see the point you are making, but at some level most people would like multiple inheritance to work automatically, without a lot of inspecting for overlapping names. For example if I am inheriting from A and B maybe later A gets a new function of variable with the same name as one in B, but everytime I modify A or B I don't want to have to go looking through every other class for similiar names, expecially when the inheritance tree becomes complicated. I admit that CLOS is very powerful, and there are Scheme implementations of it, but at some level we resort to objects in order to hide some of the details, so that it is easier to program on a large scale without worrying about the details all the time. Having to inspect inheritance paths for name clashing seems like exactly the kind of detail that we were hoping objects could avoid, or am I missing something?
theo...@gmail.com wrote: > As I've been adding features that I haven't found in other lisps, I > have managed to come up with reasonably elegant ways to represent them > syntactically. For instance, in LISP, there is a distinction between a > function (which evaluates its arguments) and a special form (which does > not). I have eliminated that distinction by allowing function > definitions to dictate which parameters are to be evaluated. For > instance: > (defun func (eval_param 'uneval_param) body) > In this case, uneval_param is quoted, indicating that the parameter in > the function call should be taken literally. This made things like my > internal definitions of DEFINE, DEFUN, QUOTE, and COND more elegant to > implement.
What happens if you pass func to other functions? e.g.
(map func (list a b c) (list d e f))
Naturally, one would expect the result would be the same as the result of:
(list (func a d) (func b e) (func c f))
But I don't know how to do that without very strong type inference and whole program analysis. I think this is one of the reasons most lisps don't implement the feature. (I think I've seen somebody in c.l.s implementing similar feature, though... I'm curious how it has come out.)
> > As I've been adding features that I haven't found in other lisps, I > > have managed to come up with reasonably elegant ways to represent them > > syntactically. For instance, in LISP, there is a distinction between a > > function (which evaluates its arguments) and a special form (which does > > not). I have eliminated that distinction by allowing function > > definitions to dictate which parameters are to be evaluated. For > > instance: > > (defun func (eval_param 'uneval_param) body) > > In this case, uneval_param is quoted, indicating that the parameter in > > the function call should be taken literally. This made things like my > > internal definitions of DEFINE, DEFUN, QUOTE, and COND more elegant to > > implement.
> What happens if you pass func to other functions? e.g.
> (map func (list a b c) (list d e f))
> Naturally, one would expect the result would be the same > as the result of:
> (list (func a d) (func b e) (func c f))
> But I don't know how to do that without very strong type inference > and whole program analysis. I think this is one of the reasons > most lisps don't implement the feature. > (I think I've seen somebody in c.l.s implementing similar feature, > though... I'm curious how it has come out.)
I've got two options that come to mind:
[1] So far, I've been passing around a reference to a single namespace as a "context". I can make contexts have two namespaces. One is for evaluating parameters; one is for calling functions. When a function is applied, the parameter eval namespace is switched for the function call namespace so that the called function evaluates parameters to functions it calls in its own namespace. (Lexical scope on parameter passing, essentially.)
The problem with [1] is that if you want to pass a function as a parameter it will not be executed in its own namespace.
[2] When a function (well, just a list) is looked up, somehow, its home namespace is attached so that when it's executed, it gets run in its home namespace. This way, I can pass a function as a parameter, and side-effects of that function occur in the namespace that function was looked up from. In order to pass a function and have it run in any arbitrary namespace, I would have to provide a builtin that strips it of a namespace reference.
Implementing [1] is how I described it: (namespace (funcname params))
But [2] would overload the other syntax: ((namespace funcname) params)
But with [2], I have to provide a way to execute a function, looked up in another namespace, but run in the local namespace. It would look like this: ((detach (namespace funcname)) params)
The way I've organized memory makes it hard to attach that kind of metadata to something. I thought about making an INDIRECT data type that internally was just a dotted pair, car being the object of interest, cdr being a reference to the namespace. The trouble is that since I don't distinguish between lists and functions, any time I wanted to process a list somewhere, I'd have to add code to strip off the indirect reference. Pain and performance loss. You'll see my problem when you have a look at my internal representation of a cell:
typedef int cell_t; typedef long long int64; typedef long double real80; typedef cell_t (*fptr_t)(cell_t s, cell_t alist);
typedef union _s_expr { struct { unsigned int type : 4; unsigned int flag0 : 1; unsigned int quoted : 1; unsigned int refct : 26; };
struct { int dummy; int64 val; } integer;
struct { int dummy; real80 val; } real;
struct { int dummy; cell_t a, d, b; } pair;
struct { int dummy; char *ptr; union { int len; cell_t unq; /* reference to unquoted form of symbol */ }; } string; /* and symbol */
struct { int dummy; fptr_t f; unsigned int pflags; } fptr;
} s_expr_t;
It's exactly 128 bits. The 'b' reference for a pair is actually an extension I made in order to make binary trees easier to implement (and CBR is the function to call to get this 'middle' branch), so I use it all over the place. Instead of association lists, I have association trees, making lookups O(log(n)) time. I cannot just hide in there namespace metadata without making the structure an uneven number of words.
I realize that I should not let low-level decisions hurt the over-all design of the language, but there's are some advantages to having power-of-two sized data structures.
Suggestions?
P.S. I got some great ideas from a classmate on memory organization. Adding some ideas of my own, memory cells are not malloc'd individually. Instead, they're allocated in blocks of 256, and cells are referenced by index instead of pointer. I have an expandable array of pointers to blocks of 256. Now, since cell numbers are just integers, this guy gave me the idea to encode 31-bit integers in cell numbers so that they effectively don't take up any space. Here's my macro to decode an int:
INLINE long long get_int(cell_t n) { if (n & INTMASK) { if (n & INTSIGN) { return (int64)n; } else { return (int64)(n ^ INTMASK); } } if (n < RESERVED_CELLS) int_error(); return GCELL(n).integer.val;
pscho...@uci.edu writes: > Pascal, I see the point you are making, but at some level most people > would like multiple inheritance to work automatically, without a lot of > inspecting for overlapping names. For example if I am inheriting from > A and B maybe later A gets a new function of variable with the same > name as one in B, but everytime I modify A or B I don't want to have to > go looking through every other class for similiar names, expecially > when the inheritance tree becomes complicated. I admit that CLOS is > very powerful, and there are Scheme implementations of it, but at some > level we resort to objects in order to hide some of the details, so > that it is easier to program on a large scale without worrying about > the details all the time. Having to inspect inheritance paths for name > clashing seems like exactly the kind of detail that we were hoping > objects could avoid, or am I missing something?
There's no "One Way" of handling multiple inheritance, neither one way to design an object system.
If you have these requirements, surely you can come with a solution.
Here is another question you'll have to resolve:
(defclass a () (x)) (defclass b (a) (y)) (defclass c (a) (z)) ;; up to now, no collision. (defclass d (b c))
What should be done here? D inherits from both B and C, and they both inherit from A.
What if D considered as a B instance wants to impose a different invariant on X than D considered as a C instance?
On some systems, the A part of the mixin is duplicated. In others it's unified.
You can come with real-life examples where both solutions would be right:
As to the probelm of whether to create one class or two I have run accross this already. C++ solves it by allowing you to mark parts of the inherance path as virtual, meaning that only one copy of that class if it is inherited multiple times should be made. I think you are missing my point a little, which is that I think I good object system should allow you to do these things without having to worry about name clashing. Once again I see the CLOS has more power, but there is also more work to get features like multiple inhertitance working. My argument is that the best object systems give you the power of multiple inheritance without making you have to jump through these hoops or worry about these kinds of issues because the language/system already takes care of it for you.
If you want to work entirely within a namespace, then use the namespace like a function name:
> (ns (define var 3))
3
To access something in a namespace, do the same thing:
> (ns var)
3
You can add functions to a namespace:
> (ns (defun q (x y) (+ x y)))
Q
You can execute code entirely within a namespace and call functions in that namespace:
> (ns (define a 3) (define b 4) (q a b))
7
Often, however, you'll want to execute a function in another namespace, but you want to get evaluate parameters from the calling namespace. Internally, whenever you look up a function, it brings along with it information about its home namespace. Eval for anything lexically in the function call is done in the calling namespace, but inside of the function, the namespace used is the home namespace of the function:
> (define a 6) 6 > (define b 7) 7 > ((ns q) a b)
13
Let's say you have a function that has side-effects. For instance, it sets variables in its namespace:
> (ns (defun q (x y) (set z (+ x y))))
Q
If you call it the way I mentioned above, the variable z will be set in the ns namespace:
> ((ns q) 3 4) 7 > (ns z)
7
But what if you want to execute it in the current namespace?
> ((detach (ns q)) 3 4) 7 > z
7
How about running a function from namespace D in namespace E?
> ((attach e (d q)) 3 4)
{whatever}
On top of this, a semi-object system can be built, although its original purpose was a security feature for a MUD engine. :)
Well I think one change you should make is that the parameters passed to a function should always be evaluated in their current namespace. For example consider this situation with my-plus being defined in ns. (let ((x 4) (y 7)) (my-plus x y)) I think everyone would expect the answer to be 11, but if x or y ever are defined in ns the answer will change, at least that is how I am reading your description. Especially when working with other people it may be hard ot keep track of what variables are used in which namespaces. Thus to evaluate the parameters in the functions namespace I think you should use the following: (my-plus (ns x) (ns y)) which makes it clear what is happening.
Another benefit to this method is that parameters can be from different namspaces. Consider the situation where you have a function my-map defined in A, the function you wish to pass it in B, and the list in C. I don't know how you would make this work under your current model, but under my suggestion it should look like: (my-map (B func) (C lst))
This shouldn't affect a function defined in ns that sets a varaible z, no matter where it is called from the z it sets should be in ns, because references are bound when the function is created. This is a similiar situation to a function defined as follows (define func (let ((x 0)) (lambda () (set! x (+ x 1)) x))) Even if x is defined where this function is called the only x that will be set and returned is the one in the let.
pscho...@uci.edu wrote: > Well I think one change you should make is that the parameters passed > to a function should always be evaluated in their current namespace. > For example consider this situation with my-plus being defined in ns. > (let ((x 4) (y 7)) > (my-plus x y)) > I think everyone would expect the answer to be 11, but if x or y ever > are defined in ns the answer will change, at least that is how I am > reading your description. Especially when working with other people it > may be hard ot keep track of what variables are used in which > namespaces. Thus to evaluate the parameters in the functions namespace > I think you should use the following: > (my-plus (ns x) (ns y)) which makes it clear what is happening.
I think I was unclear in my explanation. First of all, "(let ((x 4) (y 7)) (my-plus x y))" doesn't reference a namespace, so everything will happen in the current namespace. But say my-plus were in namespace ns, and you called it as "(let ((x 4) (y 7)) ((ns my-plus) x y))". It would behave exactly the same way. x and y would be evaluated in the current namespace.
The only difference between the two would be if my-plus had any side-effects. In the first case, obviously, it would affect the local namespace, because that's where the function lives. In the second case, the side-effects would affect the namespace that contains the my-plus function. Nevertheless, the parameters are evaluated in the calling namespace in both cases.
You example, "(my-plus (ns x) (ns y))", would work just fine. It would call a function in the local namespace, getting parameters from another namespace.
> Another benefit to this method is that parameters can be from different > namspaces. Consider the situation where you have a function my-map > defined in A, the function you wish to pass it in B, and the list in C. > I don't know how you would make this work under your current model, > but under my suggestion it should look like: (my-map (B func) (C lst))
Let's say you're in the default namespace (TOP), my-map is in A, func is in B, and lst is in C, and x is in TOP. In that case, the syntax would be: ((A my-map) (B func) (C lst) x)
my-map would be fetched from A, but it would have A associated with it. Func would be fetched from B, and it would have B associated with it. lst is just a list so it's just fetched from C. And x is fetched from TOP. Next, when A::my-map is running, it wants to call func. func's parameters would be evaluated in A, where my-map lives, but when executing func, it would run in the context of B, where func lives.
> This shouldn't affect a function defined in ns that sets a varaible z, > no matter where it is called from the z it sets should be in ns,
Yes. Unless you dissociate the function from ns, which you can do, by stripping it of its namespace.
> because references are bound when the function is created. This is a > similiar situation to a function defined as follows > (define func > (let ((x 0)) > (lambda () (set! x (+ x 1)) x))) > Even if x is defined where this function is called the only x that will > be set and returned is the one in the let.
In article <1142963316.178420.115...@g10g2000cwb.googlegroups.com>,
theo...@gmail.com wrote: > The title is perhaps a bit misleading. I took at grad course in > programming languages where we had to develop a LISP interpreter. > Since then, I've been tinkering with it for fun, doing things to it. > And since I like Scheme more than LISP, it's evolved to be more like > Scheme than LISP. I'm betting that nothing I mention here is new, but > on the other hand, I haven't found much about it, so please forgive me > for being redundant. Since what I'm doing is very academic in nature, > I'm not overly concerned with making a compilable language.
What kind of LISP are you talking about. Nowadays we would write Lisp and this is a family of programming languages. Members of this family are Scheme, Common Lisp, ISLisp, EuLisp, AutoLisp and so on.
> Some of you may laugh or point me to old arguments about functional > programming versus object-oriented programming. While I'm interested > in reading about that, I have a particular idea that I'm tinkering with > that I'd like some suggestions on. I've looked at existing attempts to > add OOP to LISP, and I don't like them.
Which ones are you talking about? A typical Lisp with object-oriented features is Common Lisp.
> One of the problems for me is > that they heavily pollute the namespace. If you have a simple data > structure with three variables in it, you're going to get two functions > (get/set) for each variable.
In Common Lisp this is not true. You have to specify the names of GET/SET functions if you want them. You can name these functions in any namespace (called packages) you want. You can even avoid putting the function names into a namespace (package).
> It occurred to me that it might be nice to CLEAN UP the namespace. The > idea that occurred to me was to create a new data type that is a > namespace.
Called 'package' in Common Lisp.
> What it amounts to is a variable table that is bound to a > variable in another namespace.
> Say you wanted to load a socket library. It seems to me like it would > be nice to group all of those calls together into their own namespace.
Yes, that is a package in Common Lisp.
> The SOCKETLIB namespace would be bound to a variable in the global > namespace. When you wanted to access a function in there, you > reference it by namespace and by name. There are also some potential > performance advantages (at least for an interpreter).
There are no performance advantages.
...
> As I've been adding features that I haven't found in other lisps, I > have managed to come up with reasonably elegant ways to represent them > syntactically. For instance, in LISP, there is a distinction between a > function (which evaluates its arguments) and a special form (which does > not).
That's wrong.
It is not the function that evaluates the arguments. Actually the function will be called with already evaluated arguments.
> I have eliminated that distinction by allowing function > definitions to dictate which parameters are to be evaluated. For > instance: > (defun func (eval_param 'uneval_param) body) > In this case, uneval_param is quoted, indicating that the parameter in > the function call should be taken literally. This made things like my > internal definitions of DEFINE, DEFUN, QUOTE, and COND more elegant to > implement.
That was many years ago common in various Lisp dialects. It has been removed for several reasons. Scheme and Common Lisp don't provide this. There are only a very small amount of built-in and non-extensible special forms. The rest are macros and ordinary function.
> Back to namespaces. It seems reasonable that a namespace could be > treated, in a sense, like a function. That is, if you want to execute > something in a namespace, you would use this syntax: > (other_namespace (function_in_that_space parameters)) > Or, if you wanted to get a variable from a namespace, it would be like > this: > (other_namespace var_in_that_space)
I don't want this. A namespace is a construct that should only be use a READ time.
You are probably confusing 'namespaces' and 'environments'.
> Now to my problem. Given this syntax, I can execute a function whose > body and parameters are in another namespace: > (other_namespace (function_there variable_there)) > Here, 'variable_there' has to be in other_namespace. I could also call > a function from another namespace and execute it in the calling > namespace: > ((other_namespace function_there) variable_here) > Here, the parameter is evaluated in the calling namespace, but the the > function is also executed in the calling namespace.
> The trouble comes down to side-effects. I would like to be able to > call a function from another namespace, passing it parameters evaluated > in the calling namespace, and then execute it in the other namespace. > This way, I can pass in the parameters I like, but side-effects are > confined to the function's home namespace. This is necessary for good > OOP.
> I'm trying to come up with a way to represent this that is both > syntactically elegant and easy to implement in my interpreter.
> Any suggestions? :)
Yes, if you don't have it, run not walk and buy Christian Queinnec's Book 'Lisp' (Lisp in Small Pieces).