For Programmers: Free Programming Magazines  


Home > Archive > Scheme > June 2004 > Portably Detecting the implementation









You are viewing an archived Text-only version of the thread. To view this thread in it's original format and/or if you want to reply to this thread please [click here]

 

Author Portably Detecting the implementation
Joe Marshall

2004-05-17, 1:39 pm


I was curious if anyone has discovered any mechanisms for portably
detecting what Scheme implementation is in use. Since portability is
of the essence, the functions used would have to be available on all
implementations (which would restrict you to a subset of the R5RS
functions). There are a few functions whose behavior is loosly enough
specified that they could be used. For instance, number->string (when
it overflows into scientific notation, what scientific notation looks
like), make-string (what are the initial contents of the string),
order of evaluation, whether #f is null?, etc.

Any other good ones? Maybe the return value of SET! or one-armed
conditionals?

Jens Axel Søgaard

2004-05-17, 2:34 pm

Joe Marshall wrote:

> I was curious if anyone has discovered any mechanisms for portably
> detecting what Scheme implementation is in use. S


Have you look at Sitarams code?

<http://www.ccs.neu.edu/home/dorai/s...e/scmxlate.html>

--
Jens Axel Søgaard

Anton van Straaten

2004-05-17, 2:34 pm

Jens Axel Søgaard wrote:

> Joe Marshall wrote:
>
>
> Have you look at Sitarams code?
>
> <http://www.ccs.neu.edu/home/dorai/s...e/scmxlate.html>


Apparently you're not an astute reader! ;oP

From
http://www.ccs.neu.edu/home/dorai/s...l#footnote_Temp
_1 :

"The astute reader may wonder why Scmxlate needs to explicitly ask the user
what the target dialect is, when it is already running on it! Unfortunately,
since the Scxmlate code is necessarily written in a style that must load in
all Schemes, it cannot portably determine the identity of the particular
Scheme dialect it is currently running on."

Anton


Anton van Straaten

2004-05-17, 2:34 pm

Joe Marshall wrote:
>
> I was curious if anyone has discovered any mechanisms for portably
> detecting what Scheme implementation is in use. Since portability is
> of the essence, the functions used would have to be available on all
> implementations (which would restrict you to a subset of the R5RS
> functions). There are a few functions whose behavior is loosly enough
> specified that they could be used. For instance, number->string (when
> it overflows into scientific notation, what scientific notation looks
> like), make-string (what are the initial contents of the string),
> order of evaluation, whether #f is null?, etc.
>
> Any other good ones? Maybe the return value of SET! or one-armed
> conditionals?
>


symbol->string and string->symbol offer some possibilities.

Invoking string->symbol on a string containing uppercase characters produces
a string delimited by vertical bars in some implementations, but not others.

An expression such as (symbol->string '|Foo|) will give "Foo" on some
implementations, and "|foo|" on others, and maybe other variations. Not
sure if these always correspond to the cases in the above paragraph, or
whether additional information can be obtained.

One problem with the above is that many implementations allow
case-sensitivity to be configured, so not too many assumptions can be made
about case-sensitivity above, and it's possible that the vertical bar idiom
could disappear in a case-sensitive mode. Invoking symbol->string on a
symbol such as 'Foo will tell you something about current case-sensitivity
settings, fwiw.

The printed representation of the return value from a one-armed conditional
would identify quite a few Schemes (at least five definitively; many others
would be partitioned into groups). However, I'm not sure how to portably
get a printed representation of that, short of writing to a file and reading
it back, which of course involves its own set of portability issues (what
filename to use, can you use the current directory or how to find an
appropriate temporary directory, etc.)

Al's test for R5RS-compliant LETREC behavior would partition the set of
implementations into two groups, one of which is quite small consisting of
SISC, Chez, Larceny (I think), and perhaps a few others.

Anton


Jens Axel Søgaard

2004-05-17, 2:34 pm

Anton van Straaten wrote:

> Jens Axel Søgaard wrote:


> "The astute reader may wonder why Scmxlate needs to explicitly ask the user
> what the target dialect is, when it is already running on it! Unfortunately,
> since the Scxmlate code is necessarily written in a style that must load in
> all Schemes, it cannot portably determine the identity of the particular
> Scheme dialect it is currently running on."


> Apparently you're not an astute reader! ;oP


Somewhere in that deduction you assume I was a reader ;-)

