Gmail Calendar Documents Reader Web more »
Recently Visited Groups | Help | Sign in
Google Groups Home
Personal project, extending Scheme to OOP
There are currently too many topics in this group that display first. To make this topic appear first, remove this option from another topic.
There was an error processing your request. Please try again.
flag
  Messages 1 - 25 of 28 - Collapse all  -  Translate all to Translated (View all originals)   Newer >
The group you are posting to is a Usenet group. Messages posted to this group will make your email address visible to anyone on the Internet.
Your reply message has not been sent.
Your post was successful
 
From:
To:
Cc:
Followup To:
Add Cc | Add Followup-to | Edit Subject
Subject:
Validation:
For verification purposes please type the characters you see in the picture below or the numbers you hear by clicking the accessibility icon. Listen and type the numbers you hear
 
theo...@gmail.com  
View profile  
 More options Mar 21 2006, 12:48 pm
Newsgroups: comp.lang.scheme
From: theo...@gmail.com
Date: 21 Mar 2006 09:48:36 -0800
Local: Tues, Mar 21 2006 12:48 pm
Subject: Personal project, extending Scheme to OOP
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.

Any suggestions?  :)


    Reply to author    Forward  
You must Sign in before you can post messages.
To post a message you must first join this group.
Please update your nickname on the subscription settings page before posting.
You do not have the permission required to post.
pscho...@uci.edu  
View profile  
(1 user)  More options Mar 21 2006, 3:48 pm
Newsgroups: comp.lang.scheme
From: pscho...@uci.edu
Date: 21 Mar 2006 12:48:11 -0800
Local: Tues, Mar 21 2006 3:48 pm
Subject: Re: Personal project, extending Scheme to OOP
How are you implementing inheritance?  Inheritance may be one of the
most useful features of any object system.

    Reply to author    Forward  
You must Sign in before you can post messages.
To post a message you must first join this group.
Please update your nickname on the subscription settings page before posting.
You do not have the permission required to post.
Ray Dillinger  
View profile  
(1 user)  More options Mar 21 2006, 4:40 pm
Newsgroups: comp.lang.scheme
From: Ray Dillinger <b...@sonic.net>
Date: Tue, 21 Mar 2006 13:40:31 -0800
Local: Tues, Mar 21 2006 4:40 pm
Subject: Re: Personal project, extending Scheme to OOP

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.

                                Bear


    Reply to author    Forward  
You must Sign in before you can post messages.
To post a message you must first join this group.
Please update your nickname on the subscription settings page before posting.
You do not have the permission required to post.
theo...@gmail.com  
View profile  
 More options Mar 21 2006, 5:56 pm
Newsgroups: comp.lang.scheme
From: theo...@gmail.com
Date: 21 Mar 2006 14:56:24 -0800
Local: Tues, Mar 21 2006 5:56 pm
Subject: Re: Personal project, extending Scheme to OOP

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.

How's that?  :)


    Reply to author    Forward  
You must Sign in before you can post messages.
To post a message you must first join this group.
Please update your nickname on the subscription settings page before posting.
You do not have the permission required to post.
pscho...@uci.edu  
View profile  
 More options Mar 21 2006, 5:59 pm
Newsgroups: comp.lang.scheme
From: pscho...@uci.edu
Date: 21 Mar 2006 14:59:11 -0800
Local: Tues, Mar 21 2006 5:59 pm
Subject: Re: Personal project, extending Scheme to OOP
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.

    Reply to author    Forward  
You must Sign in before you can post messages.
To post a message you must first join this group.
Please update your nickname on the subscription settings page before posting.
You do not have the permission required to post.
pscho...@uci.edu  
View profile  
 More options Mar 21 2006, 6:03 pm
Newsgroups: comp.lang.scheme
From: pscho...@uci.edu
Date: 21 Mar 2006 15:03:02 -0800
Local: Tues, Mar 21 2006 6:03 pm
Subject: Re: Personal project, extending Scheme to OOP
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.

    Reply to author    Forward  
You must Sign in before you can post messages.
To post a message you must first join this group.
Please update your nickname on the subscription settings page before posting.
You do not have the permission required to post.
pscho...@uci.edu  
View profile  
 More options Mar 21 2006, 6:10 pm
