Opened 10 years ago

Closed 10 years ago

#32 closed defect (fixed)

get tinyclos egg for chicken 4 to work

Reported by: felix winkelmann Owned by: Tony Sidaway
Priority: major Milestone:
Component: extensions Version: 4.0.5
Keywords: tinyclos Cc:
Estimated difficulty:

Description

tinyclos has been imported into the release/4 branch, but currently tinyclos-examples.scm fails.

Attachments (6)

3.scm (61.4 KB) - added by Tony Sidaway 10 years ago.
Trimmed version of version 3 egg, for comparison using diff
4.scm (61.7 KB) - added by Tony Sidaway 10 years ago.
Trimmed version of the version 4 egg, for comparison using diff
support.diff (68 bytes) - added by Tony Sidaway 10 years ago.
diff for support.scm as downloaded from Xeroc Parc website--hacks to make it work for modern MIT Scheme
tiny-clos.diff (104 bytes) - added by Tony Sidaway 10 years ago.
diff for tiny-clos.scm as downloaded from Xeroc Parc website--hacks to make it work for modern MIT Scheme
tiny-examples.scm (10.2 KB) - added by Tony Sidaway 10 years ago.
diff for tiny-examplesscm as downloaded from Xeroc Parc website--hacks to make it work for modern MIT Scheme
tiny-examples.diff (1.5 KB) - added by Tony Sidaway 10 years ago.
diff for tiny-examples.scm as downloaded from Xeroc Parc website--hacks to make it work for modern MIT Scheme

Download all attachments as: .zip

Change History (23)

comment:1 Changed 10 years ago by Tony Sidaway

Is anybody else looking at this? I don't yet understand the innards of this very complex code.

tinyclos-examples.scm flops over because, I think, the initialize generic is getting a bogus call-next-method chain.

Changing "(##core-global-ref ,name)" to ",name" in define-method seems to help. Then all you have to do to stop define-method complaining during ensure-generic is to insert "(define-generic X)" before every reference to define-method.

Then it goes into a black hole and you have to kill the process...

I don't understand how module namespaces work in Chicken so I'm completely mystified by the whole thing. Is there documentation that might help?

comment:2 in reply to:  1 ; Changed 10 years ago by felix winkelmann

Replying to tonysidaway:

Is anybody else looking at this? I don't yet understand the innards of this very complex code.

I haven't looked for a while. Yes, the code is a horrible mess, I don't understand it myself anymore. I actually would throw it away, and replace it with a generic-function library written from scratch, but many people have used tinyclos and having it at least working would be good.

tinyclos-examples.scm flops over because, I think, the initialize generic is getting a bogus call-next-method chain.

Changing "(##core-global-ref ,name)" to ",name" in define-method seems to help. Then all you have to do to stop define-method complaining during ensure-generic is to insert "(define-generic X)" before every reference to define-method.

Then it goes into a black hole and you have to kill the process...

I don't understand how module namespaces work in Chicken so I'm completely mystified by the whole thing. Is there documentation that might help?

No, not really - only the comments in the code. The thing *did* work, and I have absolutely no idea why it doesn't now.

comment:3 in reply to:  2 Changed 10 years ago by Christian Kellermann

Replying to felix:

Replying to tonysidaway:

Changing "(##core-global-ref ,name)" to ",name" in define-method seems to help. Then all you have to do to stop define-method complaining during ensure-generic is to insert "(define-generic X)" before every reference to define-method.

Actually this will work for the current namespace but will not introduce symbols globally as I have found out.

The reason for the current implementations failure is more deeper as I am afraid. For example: Why isn't the call-next-method installed upon the first invocation of define-class (make class....)?

comment:4 Changed 10 years ago by Christian Kellermann

Is there a description of available slots and how they are used somewhere? Either that is used wrong here or ##core#global-ref is used wrong here it seems to me atm.

Changed 10 years ago by Tony Sidaway

Attachment: 3.scm added

Trimmed version of version 3 egg, for comparison using diff

Changed 10 years ago by Tony Sidaway

Attachment: 4.scm added

Trimmed version of the version 4 egg, for comparison using diff

comment:5 Changed 10 years ago by Tony Sidaway

I think it unlikely that the basic code is wrong as it hasn't changed much for some time. Only the support macros and the module trappings have changed. The other day I merged the two source files of the version 3 egg, and then removed the module/unit trappings from each file and ran a diff. This gives a way of focusing on the code changes.

See files 3.scm and 4.scm attached.

Caution: I haven't used Chicken 3 or a working version of tinyclos for some time, and I assume that the egg in the version 3 repository works with Chicken 3.

comment:6 Changed 10 years ago by felix winkelmann

I'm sorry that I can't be of much help here, but it's a long time since I touched that code, and the stuff has been optimized so heavily that it's practically inscrutable.

I appreciate that you look into this, but the effort to resurrect it may be higher than starting a clean implementation from scratch.

