Let's start Scheme

2014-07-25

R7RS implementation differences

Recently, Gauche 0.9.4 has been out and it now supports R7RS. Now, I have couple of R7RS implementation in my hand, like followings;
  • Sagittarius (of course)
  • Chibi Scheme
  • Gauche
  • Foment
Well, only 3. I've also tried foment and picrin which are is supposed to be R7RS implementations however it didn't work on Cygwin which is my main environment. (Sorry, I'm Windows user...)

July 28th 2014: Foment compiled on VC2010 has been added.

The purpose of this article is to show some corner cases of R7RS. Sagittarius is R6RS/R7RS and Chibi and Gauche are R5RS/R7RS implementation so there should be something but how much? So I've prepared this test script.
;; #!r7rs is supported only Gauche and Sagittarius...
;; #!r7rs
(import (scheme base)
        (scheme write)
        (scheme read)
        (scheme file)
        (scheme inexact)
        (scheme lazy))

(define (print . args) (for-each display args) (newline))

;; string
;; allow #\null in string?
(print "String range")
(write "abc\x0;def")(newline)

;; file
;; are binary and textual port separated?
(print "Port separation")
(define-syntax check-port
  (syntax-rules ()
    ((_ opener reader data)
     (let ((in (opener data)))
       (guard (e (#t (print "ERROR: "(error-object-message e))
                     (close-input-port in)))
         (print 'opener ":" 'reader)
         (reader in)
         (close-input-port in))))))

;; files
(check-port open-binary-input-file read-char "r7rs-comp.scm")
(check-port open-binary-input-file read-u8 "r7rs-comp.scm")
(check-port open-input-file read-char "r7rs-comp.scm")
(check-port open-input-file read-u8 "r7rs-comp.scm")
;; memory
(check-port open-input-bytevector read-u8 #u8(0 1 2 3))
(check-port open-input-bytevector read-char #u8(0 1 2 3))
(check-port open-input-string read-u8 "abc")
(check-port open-input-string read-char "abc")

;; error object
(print "Error object")
(define-syntax check-error
  (syntax-rules ()
    ((_ pred expr)
     (guard (e (#t (print 'pred ":" (pred e))))
       expr))))
(check-error error-object? (car 'a))
(check-error read-error? (read (open-input-string "#0#")))
(check-error file-error? (open-input-file "this doesn't exist.txt"))

(define-syntax check
  (syntax-rules ()
    ((_ expr)
     (guard (e (#t (print 'expr ":" (error-object-message e))))
       (print 'expr ":" expr)))))

;; sqrt
(print "Procedures")
(check (sqrt 4))

;; promise
(print "Promise")
(check (+ (delay (* 3 7)) 13))
The script is constructed based on what I thought could be a corner case. Mostly port related procedures and error object. Then this is the output;
Sagittarius
String range
"abc\x0;def"
Port separation
open-binary-input-file:read-char
ERROR: textual port required, but got #<file-binary-input-port r7rs-comp.scm>
open-binary-input-file:read-u8
open-input-file:read-char
open-input-file:read-u8
ERROR: "binary-port" required, but got #<transcoded-textual-input-port r7rs-comp.scm utf8-codec>
open-input-bytevector:read-u8
open-input-bytevector:read-char
ERROR: textual port required, but got #<bytearray-binary-input-port>
open-input-string:read-u8
ERROR: "binary-port" required, but got #<string-textual-input-port>
open-input-string:read-char
Error object
error-object?:#t
file-error?:#t
Procedures
(sqrt 4):2
Promise
(+ (delay (* 3 7)) 13):"number" required, but got (13 #<<promise> 0x80599100>)
---
Chibi Scheme
String range
"abc\x00;def"
Port separation
open-binary-input-file:read-char
open-binary-input-file:read-u8
open-input-file:read-char
open-input-file:read-u8
open-input-bytevector:read-u8
open-input-bytevector:read-char
open-input-string:read-u8
ERROR: not a binary port
open-input-string:read-char
Error object
error-object?:#t
read-error?:#t
file-error?:#t
Procedures
(sqrt 4):2
Promise
(+ (delay (* 3 7)) 13):invalid type, expected Number
---
Gauche
String range
"abc\0def"
Port separation
open-binary-input-file:read-char
open-binary-input-file:read-u8
open-input-file:read-char
open-input-file:read-u8
open-input-bytevector:read-u8
open-input-bytevector:read-char
open-input-string:read-u8
open-input-string:read-char
Error object
error-object?:#t
read-error?:#t
file-error?:#t
Procedures
(sqrt 4):2
Promise
(+ (delay (* 3 7)) 13):operation + is not defined between 13 and #<promise 0x80445480>
Following it the output from Foment
String range
"abc^@def"
Port separation
open-binary-input-file:read-char
ERROR: read-char: expected an open textual input port
open-binary-input-file:read-u8
open-input-file:read-char
open-input-file:read-u8
ERROR: read-u8: expected an open binary input port
open-input-bytevector:read-u8
open-input-bytevector:read-char
ERROR: read-char: expected an open textual input port
open-input-string:read-u8
ERROR: read-u8: expected an open binary input port
open-input-string:read-char
Error object
error-object?:#t
file-error?:#t
Procedures
(sqrt 4):2
Promise
(+ (delay (* 3 7)) 13):+: expected a number
Hmmm, not much difference than I expected. The only difference is port related procedures. Gauche is the most tolerant then Chibi, Sagittarius is the strictest one. Well, Sagittarius is an R6RS implementation and it's required to be. Gauche's behaviour is a bit too flexible to me but still consistent. Chibi looks a bit tricky to me. It actually reject to pass texutal port to read-u8 however read-char accept binary port.

Even though, R7RS explicitly says that implementation may reject #\null in strings however none of implementations does it. Interestingly, Foment doesn't support writing #\null. So it writes as it is. It would be better to consider that it doesn't support properly or particially supported. So to write portable string library, users should not depend on null character.
 
I thought error object could have some difference but not really (or the test script doesn't cover that much).

I was hoping one of the implementations (Chibi or Gauche though) have implicit forcing but the result was none of them.

The difference is small and it's avoidable if users are consistent. So if you write R7RS scripts and it could be run on Sagittarius, then most likely it would run on the other implementations.

No comments:

Post a Comment