Newsgroups: comp.lang.scheme
From: pscho...@uci.edu
Date: 21 Mar 2006 15:10:49 -0800
Local: Tues, Mar 21 2006 6:10 pm
Subject: Re: Personal project, extending Scheme to OOP
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/

    Reply to author    Forward  
You must Sign in before you can post messages.
To post a message you must first join this group.
Please update your nickname on the subscription settings page before posting.
You do not have the permission required to post.
theo...@gmail.com  
View profile  
 More options Mar 21 2006, 6:19 pm
Newsgroups: comp.lang.scheme
From: theo...@gmail.com
Date: 21 Mar 2006 15:19:29 -0800
Local: Tues, Mar 21 2006 6:19 pm
Subject: Re: Personal project, extending Scheme to OOP

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)))

Although that is kinda icky.


    Reply to author    Forward  
You must Sign in before you can post messages.
To post a message you must first join this group.
Please update your nickname on the subscription settings page before posting.
You do not have the permission required to post.
theo...@gmail.com  
View profile  
 More options Mar 21 2006, 6:41 pm
Newsgroups: comp.lang.scheme
From: theo...@gmail.com
Date: 21 Mar 2006 15:41:34 -0800
Local: Tues, Mar 21 2006 6:41 pm
Subject: Re: Personal project, extending Scheme to OOP

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.

    Reply to author    Forward  
You must Sign in before you can post messages.
To post a message you must first join this group.
Please update your nickname on the subscription settings page before posting.
You do not have the permission required to post.
pscho...@uci.edu  
View profile  
 More options Mar 21 2006, 6:44 pm
Newsgroups: comp.lang.scheme
From: pscho...@uci.edu
Date: 21 Mar 2006 15:44:41 -0800
Local: Tues, Mar 21 2006 6:44 pm
Subject: Re: Personal project, extending Scheme to OOP
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.

    Reply to author    Forward  
You must Sign in before you can post messages.
To post a message you must first join this group.
Please update your nickname on the subscription settings page before posting.
You do not have the permission required to post.
theo...@gmail.com  
View profile  
 More options Mar 21 2006, 8:16 pm
Newsgroups: comp.lang.scheme
From: theo...@gmail.com
Date: 21 Mar 2006 17:16:11 -0800
Local: Tues, Mar 21 2006 8:16 pm
Subject: Re: Personal project, extending Scheme to OOP

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.


    Reply to author    Forward  
You must Sign in before you can post messages.
To post a message you must first join this group.
Please update your nickname on the subscription settings page before posting.
You do not have the permission required to post.
pscho...@uci.edu  
View profile  
 More options Mar 21 2006, 8:30 pm
Newsgroups: comp.lang.scheme
From: pscho...@uci.edu
Date: 21 Mar 2006 17:30:42 -0800
Local: Tues, Mar 21 2006 8:30 pm
Subject: Re: Personal project, extending Scheme to OOP
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.

    Reply to author    Forward  
You must Sign in before you can post messages.
To post a message you must first join this group.
Please update your nickname on the subscription settings page before posting.
You do not have the permission required to post.
Pascal Bourguignon  
View profile  
 More options Mar 21 2006, 9:16 pm
Newsgroups: comp.lang.scheme
From: Pascal Bourguignon <use...@informatimago.com>
Date: Wed, 22 Mar 2006 03:16:11 +0100
Local: Tues, Mar 21 2006 9:16 pm
Subject: Re: Personal project, extending Scheme to OOP

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:

[22]> (defmethod (setf c-x) ((value number) (object c))
           (setf (a-x object) value))
#<STANDARD-METHOD (#<BUILT-IN-CLASS NUMBER> #<STANDARD-CLASS C :VERSION 3>)>
[23]> (defmethod (setf c-x) ((value string) (object c))
           (setf (b-x object) value))
#<STANDARD-METHOD (#<BUILT-IN-CLASS STRING> #<STANDARD-CLASS C :VERSION 3>)>

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

--
__Pascal Bourguignon__                     http://www.informatimago.com/

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.


    Reply to author    Forward  