Besides, no one can be astute and hungry at the same time.

--
Jens Axel Søgaard
Alex Shinn

2004-05-20, 5:32 am

At Mon, 17 May 2004 17:48:49 GMT, Anton van Straaten wrote:
>
> An expression such as (symbol->string '|Foo|) will give "Foo" on some
> implementations, and "|foo|" on others, and maybe other variations.


The vertical bar | is reserved so '|Foo| may not even be valid syntax in
a given implementation.

The code below detects all the major Schemes (except right now there's a
bug when run under kawa). The numeric tests need checking against other
architectures. It may be worthwhile adding version detection since some
of this behavior changes (e.g. mzscheme is switching to being
case-sensitive by default).

If you want to add a Scheme you need to check it against all existing
tests. Likewise if you want to add a test you need to check it with all
Schemes. Order of the tests matter in that some of the later tests will
raise errors if run under certain Schemes (e.g. (sqrt -1) fails if the
Scheme doesn't support complex numbers, so we need to weed those Schemes
out first). The two commented out tests cause read errors in some
implementations.

--
Alex

(define (any pred ls)
(and (pair? ls) (or (pred (car ls)) (any pred (cdr ls)))))

(define (filter pred ls)
(cond ((null? ls) ls)
((pred (car ls)) (cons (car ls) (filter pred (cdr ls))))
(else (filter pred (cdr ls)))))

(define (lset-intq ls1 ls2)
(filter (lambda (x) (memq x ls2)) ls1))

(define (lset-diffq ls1 ls2)
(filter (lambda (x) (not (memq x ls2))) ls1))

(define scheme-implementation
(let ()
(define (unified-nil-false) (eq? '() #f))
(define (false-trinary-if) (eq? #f (if #f #f)))
(define (null-trinary-if) (eq? '() (if #f #f)))
(define (singleton-quoted-empty-vector) (eq? '#() '#()))
;;(define (singleton-unquoted-empty-vector) (eq? #() #()))
(define (singleton-empty-string) (eq? "" ""))
(define (singleton-combinators)
(letrec ((id (lambda () (lambda (x) x))))
(eq? (id) (id))))
(define (case-sensitive) (not (eq? 'a 'A)))
(define (space-default-strings)
(string=? (make-string 10) " "))
(define (wrap-to-float)
(any inexact? (map (lambda (x) (expt 2 x)) '(29 30 31 61 62 63))))
(define (wrap-to-negative)
(or (< (expt 2 29) (expt 2 28))
(< (expt 2 31) (expt 2 30))
(< (expt 2 63) (expt 2 62))))
(define (rationals) (= (+ (/ 1 3) (/ 1 2)) (/ 5 6)))
(define (no-trailing-zero-in-float)
(= 2 (string-length (number->string 1.0))))
(define (no-leading-zero-in-complex)
(string=? (number->string (sqrt -1)) "+1.0i"))
(define (keywords)
(not (eq? (string->symbol ":foo") ':foo)))
;;(define (octothorpe-keywords)
;; (not (eq? (string->symbol "#:foo") '#:foo)))
(let ((tests
`((,unified-nil-false mit-scheme oaklisp)
(,singleton-quoted-empty-vector chez mzscheme rscheme)
(,singleton-empty-string chez guile rscheme scm stalin)
(,singleton-combinators kawa mzscheme stalin)
(,space-default-strings bigloo chicken elk gauche gambit rscheme)
(,wrap-to-float rscheme chicken)
(,wrap-to-negative bigloo stalin)
(,rationals chez gambit kawa mit-scheme mzscheme sisc)
(,false-trinary-if bigloo)
(,null-trinary-if elk)
(,no-trailing-zero-in-float gambit mit-scheme)
(,no-leading-zero-in-complex kawa)
(,keywords bigloo gauche kawa)
(,case-sensitive bigloo chicken gauche guile kawa rscheme)
;;(,octothorpe-keywords guile)
;;(,singleton-unquoted-empty-vector chez mzscheme rscheme scm)
))
(schemes
'(bigloo chicken chez elk gambit gauche guile kawa
oaklisp mit-scheme mzscheme rscheme scm sisc stalin)))
(lambda ()
(let part ((t tests) (s schemes))
(cond ((= 1 (length s)) (car s))
((null? t) s)
((null? s) #f)
(else
(if ((caar t))
(part (cdr t) (lset-intq s (cdar t)))
(part (cdr t) (lset-diffq s (cdar t)))))))))))

Joe Marshall

2004-05-20, 3:32 pm

Alex Shinn <foof@synthcode.com> writes:

> The code below detects all the major Schemes (except right now there's a
> bug when run under kawa). The numeric tests need checking against other
> architectures. It may be worthwhile adding version detection since some
> of this behavior changes (e.g. mzscheme is switching to being
> case-sensitive by default).


Thanks, this is just what I was looking for!
Per Bothner

2004-05-23, 1:37 pm

Joe Marshall wrote:
[color=darkred]

space-default-strings is true for Kawa.

singleton-quoted-empty-vector is the tricky one. The expression
(eq? '#() '#()) evaluates to false when typed by itself on the
command-line, but is true inside a function. This is because
simple expressions are evaluated using a simple interpreter,
but once it's in a function Kawa uses the compiler, which handles
literals differently.

The simple fix is to add Kawa to the list of implementations for
which singleton-empty-string is true. A more robust fix would
be a tri-state table: For each feature and implementation specify
whether the feature is true, false, or indeterminate.

One feature that may be unique to Kawa is standard support for
units and dimensions. So you can use:
(not (not (string->number "3in")))
Is there any other implementation for which this is true?
--
--Per Bothner
per@bothner.com http://per.bothner.com/
Alex Shinn

2004-05-23, 10:32 pm

At Sun, 23 May 2004 17:02:26 GMT, Per Bothner wrote:
>
> space-default-strings is true for Kawa.


Hmmm... not in the interpreter. But right now I'm stuck at following
error which occurs in filter the first time it returns #t for
lset-diffq, but I don't understand why (using kawa 1.7):

Invalid parameter, was: null
java.lang.ClassCastException
at atInteractiveLevel$frame.lambda1(Unknown Source)
at atInteractiveLevel$frame.apply1(Unknown Source)
at gnu.expr.ModuleMethod.apply1(ModuleMethod.java:85)
at atInteractiveLevel.filter1(Unknown Source)
at atInteractiveLevel.filter1(Unknown Source)
at atInteractiveLevel.filter1(Unknown Source)
at atInteractiveLevel.filter1(Unknown Source)
at atInteractiveLevel.filter1(Unknown Source)
at atInteractiveLevel.filter1(Unknown Source)
at atInteractiveLevel.filter1(Unknown Source)
at atInteractiveLevel.filter1(Unknown Source)
at atInteractiveLevel.filter1(Unknown Source)
at atInteractiveLevel.apply2(Unknown Source)
at gnu.expr.ModuleMethod.apply2(ModuleMethod.java:90)
at atInteractiveLevel.lsetDiffq(Unknown Source)
at atInteractiveLevel.apply2(Unknown Source)
at gnu.expr.ModuleMethod.apply2(ModuleMethod.java:90)
at atInteractiveLevel.lambda1(Unknown Source)
at atInteractiveLevel.apply0(Unknown Source)
at gnu.expr.ModuleMethod.apply0(ModuleMethod.java:80)
at atInteractiveLevel.main(Unknown Source)
at atInteractiveLevel.apply1(Unknown Source)
at gnu.expr.ModuleBody.applyN(ModuleBody.java:171)
at gnu.expr.ModuleMethod.applyN(ModuleMethod.java:105)
at gnu.mapping.Procedure.apply(Procedure.java:102)
at gnu.mapping.CallContext.runUntilDone(CallContext.java:258)
at gnu.expr.ModuleExp.evalModule(ModuleExp.java:188)
at kawa.Shell.run(Shell.java:232)
at kawa.standard.load.loadSource(load.java:162)
at kawa.standard.load.loadSource(load.java:131)
at kawa.standard.load.apply(load.java:233)
at kawa.Shell.runFile(Shell.java:295)
at kawa.repl.main(repl.java:602)

When I try to compile and run the file (attached) I get

Exception in thread "main" java.lang.NoClassDefFoundError: gnu/expr/ModuleBody
at java.lang.ClassLoader.defineClass0(Native Method)
at java.lang.ClassLoader.defineClass(ClassLoader.java:537)
at java.security.SecureClassLoader.defineClass(SecureClassLoader.java:123)
at java.net.URLClassLoader.defineClass(URLClassLoader.java:251)
at java.net.URLClassLoader.access$100(URLClassLoader.java:55)
at java.net.URLClassLoader$1.run(URLClassLoader.java:194)
at java.security.AccessController.doPrivileged(Native Method)
at java.net.URLClassLoader.findClass(URLClassLoader.java:187)
at java.lang.ClassLoader.loadClass(ClassLoader.java:289)
at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:274)
at java.lang.ClassLoader.loadClass(ClassLoader.java:235)
at java.lang.ClassLoader.loadClassInternal(ClassLoader.java:302)

> singleton-quoted-empty-vector is the tricky one. The expression
> (eq? '#() '#()) evaluates to false when typed by itself on the
> command-line, but is true inside a function.


The other Scheme that handles this in an interesting way is scm, for
which (eq? '#() '#()) => #f but (eq? #() #()) => #t!

> A more robust fix would be a tri-state table: For each feature and
> implementation specify whether the feature is true, false, or
> indeterminate.


This sounds like a good idea. Another thing I may add is test
conflicts; tests won't be run when any of the current possible
implementations are in the tests conflict list, to avoid the ordering
requirement on the tests.

> One feature that may be unique to Kawa is standard support for
> units and dimensions. So you can use:
> (not (not (string->number "3in")))
> Is there any other implementation for which this is true?


This is really neat! Since it's unique and safe it would be a quick way
to single out kawa right away.

Is the intended idiom for performing conversions to add a zero quantity
as in

(+ 0cm 1in) => 2.54cm

?

One downside of this is that by making quantities numbers you raise the
possibility of errors in arithmetic. (number? x) and (number? y) no
longer means (+ x y) is valid. Also addition and subtraction are no
longer commutative.

--
Alex

Per Bothner

2004-05-24, 5:35 pm

Alex Shinn wrote:

> At Sun, 23 May 2004 17:02:26 GMT, Per Bothner wrote:
>
>
> Hmmm... not in the interpreter.


"Oh yes it is." It returns true for me; the implementation
explicitly does so; this code has not been changed for a long time:

(define (make-string (n :: <int> ) #!optional (ch #\Space)) :: <string>
(make <string> n ch))

In the revised WhatScheme.scm you sent me, it works after adding kawa
to singleton-quoted-empty-vector, singleton-empty-string, and
space-default-strings.

> But right now I'm stuck at following
> error which occurs in filter the first time it returns #t for
> lset-diffq, but I don't understand why (using kawa 1.7):
>
> Invalid parameter, was: null
> java.lang.ClassCastException
> at atInteractiveLevel$frame.lambda1(Unknown Source)


This seems to have been fixed in kawa-1.7.90, and also works
in the CVS version of Kawa.

>
>
> This is really neat! Since it's unique and safe it would be a quick way
> to single out kawa right away.
>
> Is the intended idiom for performing conversions to add a zero quantity
> as in
>
> (+ 0cm 1in) => 2.54cm


Yes.

> One downside of this is that by making quantities numbers you raise the
> possibility of errors in arithmetic. (number? x) and (number? y) no
> longer means (+ x y) is valid.


True.

> Also addition and subtraction are no longer commutative.


Er, subtraction has never been commutative ...

Addition and multiplication are communitive as "numbers" even if they
don't have the same units, which is mainly a display property.
Thus (= 1m 100cm) is true.
--
--Per Bothner
per@bothner.com http://per.bothner.com/
Alex Shinn

2004-05-24, 10:32 pm

At Mon, 24 May 2004 20:02:27 GMT, Per Bothner wrote:
>
> Alex Shinn wrote:
>
>
> "Oh yes it is."


Sorry, was thinking of singleton-empty-string. I was a little hasty
with the initial version and must have missed the space-default-strings
test for kawa.

>
> This seems to have been fixed in kawa-1.7.90, and also works
> in the CVS version of Kawa.


OK, I grabbed CVS. I still get the error when I try to compile with
"kawa --main -C WhatScheme.scm" but if I just run "kawa WhatScheme.scm"
it works fine.

> Addition and multiplication are communitive as "numbers" even if they
> don't have the same units, which is mainly a display property.
> Thus (= 1m 100cm) is true.


But they're not equivalent. (/ 1in 1in) => 1, a unit-less result.
However, (/ 1in 2.54cm) => 0.39370078740157477in*cm^-1 (which is not
readable, btw). And (* 1in 1cm) => 1in*cm. Since units group and
cancel out differently, you have to be careful not to perform unintended
conversions, so in effect you've lost commutativity.

Thanks a lot for your help, the updated version with Kawa support is
below, along with SCSH (which so far is the only Scheme that initializes
strings with "?").

--
Alex

(define (any pred ls)
(and (pair? ls) (or (pred (car ls)) (any pred (cdr ls)))))

(define (filter pred ls)
(cond ((null? ls) ls)
((pred (car ls)) (cons (car ls) (filter pred (cdr ls))))
(else (filter pred (cdr ls)))))

(define (lset-intq ls1 ls2)
(filter (lambda (x) (memq x ls2)) ls1))

(define (lset-diffq ls1 ls2)
(filter (lambda (x) (not (memq x ls2))) ls1))

(define scheme-implementation
(let ()
(define (unified-null/false) (eq? '() #f))
(define (false-one-armed-if) (eq? #f (if #f #f)))
(define (null-one-armed-if) (eq? '() (if #f #f)))
(define (singleton-quoted-empty-vector) (eq? '#() '#()))
(define (singleton-empty-string) (eq? "" ""))
(define (singleton-combinators)
(letrec ((id (lambda () (lambda (x) x))))
(eq? (id) (id))))
(define (case-sensitive) (not (eq? 'a 'A)))
(define (space-default-strings)
(string=? (make-string 10) " "))
(define (wrap-to-float)
(any inexact? (map (lambda (x) (expt 2 x)) '(29 30 31 61 62 63))))
(define (wrap-to-negative)
(or (< (expt 2 29) (expt 2 28))
(< (expt 2 31) (expt 2 30))
(< (expt 2 63) (expt 2 62))))
(define (rationals) (= (+ (/ 1 3) (/ 1 2)) (/ 5 6)))
(define (no-trailing-zero-in-float)
(= 2 (string-length (number->string 1.0))))
(define (no-leading-zero-in-complex)
(string=? (number->string (sqrt -1)) "+1.0i"))
(define (keywords)
(not (eq? (string->symbol ":foo") ':foo)))
(let ((tests
`((,unified-null/false mit-scheme oaklisp)
(,singleton-quoted-empty-vector chez kawa mzscheme rscheme scsh)
(,singleton-empty-string chez guile kawa rscheme scm scsh stalin)
(,singleton-combinators kawa mzscheme stalin)
(,space-default-strings bigloo chicken elk gauche gambit kawa rscheme)
(,wrap-to-float rscheme chicken)
(,wrap-to-negative bigloo stalin)
(,rationals chez gambit kawa mit-scheme mzscheme sisc scsh)
(,false-one-armed-if bigloo)
(,null-one-armed-if elk)
(,no-trailing-zero-in-float gambit mit-scheme scsh)
(,no-leading-zero-in-complex kawa)
(,keywords bigloo gauche kawa)
(,case-sensitive bigloo chicken gauche guile kawa rscheme scsh)
))
(schemes
'(bigloo chicken chez elk gambit gauche guile kawa
oaklisp mit-scheme mzscheme rscheme scm scsh sisc stalin)))
(lambda ()
(let part ((t tests) (s schemes))
(cond ((null? s) #f)
((null? (cdr s)) (car s))
((null? t) s)
(else
(part (cdr t) ((if ((caar t)) lset-intq lset-diffq) s (cdar t))))))))))

Alex Shinn

2004-06-03, 7:27 pm

Added stklos, jscheme, ksm, inlab-scheme, and a check to find
equivalence classes of Schemes for the given tests, to help when adding
new Schemes (if any turn up you need to add more tests).

That's an even 20 implementations :)

--
Alex

(define (any pred ls)
(and (pair? ls) (or (pred (car ls)) (any pred (cdr ls)))))

(define (filter pred ls)
(cond ((null? ls) ls)
((pred (car ls)) (cons (car ls) (filter pred (cdr ls))))
(else (filter pred (cdr ls)))))

(define (lset-intq ls1 ls2)
(filter (lambda (x) (memq x ls2)) ls1))

(define (lset-diffq ls1 ls2)
(filter (lambda (x) (not (memq x ls2))) ls1))

(define scheme-implementation
(let*
((unified-null/false (lambda () (eq? '() #f)))
(false-one-armed-if (lambda () (eq? #f (if #f #f))))
(null-one-armed-if (lambda () (eq? '() (if #f #f))))
(singleton-quoted-empty-vector (lambda () (eq? '#() '#())))
(singleton-empty-string (lambda () (eq? "" "")))
(singleton-combinators
(lambda () (letrec ((id (lambda () (lambda (x) x)))) (eq? (id) (id)))))
(case-sensitive (lambda () (not (eq? 'a 'A))))
(space-default-strings
(lambda () (string=? (make-string 10) " ")))
(question-default-strings
(lambda () (string=? (make-string 10) "??????????")))
(rationals (lambda () (= (+ (/ 1 3) (/ 1 2)) (/ 5 6))))
(wrap-to-float
(lambda () (any inexact? (map (lambda (x) (expt 2 x)) '(29 30 31 61 62 63)))))
(wrap-to-negative
(lambda () (or (< (expt 2 29) (expt 2 28))
(< (expt 2 31) (expt 2 30))
(< (expt 2 63) (expt 2 62)))))
(no-trailing-zero-in-float
(lambda () (string=? "1." (number->string 1.0))))
(no-leading-zero-in-complex
(lambda () (string=? "+1.0i" (number->string (sqrt -1)))))
(keywords
(lambda () (not (eq? (string->symbol ":foo") ':foo))))
(tests
`((,unified-null/false mit-scheme oaklisp)
(,singleton-quoted-empty-vector chez kawa mzscheme oaklisp rscheme scsh stklos)
(,singleton-empty-string chez guile jscheme kawa llava oaklisp rscheme scm scsh stalin stklos)
(,singleton-combinators kawa mzscheme stalin)
(,space-default-strings bigloo chicken elk gauche gambit inlab-scheme kawa rscheme)
(,question-default-strings jaja ksm scsh)
(,wrap-to-float chicken inlab-scheme jscheme rscheme)
(,wrap-to-negative bigloo stalin)
(,rationals chez gambit kawa ksm mit-scheme mzscheme oaklisp sisc scsh stklos)
(,false-one-armed-if bigloo)
(,null-one-armed-if elk inlab-scheme llava)
(,no-trailing-zero-in-float gambit mit-scheme scsh)
(,no-leading-zero-in-complex kawa)
(,keywords bigloo gauche kawa stklos)
(,case-sensitive bigloo chicken gauche guile jaja jscheme kawa rscheme scsh)
))
(schemes
'(bigloo chicken chez elk gambit gauche guile inlab-scheme jscheme
kawa ksm oaklisp mit-scheme mzscheme rscheme scm scsh sisc stalin
stklos)))
;; run once each time you change schemes/tests
(for-each
(lambda (a)
(for-each
(lambda (b)
(if (and (not (eq? a b))
(not (any (lambda (t)
(if (memq a t) (not (memq b t)) (memq b t)))
tests)))
(begin (display "can't distinguish ") (display a)
(display " from ") (display b) (newline))))
schemes))
schemes)
(lambda ()
(let part ((t tests) (s schemes))
(cond ((null? s) #f)
((null? (cdr s)) (car s))
((null? t) s)
(else
(part (cdr t) ((if ((caar t)) lset-intq lset-diffq) s (cdar t)))))))))

Ray Dillinger

2004-06-03, 7:27 pm

Per Bothner wrote:
> Alex Shinn wrote:


>
>
> True.
>


I think that this, in itself, is sufficient reason to say that
quantities are not numbers.

Numeric operations are defined on quantities, but different
quantities (quantities with units which denote different
things) don't add or subtract, and units change in
multiplication/division, and this is not numeric behavior.

Bear

Sponsored Links







Also available: Server administration forum archive | Web Design forum archive | Software forum archive | Hardware reviews archive

Copyright 2008 codecomments.com