R6RS で SRFI-6 を実装する

R6RS のカスタムポートでは SRFI-6 を実装できないとする記事を見た。

例えばこのカスタムポートを使ってSRFI-6(open-output-stringとget-output-string)を実装することはできない。

http://compassoftime.blogspot.jp/2015/02/blog-post.html

そんなはずはないだろうと私には思えたので実際に書いてみた。

#!r6rs
(library (srfi :6)
  (export get-output-string
          open-output-string
          open-input-string)
  (import (rnrs)
          (rnrs mutable-pairs))

  (define output-string-port-table
    (make-eqv-hashtable))

  (define (get-output-string port)
    (flush-output-port port)
    (let ((chunks (car (hashtable-ref output-string-port-table port '(#f)))))
      (if chunks
          (apply string-append (reverse chunks))
          (error 'get-output-string "fail get string"))))

  (define (open-output-string)
    (define buffer (cons '() #f))
    
    (define (str-close)
      (when port (hashtable-delete! output-string-port-table port)))

    (define (str-write! str start count)
      (unless (zero? count)
        (set-car! buffer
              (cons (substring str start (+ start count)) (car buffer))))
      count)

    (define port
      (make-custom-textual-output-port
       "output-string"
       str-write!
       #f
       #f
       str-close))

    (hashtable-set! output-string-port-table port buffer)
    port)

  (define open-input-string open-string-input-port)
)

ポートを作成するときにその種類をテーブルに記憶しておけば、ポートの種類を判別することにも応用できると思う。

陽にクローズしないとストレージが破棄されないのでその点は SRFI-6 を満してないと言えるかもしれないのだが、いずれにしても R6RS 的には call-with-port を使うのが行儀がよいプログラムだろう。

Ypsilon や Mosh 、そして Sagittarius では動くことが確認できたが、 Petite Chez Scheme や Larceny では動かない (というより動くが何故か書込み手続きが呼出されていない) のでもしかすると上記のコードは R6RS ポータブルになってないかもしれない。 [追記]フラッシュしてなかったからでした。[/追記]

Document ID: 507453124dfece7b211dd3cf49f92ea4