You must Sign in before you can post messages.
To post a message you must first join this group.
Please update your nickname on the subscription settings page before posting.
You do not have the permission required to post.
Ray Dillinger  
View profile  
 More options Mar 21 2006, 9:33 pm
Newsgroups: comp.lang.scheme
From: Ray Dillinger <b...@sonic.net>
Date: Tue, 21 Mar 2006 18:33:36 -0800
Local: Tues, Mar 21 2006 9:33 pm
Subject: Re: Personal project, extending Scheme to OOP

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.

                                Bear


    Reply to author    Forward  
You must Sign in before you can post messages.
To post a message you must first join this group.
Please update your nickname on the subscription settings page before posting.
You do not have the permission required to post.
theo...@gmail.com  
View profile  
 More options Mar 21 2006, 9:43 pm
Newsgroups: comp.lang.scheme
From: theo...@gmail.com
Date: 21 Mar 2006 18:43:01 -0800
Local: Tues, Mar 21 2006 9:43 pm
Subject: Re: Personal project, extending Scheme to OOP

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.

What would be a better design?

    Reply to author    Forward  
You must Sign in before you can post messages.
To post a message you must first join this group.
Please update your nickname on the subscription settings page before posting.
You do not have the permission required to post.
pscho...@uci.edu  
View profile  
 More options Mar 22 2006, 2:11 am
Newsgroups: comp.lang.scheme
From: pscho...@uci.edu
Date: 21 Mar 2006 23:11:07 -0800
Local: Wed, Mar 22 2006 2:11 am
Subject: Re: Personal project, extending Scheme to OOP

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.

    Reply to author    Forward  
You must Sign in before you can post messages.
To post a message you must first join this group.
Please update your nickname on the subscription settings page before posting.
You do not have the permission required to post.
pscho...@uci.edu  
View profile  
 More options Mar 22 2006, 2:19 am
Newsgroups: comp.lang.scheme
From: pscho...@uci.edu
Date: 21 Mar 2006 23:19:21 -0800
Local: Wed, Mar 22 2006 2:19 am
Subject: Re: Personal project, extending Scheme to OOP
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?

    Reply to author    Forward  
You must Sign in before you can post messages.
To post a message you must first join this group.
Please update your nickname on the subscription settings page before posting.
You do not have the permission required to post.
Shiro Kawai  
View profile  
 More options Mar 22 2006, 6:39 am
Newsgroups: comp.lang.scheme
From: "Shiro Kawai" <shiro.ka...@gmail.com>
Date: 22 Mar 2006 03:39:16 -0800
Local: Wed, Mar 22 2006 6:39 am
Subject: Re: Personal project, extending Scheme to OOP

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.)

--shiro


    Reply to author    Forward  
You must Sign in before you can post messages.
To post a message you must first join this group.
Please update your nickname on the subscription settings page before posting.
You do not have the permission required to post.
theo...@gmail.com  
View profile  
 More options Mar 22 2006, 9:01 am
Newsgroups: comp.lang.scheme
From: theo...@gmail.com
Date: 22 Mar 2006 06:01:43 -0800
Local: Wed, Mar 22 2006 9:01 am
Subject: Re: Personal project, extending Scheme to OOP

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;


    Reply to author    Forward  
You must Sign in before you can post messages.
To post a message you must first join this group.
Please update your nickname on the subscription settings page before posting.
You do not have the permission required to post.
Pascal Bourguignon  
View profile  
 More options Mar 22 2006, 9:47 am
Newsgroups: comp.lang.scheme
From: Pascal Bourguignon <use...@informatimago.com>
Date: Wed, 22 Mar 2006 15:47:35 +0100
Local: Wed, Mar 22 2006 9:47 am
Subject: Re: Personal project, extending Scheme to OOP

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:

(defclass named    ()      (name))
(defclass physical (named) (mass))
(defclass storable (named) (size))
(defclass stophy   (storable physical) ())

