From: Matthias R. <mat...@so...> - 2002-06-02 10:24:35
|
Hi JSchemers, I've been following the recent "SISC and JScheme java interfaces" thread. As the author of SISC's Scheme-to-Java interface I figured it might be a good idea to explain some of its features and the design rationale behind them. The main differences between the SISC and JScheme approach are the following: 1) JScheme uses special syntax to invoke Java methods. SISC uses a set of primitives, procedures and macros to represent Java methods as generic procedures. The special syntax approach makes JScheme code very compact and readable, at the expense of chewing up a portion of the name space. In SISC every Java method has to declared using (define-generic foo ...) One pervasive concept throughout SISC's generic procedure mechanism is that of chaining - the chainee procedure gets invoked when the chainer procedure cannot find a matching method on invocation. The most common place where this is used is in order to add Scheme-level methods "in front" of Java methods, basically performing a kind of overloading. This means you can do things like this: (define-generic app (generic-java-procedure 'append)) (define-method (app (next: next-method) (<jstringbuffer> buf) . rest) (for-each (lambda (x) (next-method buf x)) rest) buf) (define sb (make <jstringbuffer> (->jstring "foo"))) (app sb (->jstring "foo") (->jint 1) (->jstring "bar")) ;==> stringbuffer now contains "foofoo1bar". Note that because SISC uses no special notation for calling Java methods, the same name as before can be used to call the new procedure. Here's a list of some other interesting features of SISC generic procedures: * they are lexically scoped; One can define the same generic procedure name in multiple scopes/modules withouth them inteferring with each other. * the first parameter is not special. It participates in method selection just like any other parameter. * The names of generic procedures by default are mapped to Java method names via an "unschemification" conversion, e.g. foo-bar-baz? becomes isFooBarBaz. This further blurs the distinction between doing "schemy" things and "javay" things. * constructors are generic procedures too. * next-method parameter for calling next matching method Note that at the moment SISC generic procedures can only operate on Java objects, not Scheme objects (unless, of course, you use their Java representation). That will change soon though with the introduction of a SISC type system and object system. 2) JScheme uses special syntax to access static and instance variables. Field access in SISC is done by calling the Java object with a slot name and optional value. SISC consequently allows one to write (map 'foo (list obj1 obj2 obj3)) to get the value of the field "foo" of three objects. I'm not sure what the equivalent would look like in JScheme. 3) JScheme defines special Listener classes that allow the definition of Java listeners in Scheme. SISC has a generic proxy mechanims that allows any Java interface to be implemented in Scheme. SISC's generic proxy mechanism is somewhat clunky at the moment but this will improve considerably with the introduction of the type and object system. 4) Many JScheme types map directly to standard Java types, making type conversion unnecessary when objects cross the Scheme/Java divide. In SISC explicit conversion is needed. The JScheme solution results in incredibly concise code at the expense of losing some standards compliance (e.g. mutable strings) and generality. The precise reasons, straight from the SISC manual, why SISC opted for explict rather than implict conversion are: * For some Scheme types, such as numbers, the mapping to Java types is one-to-many, e.g. a Scheme number could be converted to a byte, short, int, etc. This causes ambiguities when automatic conversion of parameters is attempted. * Some Java types have several corresponding Scheme types, e.g. a Java array could be represented as Scheme list or vector - this causes ambiguities when automatic conversion of results is attempted. * Conversion carries an overhead that can be significant. For instance, Java strings have to be copied "by value" to Scheme strings since the former are immutable and the latter aren't. In a chained-call scenario, i.e. where the results of one method invocation are passed as arguments to another, the conversion is unnecessary and a wasted effort. * Conversion breaks the object identity relationship. In a chained-call scenario, the identities of the objects passed to the second call are different from the ones returned by the first. This causes problems if the called Java code relies on the object identity being preserved. * Conversion conflicts with generic procedures. The method selection mechanism employed by generic procedures relies on objects having exactly one type. Automatic conversion effectively gives objects more than one type - their original type and the type of the objects they can be converted to. While it would be technically possible to devise a method selection algorithm that accommodates this, the algorithm would impose a substantial overhead on generic procedure invocation and also make it significantly harder for users to predict which method will be selected when invoking a generic procedure with a particular set of arguments. Matthias |
From: Geoffrey S. K. <ge...@kn...> - 2002-06-02 12:04:31
|
Would it be possible to marry the type-predictability of SISC with the syntactic sugar of JScheme, so that type conversions need be done only once? On Sun, 2002-06-02 at 06:16, Matthias Radestock wrote: > Hi JSchemers, > > I've been following the recent "SISC and JScheme java interfaces" > thread. As the author of SISC's Scheme-to-Java interface I figured it > might be a good idea to explain some of its features and the design > rationale behind them. > > The main differences between the SISC and JScheme approach are the > following: > > 1) JScheme uses special syntax to invoke Java methods. SISC uses a set > of primitives, procedures and macros to represent Java methods as > generic procedures. > > The special syntax approach makes JScheme code very compact and > readable, at the expense of chewing up a portion of the name space. In > SISC every Java method has to declared using > (define-generic foo ...) > > One pervasive concept throughout SISC's generic procedure mechanism is > that of chaining - the chainee procedure gets invoked when the chainer > procedure cannot find a matching method on invocation. > The most common place where this is used is in order to add Scheme-level > methods "in front" of Java methods, basically performing a kind of > overloading. This means you can do things like this: > > (define-generic app (generic-java-procedure 'append)) > (define-method (app (next: next-method) > (<jstringbuffer> buf) . rest) > (for-each (lambda (x) (next-method buf x)) rest) > buf) > (define sb (make <jstringbuffer> (->jstring "foo"))) > (app sb (->jstring "foo") (->jint 1) (->jstring "bar")) > ;==> stringbuffer now contains "foofoo1bar". > > Note that because SISC uses no special notation for calling Java > methods, the same name as before can be used to call the new procedure. > > Here's a list of some other interesting features of SISC generic procedures: > > * they are lexically scoped; One can define the same generic procedure > name in multiple scopes/modules withouth them inteferring with each other. > > * the first parameter is not special. It participates in method > selection just like any other parameter. > > * The names of generic procedures by default are mapped to Java method > names via an "unschemification" conversion, e.g. foo-bar-baz? becomes > isFooBarBaz. This further blurs the distinction between doing "schemy" > things and "javay" things. > > * constructors are generic procedures too. > > * next-method parameter for calling next matching method > > Note that at the moment SISC generic procedures can only operate on Java > objects, not Scheme objects (unless, of course, you use their Java > representation). That will change soon though with the introduction of a > SISC type system and object system. > > > 2) JScheme uses special syntax to access static and instance variables. > Field access in SISC is done by calling the Java object with a slot name > and optional value. SISC consequently allows one to write > (map 'foo (list obj1 obj2 obj3)) > to get the value of the field "foo" of three objects. I'm not sure what > the equivalent would look like in JScheme. > > > 3) JScheme defines special Listener classes that allow the definition of > Java listeners in Scheme. SISC has a generic proxy mechanims that allows > any Java interface to be implemented in Scheme. > > SISC's generic proxy mechanism is somewhat clunky at the moment but this > will improve considerably with the introduction of the type and object > system. > > > 4) Many JScheme types map directly to standard Java types, making type > conversion unnecessary when objects cross the Scheme/Java divide. In > SISC explicit conversion is needed. > > The JScheme solution results in incredibly concise code at the expense > of losing some standards compliance (e.g. mutable strings) and > generality. The precise reasons, straight from the SISC manual, why SISC > opted for explict rather than implict conversion are: > > * For some Scheme types, such as numbers, the mapping to Java types is > one-to-many, e.g. a Scheme number could be converted to a byte, short, > int, etc. This causes ambiguities when automatic conversion of > parameters is attempted. > > * Some Java types have several corresponding Scheme types, e.g. a Java > array could be represented as Scheme list or vector - this causes > ambiguities when automatic conversion of results is attempted. > > * Conversion carries an overhead that can be significant. For instance, > Java strings have to be copied "by value" to Scheme strings since the > former are immutable and the latter aren't. In a chained-call scenario, > i.e. where the results of one method invocation are passed as arguments > to another, the conversion is unnecessary and a wasted effort. > > * Conversion breaks the object identity relationship. In a chained-call > scenario, the identities of the objects passed to the second call are > different from the ones returned by the first. This causes problems if > the called Java code relies on the object identity being preserved. > > * Conversion conflicts with generic procedures. The method selection > mechanism employed by generic procedures relies on objects having > exactly one type. Automatic conversion effectively gives objects more > than one type - their original type and the type of the objects they can > be converted to. While it would be technically possible to devise a > method selection algorithm that accommodates this, the algorithm would > impose a substantial overhead on generic procedure invocation and also > make it significantly harder for users to predict which method will be > selected when invoking a generic procedure with a particular set of > arguments. > > > Matthias > > > _______________________________________________________________ > > Don't miss the 2002 Sprint PCS Application Developer's Conference > August 25-28 in Las Vegas -- http://devcon.sprintpcs.com/adp/index.cfm > > _______________________________________________ > Jscheme-devel mailing list > Jsc...@li... > https://lists.sourceforge.net/lists/listinfo/jscheme-devel > -- Geoffrey S. Knauth http://knauth.org/gsk |
From: Matthias R. <mat...@so...> - 2002-06-02 14:54:40
|
Geoffrey S. Knauth wrote: > Would it be possible to marry the type-predictability of SISC with the > syntactic sugar of JScheme, so that type conversions need be done only > once? > I'm not quite sure what you mean by "only once". Can you elaborate, please? Matthias. |
From: Timothy H. <tim...@ma...> - 2002-06-03 04:28:30
|
Hi Matthias, Thanks for giving us your insights on the SISC java interface implementation... On Sunday, June 2, 2002, at 06:16 AM, Matthias Radestock wrote: > Hi JSchemers, > > I've been following the recent "SISC and JScheme java interfaces" > thread. As the author of SISC's Scheme-to-Java interface I figured it > might be a good idea to explain some of its features and the design > rationale behind them. > > The main differences between the SISC and JScheme approach are the > following: > > 1) JScheme uses special syntax to invoke Java methods. SISC uses a set > of primitives, procedures and macros to represent Java methods as > generic procedures. > > The special syntax approach makes JScheme code very compact and > readable, at the expense of chewing up a portion of the name space. The name space is chewed up, but not fully digested! In fact, Jscheme just provides initial bindings to the javadot symbols, but you can easily redefine them. e.g., > (define Date. (let ((d Date.)) (lambda() (string-append "the date is " (d))))) > (Date.) "the date is Sun Jun 02 23:25:02 EDT 2002" > (define (f Date.) (* Date. Date.)) {jsint.Closure f[1] (Date.)} > (f 4) 16 Thus, javadot can be though of as a loading a large module of primitives (although the bindings are not made until the first time the symbol value is looked up) > In SISC every Java method has to declared using > (define-generic foo ...) > > One pervasive concept throughout SISC's generic procedure mechanism is > that of chaining - the chainee procedure gets invoked when the chainer > procedure cannot find a matching method on invocation. > The most common place where this is used is in order to add > Scheme-level methods "in front" of Java methods, basically performing a > kind of overloading. This means you can do things like this: > > (define-generic app (generic-java-procedure 'append)) > (define-method (app (next: next-method) > (<jstringbuffer> buf) . rest) > (for-each (lambda (x) (next-method buf x)) rest) > buf) > (define sb (make <jstringbuffer> (->jstring "foo"))) > (app sb (->jstring "foo") (->jint 1) (->jstring "bar")) > ;==> stringbuffer now contains "foofoo1bar". > > Note that because SISC uses no special notation for calling Java > methods, the same name as before can be used to call the new procedure. Very nice! The chaining idea is an interesting way to add functionality to java classes or to other scheme procedures. Its seems to be similar to inheritance, except that the parent procedures have priority rather than the other way round. Jscheme does have a form of multi-methods similar to SISC's define-method, but rather than using chaining it lets you bind multiple functions to the same name and then selects the best match using dynamic dispatch, e.g., the following code (take from jscheme/src/elf/iterate.scm) defines an iterate method (iterate CompoundObject Action) which is a "for-each" for all sorts of compound objects: (import "java.lang.Object") (import "java.lang.String") (define-method (iterate (mapper jsint.Procedure) action) (mapper action)) ;;; Hashtable and Vector specialization are only needed for JDK 1.1 (define-method (iterate (items java.util.Hashtable) action) (iterate (.elements items) action)) (define-method (iterate (items java.util.Vector) action) (iterate (.elements items) action)) (define-method (iterate (items java.util.Enumeration) action) (let loop () (if (.hasMoreElements items) (begin (action (.nextElement items)) (loop))))) (define-method (iterate (items jsint.Pair) action) (let loop ((items items)) (if (pair? items) (begin (action (car items)) (loop (cdr items)))))) (define-method (iterate (items Object[]) action) (let loop ((i 0) (L (vector-length items))) (if (< i L) (begin (action (vector-ref items i)) (loop (+ i 1) L))))) (define-method (iterate (items String) action) (let loop ((i 0) (L (string-length items))) (if (< i L) (begin (action (string-ref items i)) (loop (+ i 1) L))))) ....etc... (see jscheme/src/elf/iterate.scm for more) > > Here's a list of some other interesting features of SISC generic > procedures: > > * they are lexically scoped; One can define the same generic procedure > name in multiple scopes/modules withouth them inteferring with each > other. Nice. Jscheme generic functions as defined using "define-method" are globally scoped. The javadot symbols are also defined in the outermost scope. > > * the first parameter is not special. It participates in method > selection just like any other parameter. Jscheme's define-method also does not give any special significance to the first parameter. Jscheme's javadot does give special signficance to the first parameter for instance methods and instance fields. > > * The names of generic procedures by default are mapped to Java method > names via an "unschemification" conversion, e.g. foo-bar-baz? becomes > isFooBarBaz. This further blurs the distinction between doing "schemy" > things and "javay" things. This is cute! But its not essential, is it? Couldn't you just as well have used straight Java syntax here... > > * constructors are generic procedures too. ?????????? Do you use chaining for selecting the appropriate constructor or method for a straight Java call? e.g. if there are five different constructors, does SISC select the one that best matches the arguments or the one that "first" matches the arguments (with respect to some ordering...) ?????????? > > * next-method parameter for calling next matching method > > Note that at the moment SISC generic procedures can only operate on > Java objects, not Scheme objects (unless, of course, you use their Java > representation). That will change soon though with the introduction of > a SISC type system and object system. I look forward to seeing this, but it may be confusing having two type systems (Java and SISC) to consider. I think Jython has some experience dealing with these issues. > > > 2) JScheme uses special syntax to access static and instance variables. > Field access in SISC is done by calling the Java object with a slot > name and optional value. SISC consequently allows one to write > (map 'foo (list obj1 obj2 obj3)) > to get the value of the field "foo" of three objects. I'm not sure what > the equivalent would look like in JScheme. We would use a similar syntax. > (map .first$ '((a b) (c d) (e f))) '(a c e) The .first$ is an accessor if called with one argument and a setter if called with two. Static variables work differently and are bound to the classname, e.g. Math.PI$ Math.E$. > > > 3) JScheme defines special Listener classes that allow the definition > of Java listeners in Scheme. SISC has a generic proxy mechanims that > allows any Java interface to be implemented in Scheme. Jscheme can also access the proxy listener directly, but we don't yet have any special syntax, e.g. one can define the following procedure which creates a proxy object given a list of interfaces and a handler: (define (make-proxy-object interfacelist handler) (Proxy.newProxyInstance (.getClassLoader (first interfacelist)) (list->array Class.class interfacelist) (elf.SchemeInvocationHandler. (lambda (proxy method argv) (handler proxy method argv))))) This can then be used to create proxy objects on the fly: Here's an example of creating a comparator for the sorting procedure: (define (mycompare fn) (make-proxy-object (list java.util.Comparator.class) (lambda (proxy method argv) (case (.getName method) (("compare") (let ((L (array->list argv))) (cond ((apply fn L) -1) ((apply fn L) 0) (else 1)))) (else (.invoke method 'myCompare argv)))))) (define (mysort array) (java.util.Arrays.sort array (mycompare <))) (define z #(2 7 1 8 2 8 1 8 2 8 4 5 9 0 4 5)) (mysort z) > > SISC's generic proxy mechanism is somewhat clunky at the moment but > this will improve considerably with the introduction of the type and > object system. > > > 4) Many JScheme types map directly to standard Java types, making type > conversion unnecessary when objects cross the Scheme/Java divide. In > SISC explicit conversion is needed. Indeed, Jscheme is probably best thought of as a Scheme-skin for Java. It really is programming in Java, but the syntax is different and some higher order procedure patterns have been incorporated into the syntax. We're currentlly working on a syntax for implementing Java classes using Jscheme, which would complete this analogy. > > The JScheme solution results in incredibly concise code at the expense > of losing some standards compliance (e.g. mutable strings) and > generality. The precise reasons, straight from the SISC manual, why > SISC opted for explict rather than implict conversion are: > > * For some Scheme types, such as numbers, the mapping to Java types is > one-to-many, e.g. a Scheme number could be converted to a byte, short, > int, etc. This causes ambiguities when automatic conversion of > parameters is attempted. Makes sense (but couldn't a best fit mapping be made by default, i.e. choose the smallest applicable type ....) > > * Some Java types have several corresponding Scheme types, e.g. a Java > array could be represented as Scheme list or vector - this causes > ambiguities when automatic conversion of results is attempted. The main problem is conversion from Scheme to Java (for procedure calls), i.e., what do Strings, Lists, and Vectors maps to. Converting back to Scheme could just do the reverse. We chose to map lists to a new Java class (jsint.Pair), vectors to Object[], and procedures to a new Java type jsint.Procedure. This strategy is to simply embed Scheme types in the Java types. This wouldn't work for SISC as easily as Strings must be mutable. Pair's and Vectors might map naturally though.... > > * Conversion carries an overhead that can be significant. For instance, > Java strings have to be copied "by value" to Scheme strings since the > former are immutable and the latter aren't. In a chained-call scenario, > i.e. where the results of one method invocation are passed as arguments > to another, the conversion is unnecessary and a wasted effort. > > * Conversion breaks the object identity relationship. In a chained-call > scenario, the identities of the objects passed to the second call are > different from the ones returned by the first. This causes problems if > the called Java code relies on the object identity being preserved. > > * Conversion conflicts with generic procedures. The method selection > mechanism employed by generic procedures relies on objects having > exactly one type. Automatic conversion effectively gives objects more > than one type - their original type and the type of the objects they > can be converted to. While it would be technically possible to devise a > method selection algorithm that accommodates this, the algorithm would > impose a substantial overhead on generic procedure invocation and also > make it significantly harder for users to predict which method will be > selected when invoking a generic procedure with a particular set of > arguments. Conversion is indeed a tricky problem. It seems mandated by the need to have mutable Strings and I don't see a way around it for SISC. For a while we toyed with the idea of having Scheme Symbols get mapped to Java Strings. Both are immutable and both can be interned.... Maybe that would work for SISC as well.... Then you could just use a straight embedding of all other Scheme objects into Java by adding the Pair, Procedure, and Continuation classes to the public Java API for SISC.... Do you think this might work??? Anyway, I want to thank you for taking the time to write this explanation. It helps clarify the similarities and differences. I want to look at the way we handle define-method, perhaps we should allow it to be used over javadot names and to let it use the " . rest)" notation as SISC's define-method does... (e.g. (define-method (.append (sb StringBuffer) . rest) ....) I'm still interested in the possibility of adding javadot notation to SISC. It would be a pretty simple extension and could be though of as loading a program that lazily defines 100,000 new primitives ..... It doesn't strictly violate the Scheme semantics, but it does greatly increase the size of the namespace... I think Geoff's suggestions was to look into a way of finding a canonical method for converting SISC values into Scheme values (e.g. numbers go to ints if possible, the longs if possible, then doubles, the floats, then BigIntegers or BigDecimals, but inexacts always goto doubles, floats, and BigDecimals. Converting back is more straightforward...... Thus, SISC could support a version of javadot with automatic conversion. It might also be interesting to allow the use of Java literals (e.g. 5b, 7.2f, 30L) which could be converted into SISC-wrapped Java objects.... It might even be possible to have javadot and the current SISC/java interface work concurrently... ---Tim--- > > > Matthias > > > _______________________________________________________________ > > Don't miss the 2002 Sprint PCS Application Developer's Conference > August 25-28 in Las Vegas -- http://devcon.sprintpcs.com/adp/index.cfm > > _______________________________________________ > Jscheme-devel mailing list > Jsc...@li... > https://lists.sourceforge.net/lists/listinfo/jscheme-devel > |
From: Matthias R. <mat...@so...> - 2002-06-03 08:10:56
|
Timothy Hickey wrote: > Jscheme does have a form of multi-methods similar to SISC's > define-method, but rather than > using chaining it lets you bind multiple functions to the same name and > then selects the best match > using dynamic dispatch, Perhaps you misunderstood me. Generic procedures in SISC have methods that get selected based on the argument types - just like in JScheme (and CLOS, Dylan etc etc). Chaining is an additional concept that allows one generic procedure to delegate calls to another generic procedure when it cannot find a suitable match. By default (define-generic foo) will create a "normal" generic procedure to which any methods are added with (define-methd (foo ...) ...), and chain it to a *special* generic procedure containing all Java methods "foo" of all classes. >> * The names of generic procedures by default are mapped to Java method >> names via an "unschemification" conversion, e.g. foo-bar-baz? becomes >> isFooBarBaz. This further blurs the distinction between doing "schemy" >> things and "javay" things. > > This is cute! But its not essential, is it? Couldn't you just as well > have used straight Java syntax here... > Yes. The philosophy behind the S2J interface in SISC is very much one of making Java blend in with Scheme. Another reason is that SISC, by default, is case-insensitive which would make using straight Java names awkward. You can do so by doing something like (define-generic |isFooBar| (generic-java-procedure '|isFooBar|) Note the || notation for case-sensitive symbols. Btw, this example also makes the chaining explicit - the generic-java-procedure call retrieves the generic procedure containing all methods isFooBar of all classes. >> * constructors are generic procedures too. > > ?????????? > Do you use chaining for selecting the appropriate constructor or method > for a straight Java call? > e.g. if there are five different constructors, does SISC select the one > that best matches the arguments > or the one that "first" matches the arguments (with respect to some > ordering...) > ?????????? > Here's an example: (define-constructor (<jstring> (next: next-method) (<jchar> c)) (next-method (->jarray (list c) <jchar>))) (make <jstring>) ;calls Java method (make <jstring> (->jchar #\o)) ;calls scheme method Handling constructors is actually somewhat complex. 'make' maintains a table of generic procedures - one per class. The above example adds a method to one of these, i.e. the generic procedure representing the <jstring> constructors. The generic procedures are chained to generic procedures holding all the Java constructors, again, on a per-class basis. Constructor selection is based on looking up the generic procedure for the constructors of the given class and then doing a normal best-match selection on the methods. >> Note that at the moment SISC generic procedures can only operate on >> Java objects, not Scheme objects (unless, of course, you use their >> Java representation). That will change soon though with the >> introduction of a SISC type system and object system. > > I look forward to seeing this, but it may be confusing having two type > systems (Java and SISC) to consider. I think > Jython has some experience dealing with these issues. > The Java type system frankly isn't good enough for me :) The two type systems will be integrated, i.e. it's going to be just one type system of which the Java types are a part of. >> 4) Many JScheme types map directly to standard Java types, making type >> conversion unnecessary when objects cross the Scheme/Java divide. In >> SISC explicit conversion is needed. > > Indeed, Jscheme is probably best thought of as a Scheme-skin for Java. > It really is programming in Java, > but the syntax is different and some higher order procedure patterns > have been incorporated into the syntax. > We're currentlly working on a syntax for implementing Java classes using > Jscheme, which would complete this > analogy. > I see. I don't think SISC is going to go down the route of class generation. There are obvious limitations to using only reflection, but it fits in better with the SISC philosophy. >> * For some Scheme types, such as numbers, the mapping to Java types is >> one-to-many, e.g. a Scheme number could be converted to a byte, short, >> int, etc. This causes ambiguities when automatic conversion of >> parameters is attempted. > > Makes sense (but couldn't a best fit mapping be made by default, i.e. > choose the smallest applicable type ....) Yes, but when using generic procedures things start to get messy. See the point further down my original list. >> * Some Java types have several corresponding Scheme types, e.g. a Java >> array could be represented as Scheme list or vector - this causes >> ambiguities when automatic conversion of results is attempted. > > The main problem is conversion from Scheme to Java (for procedure > calls), i.e., what do Strings, Lists, > and Vectors maps to. Converting back to Scheme could just do the reverse. The problem is that you start making arbitrary choices and thus taking away choice from the user. Whether I want an array returned "as is", as a list, or as a vector depends on the context. Sure, if the call always returns a vector I can convert things back into an array, or into a list, but a) I end up doing two conversions instead of one, b) in the array->vector->array conversion the object identity of the array is lost. > Conversion is indeed a tricky problem. It seems mandated by the need to > have mutable Strings No. That's just one reason. All the other reasons I mentioned are much more important. > Then you could just use a straight embedding of all > other Scheme objects into Java > by adding the Pair, Procedure, and Continuation classes to the public > Java API for SISC.... > Do you think this might work??? These classes already exist and you can pass unconverted instances to Java by wrapping them, e.g. (println (<jsystem> 'err) (java-wrap '(1 2 3))) I could have made the wrapping implicit, but at the moment it isn't. I might still change my mind on that. Generally, I want to keep the Scheme types distinct from the Java types. That's a different approach to JScheme, but is more suitable for SISC. > Anyway, I want to thank you for taking the time to write this explanation. You are welcome. I've learned a lot more about JScheme in the process. > It helps clarify the similarities and differences. I want to look at the > way we handle define-method, perhaps we should allow it to be > used over javadot names and to let it use the " . rest)" notation > as SISC's define-method does... > I'd encourage you to read the SISC manual and the code. The generic procedure code is actually reasonably portable. > I'm still interested in the possibility of adding javadot notation to SISC. It's possible. You are only saving one line of code per method name though at the expense of having to use the uglier (IMHO ;) Java names. > I think Geoff's suggestions was to look into a way of finding a canonical > method for converting SISC values into Scheme values (e.g. numbers > go to ints if possible, the longs if possible, then doubles, the floats, > then > BigIntegers or BigDecimals, but inexacts always goto doubles, floats, and > BigDecimals. The problem with conversion based on range is that if a Java method is overloaded on, say, byte, int and long then you have no idea which one is going to get called. In many cases this won't matter, because the methods will usually all do the same thing, but not always. Also, note that the numeric types are not sub-types of each other, e.g. int is not a sub-type of long. Since selection of methods from a generic procedure is based on types, automatic conversion to the smallest type will result in failures to find appropriate methods. Java gets around this by performing a widening conversion, which is completely orthogonal to type-conversion (i.e. the implicit casting to a base class) and not a concept I would want to introduce into SISC's type system. > Thus, SISC could support a version of javadot with automatic conversion. > It might even be possible to have javadot and the current SISC/java > interface work concurrently... > Yes. Anyone who wants to implement automatic conversion between Scheme and Java types in SISC this is welcome to do so. I can certainly see the attraction of the idea and perhaps some applications would indeed benefit from it. However, in the general case I believe that it creates more problems than it solves, which is why I decided against this strategy. Matthias. |
From: Timothy H. <tim...@ma...> - 2002-06-03 14:53:44
|
On Monday, June 3, 2002, at 04:03 AM, Matthias Radestock wrote: > Timothy Hickey wrote: > >> Jscheme does have a form of multi-methods similar to SISC's >> define-method, but rather than >> using chaining it lets you bind multiple functions to the same name >> and then selects the best match >> using dynamic dispatch, > > Perhaps you misunderstood me. Generic procedures in SISC have methods > that get selected based on the argument types - just like in JScheme > (and CLOS, Dylan etc etc). Chaining is an additional concept that > allows one generic procedure to delegate calls to another generic > procedure when it cannot find a suitable match. By default > (define-generic foo) > will create a "normal" generic procedure to which any methods are added > with (define-methd (foo ...) ...), and chain it to a *special* generic > procedure containing all Java methods "foo" of all classes. Now I understand. Thanks. > >>> * The names of generic procedures by default are mapped to Java >>> method names via an "unschemification" conversion, e.g. foo-bar-baz? >>> becomes isFooBarBaz. This further blurs the distinction between doing >>> "schemy" things and "javay" things. >> This is cute! But its not essential, is it? Couldn't you just as well >> have used straight Java syntax here... > > Yes. The philosophy behind the S2J interface in SISC is very much one > of making Java blend in with Scheme. Another reason is that SISC, by > default, is case-insensitive which would make using straight Java names > awkward. You can do so by doing something like > (define-generic |isFooBar| (generic-java-procedure '|isFooBar|) > Note the || notation for case-sensitive symbols. Btw, this example also > makes the chaining explicit - the generic-java-procedure call retrieves > the generic procedure containing all methods isFooBar of all classes. Ahh yes. I had forgotten about case insensitivity. > >>> * constructors are generic procedures too. >> ?????????? >> Do you use chaining for selecting the appropriate constructor or >> method for a straight Java call? >> e.g. if there are five different constructors, does SISC select the >> one that best matches the arguments >> or the one that "first" matches the arguments (with respect to some >> ordering...) >> ?????????? > > Here's an example: > (define-constructor (<jstring> (next: next-method) > (<jchar> c)) > (next-method (->jarray (list c) <jchar>))) > (make <jstring>) ;calls Java method > (make <jstring> (->jchar #\o)) ;calls scheme method > > Handling constructors is actually somewhat complex. 'make' maintains a > table of generic procedures - one per class. The above example adds a > method to one of these, i.e. the generic procedure representing the > <jstring> constructors. The generic procedures are chained to generic > procedures holding all the Java constructors, again, on a per-class > basis. Constructor selection is based on looking up the generic > procedure for the constructors of the given class and then doing a > normal best-match selection on the methods. I understand. > >>> Note that at the moment SISC generic procedures can only operate on >>> Java objects, not Scheme objects (unless, of course, you use their >>> Java representation). That will change soon though with the >>> introduction of a SISC type system and object system. >> I look forward to seeing this, but it may be confusing having two type >> systems (Java and SISC) to consider. I think >> Jython has some experience dealing with these issues. > > The Java type system frankly isn't good enough for me :) > The two type systems will be integrated, i.e. it's going to be just one > type system of which the Java types are a part of. Hmmm. Could you make the Java part optional or switchable. There has been some interest from Microsoft in building .NET schemes. They might be interested in a SISC#. It seems that you are well on the way to defining a generic interface to OO languages that support reflection (e.g. SISC-java, SISC-C#, SISC-goo?, ...) > >>> 4) Many JScheme types map directly to standard Java types, making >>> type conversion unnecessary when objects cross the Scheme/Java >>> divide. In SISC explicit conversion is needed. >> Indeed, Jscheme is probably best thought of as a Scheme-skin for Java. >> It really is programming in Java, >> but the syntax is different and some higher order procedure patterns >> have been incorporated into the syntax. >> We're currentlly working on a syntax for implementing Java classes >> using Jscheme, which would complete this >> analogy. > > I see. I don't think SISC is going to go down the route of class > generation. There are obvious limitations to using only reflection, but > it fits in better with the SISC philosophy. > >>> * For some Scheme types, such as numbers, the mapping to Java types >>> is one-to-many, e.g. a Scheme number could be converted to a byte, >>> short, int, etc. This causes ambiguities when automatic conversion of >>> parameters is attempted. >> Makes sense (but couldn't a best fit mapping be made by default, i.e. >> choose the smallest applicable type ....) > > Yes, but when using generic procedures things start to get messy. See > the point further down my original list. > >>> * Some Java types have several corresponding Scheme types, e.g. a >>> Java array could be represented as Scheme list or vector - this >>> causes ambiguities when automatic conversion of results is attempted. >> The main problem is conversion from Scheme to Java (for procedure >> calls), i.e., what do Strings, Lists, >> and Vectors maps to. Converting back to Scheme could just do the >> reverse. > > The problem is that you start making arbitrary choices and thus taking > away choice from the user. > Whether I want an array returned "as is", as a list, or as a vector > depends on the context. Sure, if the call always returns a vector I can > convert things back into an array, or into a list, but a) I end up > doing two conversions instead of one, b) in the array->vector->array > conversion the object identity of the array is lost. This discussion is reminiscent of the AWT/Swing/SWT debate on the use of peers in building window systems. AWT uses peers exclusively (as in Jschemes use of Java types), Swing eschews the use of almost all peers (as in SISCs use of its own internal types), and SWT from IBM uses a mixture of both. > >> Conversion is indeed a tricky problem. It seems mandated by the need >> to have mutable Strings > > No. That's just one reason. All the other reasons I mentioned are much > more important. > >> Then you could just use a straight embedding of all >> other Scheme objects into Java >> by adding the Pair, Procedure, and Continuation classes to the public >> Java API for SISC.... >> Do you think this might work??? > > These classes already exist and you can pass unconverted instances to > Java by wrapping them, e.g. > (println (<jsystem> 'err) (java-wrap '(1 2 3))) > > I could have made the wrapping implicit, but at the moment it isn't. I > might still change my mind on that. > > Generally, I want to keep the Scheme types distinct from the Java > types. That's a different approach to JScheme, but is more suitable for > SISC. I see (and it seems like it might be portable to other Schemes, including those not written in Java, using the JNI....) > >> Anyway, I want to thank you for taking the time to write this >> explanation. > > You are welcome. I've learned a lot more about JScheme in the process. From my perspective SISC and Jscheme are not direct competitors anymore than Java and Scheme are. I really do view Jscheme as Java programming using a Scheme-like syntax and semantics, where SISC seems to be a high quality Scheme which happens to be implemented in Java. > >> It helps clarify the similarities and differences. I want to look at >> the >> way we handle define-method, perhaps we should allow it to be >> used over javadot names and to let it use the " . rest)" notation >> as SISC's define-method does... > > I'd encourage you to read the SISC manual and the code. The generic > procedure code is actually reasonably portable. I have downloaded it and started to read it, but I actually have had some trouble getting the java-interface to work (e.g. java-class seems to be undefined). I'm sure I'm just forgetting to load the right module or something equally silly, but I haven't had a lot of time to work on it yet. > >> I'm still interested in the possibility of adding javadot notation to >> SISC. > > It's possible. You are only saving one line of code per method name > though at the expense of having to use the uglier (IMHO ;) Java names. But thats one line for each "use" of a javadot symbol and when programming a big Java application with lots of library use each line of Jscheme can contain many many javadot symbols, e.g. the six lines of code after the imports contain 10 javadot symbols and this is pretty typical. (It is taken from an early email in this thread...) (import "java.util.zip.*") (import "java.io.*") (import "sisc.*") (import "sisc.data.*") ;; initialize SISC (define in (GZIPInputStream. (BufferedInputStream. (FileInputStream. (File. "sisc.heap"))))) (define ctx (AppContext.)) (Context.register "main" ctx) (define r (Context.enter "main")) (REPL.initializeInterpreter r (list->array String.class '()) in) (Context.exit) ;; The code above is an approximate Jscheme translation of the following java code from ;; sisc.contrib.applet.SISCApplet.java (but the sisc.heap is read from a File not a URL...) import sisc.*; import javax.swing.*; import java.awt.*; import java.awt.event.*; import java.io.*; import java.net.*; import java.util.zip.*; ....snip..... URL u=new URL(getDocumentBase(),"sisc.heap"); URLConnection conn=u.openConnection(); AppContext ctx = new AppContext(); Context.register("main", ctx); Interpreter r = Context.enter("main"); InputStream in=new GZIPInputStream(new BufferedInputStream(conn.getInputStream())); REPL.initializeInterpreter(r, new String[0], in); Context.exit(); ....snip.... On the otherhand, the SISC style probably encourages developers to develop Scheme libraries providing access to Java and hence make the resulting code more schemish. > >> I think Geoff's suggestions was to look into a way of finding a >> canonical >> method for converting SISC values into Scheme values (e.g. numbers >> go to ints if possible, the longs if possible, then doubles, the >> floats, then >> BigIntegers or BigDecimals, but inexacts always goto doubles, floats, >> and >> BigDecimals. > > The problem with conversion based on range is that if a Java method is > overloaded on, say, byte, int and long then you have no idea which one > is going to get called. Yes this does create a problem... > In many cases this won't matter, because the methods will usually all > do the same thing, but not always. > > Also, note that the numeric types are not sub-types of each other, e.g. > int is not a sub-type of long. Since selection of methods from a > generic procedure is based on types, automatic conversion to the > smallest type will result in failures to find appropriate methods. Java > gets around this by performing a widening conversion, which is > completely orthogonal to type-conversion (i.e. the implicit casting to > a base class) and not a concept I would want to introduce into SISC's > type system. Yes, we played with widening for a while, but have since removed it. > >> Thus, SISC could support a version of javadot with automatic >> conversion. >> It might even be possible to have javadot and the current SISC/java >> interface work concurrently... > > Yes. Anyone who wants to implement automatic conversion between Scheme > and Java types in SISC this is welcome to do so. I can certainly see > the attraction of the idea and perhaps some applications would indeed > benefit from it. However, in the general case I believe that it creates > more problems than it solves, which is why I decided against this > strategy. I think you made the right choice for SISC as the resulting system is truly a R5RS Scheme interface to Java and could easily be replaced with a similar interface to C# or other OO languages... My experience with Jscheme is leading me to believe that there is considerable latent power in principled, multilingual programming. Jscheme and SISC present two different approaches to this programming scheme. Cheers, ---Tim--- > > > Matthias. > |