comment:7 Changed 10 years ago by Tony Sidaway

I've already looked at the possibility of a clean start--in the sense of beginning with an unhacked copy of tinyclos and taking it from there.

ftp://ftp.parc.xerox.com/pub/mops/tiny/

The code, alas, is more than 15 years old, pre-IEEE, and no longer even runs on the systems for which it was originally developed.

This is what happens when I run a really old version in MIT Scheme:

Image saved on Thursday March 12, 2009 at 4:17:10 AM

Release 7.7.90.+
Microcode 15.1 Runtime 15.7 SF 4.41 LIAR/i386 4.118 Edwin 3.116

1 ]=> (load "tiny-clos")

;Loading "tiny-clos.scm"...
; Loading "support.scm"... done
;... done
;Value: tiny-clos-up-and-running

1 ]=> (load "tiny-examples")

;Loading "tiny-examples.scm"...
;The object #f, passed as the first argument to set-cdr!, is not the correct type.

I suspect the problem there is that the code relies on old MIT Scheme semantics.

For what it's worth, the only set-cdr! in the code is in compute-getter-and-setter in tiny-examples.scm. MIT Scheme is apparently barfing on this:

(let* ((alist (alist-getter o))

(entry (assq name alist)))

(if (null? entry)

(alist-setter o

(cons (cons name new) alist))

(set-cdr! entry new)))

In this instance obviously the problem is that the code is using the null? predicate when it should be testing for a true/false response from assq--this is typical of the ambiguities of pre-IEEE Scheme, I believe.

This particular bug is easily fixed, but obviously the code would have to be very carefully scrutinized.

Changed 10 years ago by Tony Sidaway

Attachment: support.diff added

diff for support.scm as downloaded from Xeroc Parc website--hacks to make it work for modern MIT Scheme

Changed 10 years ago by Tony Sidaway

Attachment: tiny-clos.diff added

diff for tiny-clos.scm as downloaded from Xeroc Parc website--hacks to make it work for modern MIT Scheme

Changed 10 years ago by Tony Sidaway

Attachment: tiny-examples.scm added

diff for tiny-examplesscm as downloaded from Xeroc Parc website--hacks to make it work for modern MIT Scheme

Changed 10 years ago by Tony Sidaway

Attachment: tiny-examples.diff added

diff for tiny-examples.scm as downloaded from Xeroc Parc website--hacks to make it work for modern MIT Scheme

comment:8 Changed 10 years ago by Tony Sidaway

Third attachment today, tiny-examples.scm, was a mistake. See diff in fourth attachment.

This now seems to work with MIT Scheme and although I haven't tried it, it should work with Chicken with little or no change. The macros are not present in this version so none of the syntactic sugar like define-generic, define-method, define-class, etc. This *should* be easy to fix.

The primitive class definitions in the Chicken version are much more extensive than here, but that should also be easy to remedy.

By the way if you're downloading tinyclos from the Xerox PARC ftp site (URL in a previous comment on this trac ticket) I recommend using a proper ftp client. My web browser made a mess of downloading the source files and I had to go back with command-line ftp.

comment:9 Changed 10 years ago by Tony Sidaway

Another approach would be to go for a truly clean start. Personally I'm reluctant to do that because there are dozens of object systems around and the design of tinyclos is pretty good as these things go. It's based on a MOP design and it's very flexible.

I hope hacking at the Xeroc Parc version will give me enough insight into the basic design to let me refactor the Chicken 3 version into something maintainable and fix the bugs. If not, I'll build on the Xerox version until I have a workable clone.

comment:10 Changed 10 years ago by Tony Sidaway

I checked in the code at https://galinha.ucpel.tche.br/svn/chicken-eggs/release/4/tinyclos-xerox

The version at tags/0.0.0 is as downloaded, with the addition of the file README.chicken
The version at tags/0.0.1 seems to run under modern MIT Scheme and the tests directory contains a test script (run it with the command scheme < mit-scheme.test >logfile )

There is also a copy of a log file from a test run in that directory.

It seems to run okay with Chicken if you use csi -R srfi-1

I haven't gone through the results line-by-line but there don't seem to be any show-stoppers.

comment:11 Changed 10 years ago by Tony Sidaway

Owner: set to Tony Sidaway
Status: newassigned

I have rewritten define-method to expand to a corresponding add-method. This throws an appropriate error if the generic has not already been set up. With appropriate "define-generic" calls, the example code now executes without error messages. I need to set up a proper test suite for this but I think we're out of the woods.

My tests seem to indicate that the problem is a basic design fault in define-method. This macro is supposed to do two things. The ostensible purpose is to provide syntactic sugar around the assignment of a new method to an existing generic. At some point the decision was made to have define-method create the generic if it didn't already exist. This never seems to have worked satisfactorily, and the documentation in eggref 3 discusses its limitations and recommends the use of define-generic. If my tests continue to indicate that the code is basically reliable, I'll release the egg for Chicken 4 with amended documentation indicating that define-generic is now mandatory.