Here, a stophy has only one name, as a named object.
( One could argue the class graph is wrong and should be done as:

   (defclass named    ()      (name))
   (defclass massive  ()      (mass))
   (defclass sizeable ()      (size))
   (defclass physical (named massive)          ())
   (defclass storable (named sizeable)         ())
   (defclass stophy   (named massive sizeable) ())

   This is to say, don't have attribute collisions in mixin!
)

or:

(defclass mobile   ()       (speed))
(defclass flying   (mobile) (altitude))
(defclass running  (mobile) (resistance))
(defclass albatros (flying running))

Here, an albatros can run at one speed, and can fly at another...

The class graphs programmers will have to designs will depend on your
object system rules.

--
__Pascal Bourguignon__                     http://www.informatimago.com/

"Indentation! -- I will show you how to indent when I indent your skull!"


    Reply to author    Forward  
You must Sign in before you can post messages.
To post a message you must first join this group.
Please update your nickname on the subscription settings page before posting.
You do not have the permission required to post.
pscho...@uci.edu  
View profile  
 More options Mar 22 2006, 2:57 pm
Newsgroups: comp.lang.scheme
From: pscho...@uci.edu
Date: 22 Mar 2006 11:57:55 -0800
Local: Wed, Mar 22 2006 2:57 pm
Subject: Re: Personal project, extending Scheme to OOP
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.

    Reply to author    Forward  
You must Sign in before you can post messages.
To post a message you must first join this group.
Please update your nickname on the subscription settings page before posting.
You do not have the permission required to post.
theo...@gmail.com  
View profile  
 More options Mar 22 2006, 5:24 pm
Newsgroups: comp.lang.scheme
From: theo...@gmail.com
Date: 22 Mar 2006 14:24:21 -0800
Local: Wed, Mar 22 2006 5:24 pm
Subject: Re: Personal project, extending Scheme to OOP
Here's what I've implemented on this now:

You can create namespaces:

> (define ns (namespace))

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.  :)

It's certainly far from complete.

Thoughts?


    Reply to author    Forward  
You must Sign in before you can post messages.
To post a message you must first join this group.
Please update your nickname on the subscription settings page before posting.
You do not have the permission required to post.
pscho...@uci.edu  
View profile  
 More options Mar 22 2006, 5:45 pm
Newsgroups: comp.lang.scheme
From: pscho...@uci.edu
Date: 22 Mar 2006 14:45:13 -0800
Local: Wed, Mar 22 2006 5:45 pm
Subject: Re: Personal project, extending Scheme to OOP
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.

-Peter: http://pschombe.wordpress.com/


    Reply to author    Forward  
You must Sign in before you can post messages.
To post a message you must first join this group.
Please update your nickname on the subscription settings page before posting.
You do not have the permission required to post.
theo...@gmail.com  
View profile  
 More options Mar 22 2006, 7:11 pm
Newsgroups: comp.lang.scheme
From: theo...@gmail.com
Date: 22 Mar 2006 16:11:17 -0800
Local: Wed, Mar 22 2006 7:11 pm
Subject: Re: Personal project, extending Scheme to OOP

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.

Yes, this is analogous.

    Reply to author    Forward  
You must Sign in before you can post messages.
To post a message you must first join this group.
Please update your nickname on the subscription settings page before posting.
You do not have the permission required to post.
Rainer Joswig  
View profile  
 More options Mar 22 2006, 7:37 pm
Newsgroups: comp.lang.scheme
From: Rainer Joswig <jos...@lisp.de>
Date: Thu, 23 Mar 2006 01:37:58 +0100
Local: Wed, Mar 22 2006 7:37 pm
Subject: Re: Personal project, extending Scheme to OOP
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'.

Yes, if you don't have it, run not walk and buy
Christian Queinnec's Book 'Lisp' (Lisp in Small Pieces).

http://www-spi.lip6.fr/~queinnec/WWW/LiSP.html

--
http://lispm.dyndns.org/


    Reply to author    Forward  
You must Sign in before you can post messages.
To post a message you must first join this group.
Please update your nickname on the subscription settings page before posting.
You do not have the permission required to post.
Messages 1 - 25 of 28   Newer >
« Back to Discussions « Newer topic     Older topic »

Create a group - Google Groups - Google Home - Terms of Service - Privacy Policy
©2009 Google