As Felix has indicated, the code has been heavily optimized and it can be a bit intimidating. I think it can be cleaned up a bit, though. The primitive class system can be put into a file of its own, as can much of the caching code, the generic invocation subsystem and the class bootstrap sequence. Dividing the code up by functional area in this way would make the subsystems easier to understand. And of course much easier to document. I'll also write a proper test suite for this important egg.

comment:12 in reply to:  11 Changed 10 years ago by felix winkelmann

Replying to tonysidaway:

I have rewritten define-method to expand to a corresponding add-method. This throws an appropriate error if the generic has not already been set up. With appropriate "define-generic" calls, the example code now executes without error messages. I need to set up a proper test suite for this but I think we're out of the woods.

Well done, Tony! The dual nature of define-method is indeed problematic. I found it convenient not having to define the generic, but this gets problematic with the module system. I guess, simply requiring the define-generic is the correct way.

comment:13 Changed 10 years ago by Tony Sidaway

felix, I've been thinking of this all morning. The notion that define-method used by itself will introduce a generic is from CLOS, and presumably the introduction of a top-level binding for the generic is an attempt to mimic the semantics of CLOS. I don't know Common Lisp but I assume it lacks the Algol-like block structure and strict lexical scope that make those semantics problematic in Scheme. So there are really two problems here: firstly a macro that normally modifies an existing object, a generic method object, but sometimes creates a new generic object; and secondly the binding created is supposed to be at top level.

These semantics also seem to apply in Erick Gallesio's object module in STKlos, but I still think they're wrong in Scheme. An inner block probably shouldn't create new top-level bindings in a lexically scoped block-structured language.

comment:14 in reply to:  13 Changed 10 years ago by felix winkelmann

Replying to tonysidaway:

These semantics also seem to apply in Erick Gallesio's object module in STKlos, but I still think they're wrong in Scheme. An inner block probably shouldn't create new top-level bindings in a lexically scoped block-structured language.

I absolutely agree. It's also a difference in development styles: in CL you normally have a long-running image and you need a way to wipe out all methods of a generic function (this is what DEFGENERIC does). With a batch-compiler this is not that important. I think one should require a define-generic, to have a clear indication of where the generic function is defined. Fiddling with the bound/unbound status of toplevel variables like this appears crude - one defines a variable "potentially", depending on whether a generic function already exists or not.

comment:15 Changed 10 years ago by Tony Sidaway

Moving on I've discovered two bugs in the primitive class system. Both of these bugs also exist in Chicken 3.4.0, which I have downloaded and installed.

1: describe-object encounters an error if given an instance of a primitive class. For instance in evaluating (describe-object #t) or (let ((a 1))(describe-object a)), the describe-object generic reports that it has run out of methods.

Looking at the class hierarchy, <primitive> is a direct descendant of <top>, and describe-object only has methods for <object> and its descendants. The problem seems to go away if I define an ad hoc describe-object method for <primitive>, so this is probably a simple case of omission of a required method from the generic describe-object in the code of the egg.

2: it is possible to use "make" to create an instance of a primitive class, but the resulting object cannot be evaluated. (let ((o (make <object>))) o) evaluates to #<object> but (let ((b (make <boolean>))) b) goes into a black hole during evaluation of b.

I would expect "make" to special-case the creation of primitives, evaluating not to a Tinyclos instance but to an object of the underlying Scheme type with a predictable value (for instance (make <boolean>) could create the object #f, (make <integer>) could create the object 0, and (make <char>) could create the character #\x000 and (make <string>) could create the object ""). There are alternative strategies which might make more sense, but the point is that the primitive classes don't really work properly at present and this should probably be easy to fix in a way that makes sense in the context of Scheme.

comment:16 Changed 10 years ago by Tony Sidaway

On reflection I think the result of (make <primitive>) (or any subclass of <primitive>) should be unspecified. Primitives are "built-in", that's what their equivalent in CLOS is called. They don't need to be synthesized, they come free with the language (albeit a language extended by SRFI-4 and the like).

Having primitives work with class-of, describe-object and so on is useful, providing a more wordy way to produce the basic building blocks of the language isn't--especially as it might require considerable effort to get it right.

On the first bug I mentioned yesterday, where describe-object generates an error if the object is a member of the class <primitive>: that was easily fixed as I indicated.

comment:17 Changed 10 years ago by Tony Sidaway

Resolution: fixed
Status: assignedclosed

I'm closing this as fixed but without unhiding the egg.

The code as it stands passes 47 basic unit tests. While some aspects of tinyclos are still not tested (make-primitive-class and delete-primitive-class, for example) it seems to work well enough to test primitive class recognition, polymorphism, the correct operation of generics, the slot reference mechanism, and inheritance.

Note: See TracTickets for help on using tickets.