Guile基礎/モジュールの作成と利用

変更履歴

概 要

目 次

参考資料

自作モジュールの作成手順

自作モジュールは,次のような手順に沿って作成します.
  1. まず,モジュールのソースコードを用意します.
  2. 次に,モジュール名を決定します.
  3. 最後に,ソースコードに define-module を追加します.

ソースコードを用意する

▹ 以下では,次のソースコードを題材にします. ファイル名はモジュール名のところで決めるので, しばらくの間,このソースコードを helloコード と呼ぶことにします.
(define (hello-everybody names)
  (for-each hello-somebody names))

(define (hello-somebody name)
  (display (make-hello name))
  (newline))

(define (make-hello name)
  (string-append "*** Hello," name "!!! ***"))

モジュール名を決定する

モジュール名 は,モジュールファイル(ソースコードを格納したファイル)の相対パスをリスト形式で表現したものです(ただし,モジュールファイルの拡張子は除きます). 例えば,
(ice-9 boot-9)
というモジュール名は,
ice-9/boot-9.scm
といった相対パスを表現しています(拡張子 .scm は自動的に付加されます).

相対パスの起点は,モジュールファイルの検索の起点となるディレクトリです.これは,グローバル変数の %load-path に登録されたディレクトリのことです. 以後,%load-path に登録されたディレクトリのことをロードパスと呼ぶことにします.

▹ 以上から,モジュール名を決定するとは, ということになります. つまり,モジュールファイルの保存場所とファイル名を決めることでもあります.

そこで,helloコードは次のようにします.まず,
/home/user/mymods
をロードパスにします.ここで,/home/user はホームディレクトリを表しています. 次に,helloコードは
/home/user/mymods/hello.scm
というファイルに保存します. 従って,helloコード の(ロードパスに対する)相対パスは hello.scm そのものになります.これより,helloコードのモジュール名は,
(hello)
になります. これは, 相対パス(hello.scm)から拡張子(.scm)を除いてリスト形式にしたものです.

define-module を追加する

▹ 次に,ソースコード(helloコード)に define-module を追加します.define-module は,モジュールのパブリックインターフェースを定義するための構文形式です.

▹ 以下は define-module のもっとも基本的な形式を示しています.
   (define-module module name
      #:export (name ... ) )
ここで,module name はモジュール名を表し, name は公開したい名前を表しています. なお,#:export のうしろは name (公開したい名前)のリストです.このリストのことをエクスポートリスト(export list)と呼びます. 注意すべきことは,エクポートリストに登録しなかった束縛は,他のプログラムからは利用できないことです(注:引き抜き方式を使えば利用できます).筆者はしばしば登録し忘れて混乱することがあります.

▹ helloコードについては,hello-everybody と hello-somebody を公開して, make-hello は公開しないことにします. 従って,helloコードに追加する define-module は次のようになります.
(define-module (hello)
  #:export (hello-everybody hello-somebody))
           
(define (hello-everybody names)
  (for-each hello-somebody names))

(define (hello-somebody name)
  (display (make-hello name))
  (newline))

(define (make-hello name)
  (string-append "*** Hello," name "!!! ***"))
以上でモジュールは完成です.

▹ (注意) これ以後は,「helloコード」という言葉はやめて,(hello) モジュール と呼ぶことにします.丸括弧も含めてモジュール名になっていることに注意して下さい.

補足:ロードパスの候補

▹ ロードパスは次のような候補があります.
  1. Guileシステムが標準で使用しているディレクトリ.
    これは %load-path (の初期値)の最初の要素になっているディレクトリです. でも,自作のモジュールはここには保存しないほうがよいでしょう.
  2. サイト固有のディレクトリ.
    これは %load-path (の初期値)の2番目以降の要素となっているディレクトリです. 複数のユーザーで共有したり, システムプログラムの一部として利用するモジュールは, ここに保存するとよいと思います. ただし,環境によっては(例えば,筆者の環境では), サイト固有のディレクトリは存在しないので, 新たに作る必要があります.
  3. 自前のディレクトリ(例えば,ホームディレクトリ上に用意した適当なディレクトリ).
    個人で利用するときには,自前のディレクトリが扱い易いと思います. ただし,そのディレクトリを %load-path に登録しなければなりません.登録方法はあとで説明します.
  4. カレントディレクトリ.
    モジュールをテストするときには, カレントディレクトリをロードパスにするとテスト作業が実施し易いと思います. ただし,カレントディレクトリを %load-path に登録しなければなりません.登録方法はあとで説明します.

自作モジュールの利用方法

▹ 自作モジュールを利用する方法は,ある1点を除いて, システムモジュールを利用する方法と同じです. その1点とは, 自作モジュールのロードパスが %load-path (の初期値)に登録されていないとき,そのロードパスを登録しなければならない, ということです. そうしないとGuileは自作モジュールを発見できません. 例えば,(hello) モジュールのロードパスは
/home/user/mymods
にしましたが, これは %load-path (の初期値)に登録されていないので,登録しなければなりません. そうしないと,Guileは (hello) モジュールを発見することができません.

%load-path への登録は,次のような方法があります. 以下,これらの方法を説明します.

add-to-load-pathを利用する方法

▹ add-to-load-path は次の形式で使用します.
(add-to-load-path "dir")
ここで,dir はロードパスです. 例えば,次のように使用します.
(add-to-load-path "/home/user/mymods")
この add-to-load-path を use-modules よりも前に指定します. それによって,add-to-load-path の引数に指定したロードパスが %load-path の先頭に登録され,Guileはそのロードパスをモジュールの検索対象にします.

▹ (hello) モジュールを利用した具体例を示します. REPLを使った実行例と,スクリプトを使った実行例を示します. まず REPLを使った実行例です.
$ guile
GNU Guile 3.0.5
      ...... 起動メッセージ ......
guile> %load-path    ……(1) 
$1 = ("/usr/share/guile/3.0" ...(省略)... )
guile> (add-to-load-path "/home/user/mymods")  ……(2) 
guile> %load-path  ……(3) 
$2 = ("/home/user/mymods" "/usr/share/guile/3.0" ...(省略)... )
guile> (use-modules (hello))  ……(4) 
guile> (hello-everybody '("Alice" "Bob" "Carol" "David")) ……(5) 
*** Hello,Alice!!! ***
*** Hello,Bob!!! ***
*** Hello,Carol!!! ***
*** Hello,David!!! ***
guile> 
これは次のようなことを行っています.
(1) %load-path の初期値($1)を表示して,(hello) モジュールのロードパスである
/home/user/mymods
が登録されていないことを確認しています.
(2) add-to-load-path を使って,
/home/user/mymods
%load-path に登録しています.
(3) 登録後の %load-path の値($2)を表示して,リストの先頭に
/home/user/mymods
が登録されていることを確認しています.
(4) (hello) モジュールをロードしています.
(5) (hello) モジュールの hello-everybody を適当に実行しています.

▹ 次にスクリプトを使った実行例を示します. このスクリプトは,コマンドライン引数から名前のリスト(names)を取り出して, そのリストに (hello) モジュールの hello-everybody を適用しています. use-modules の前に add-to-load-path を実行して,
/home/user/mymods
%load-path に登録しています.
#!/usr/bin/guile \
-e main -s
!#

;; use-hello.scm

(add-to-load-path "/home/user/mymods")
(use-modules (hello))

(define (main args)
  (let ((names (cdr args)))
    (hello-everybody names)))
以下は実行例です.
$ ./use-hello.scm Alice Bob Carol David
      ...... コンパイルメッセージ ......
*** Hello,Alice!!! ***
*** Hello,Bob!!! ***
*** Hello,Carol!!! ***
*** Hello,David!!! ***

▹ (補足) add-to-load-path を使って カレントディレクトリ%load-path に登録するには,次のいずれかの形式を使います.
(add-to-load-path ".")
(add-to-load-path (getcwd))
getcwd はカレントディレクトリを取得するための手続きです.

▹ (参考)add-to-load-path は,(ice-9 boot-9) モジュールの中で次のように定義されています.
(define-syntax-rule (add-to-load-path elt)
  "Add ELT to Guile's load path, at compile-time and at run-time."
  (eval-when (expand load eval)
    (set! %load-path (cons elt (delete elt %load-path)))))

guileコマンドの -L スイッチを利用する方法

▹ guileコマンドの -L スイッチは,一般に,次の形式で使用します.
guile -L dir arg ...
ここで,dir%load-path に登録するロードパスです.arg は,他のスイッチや引数です. これを実行すると,dir%load-path の先頭に登録した上で guile コマンドを実行します.

参考までに,-Lスイッチの使い方をもう少し具体的に示します.
・REPLを起動するとき:
$ guile -L dir
・プログラムファイルを実行するとき:
$ guile -L dir -s file
$ guile -L dir -e proc -s file
・スクリプトの冒頭:メタスイッチを使う必要があります.
#!/usr/bin/guile \
-L dir -s
!#
      ...... (プログラム) ......
#!/usr/bin/guile \
-L dir -e proc -s
!#
      ...... (プログラム) ......
以下はシェル(sh)を使ったポータブルな形式です.
#!/usr/bin/env sh
exec guile -L dir -s "$0" "$@"
!#
      ...... (プログラム) ......
#!/usr/bin/env sh
exec guile -L dir -e proc -s "$0" "$@"
!#
      ...... (プログラム) ......

▹ REPLを使った具体例を示します.
$ guile -L /home/user/mymods
GNU Guile 3.0.5
      ...... 起動メッセージ ......
guile> %load-path
$1 = ("/home/user/mymods" "/usr/share/guile/3.0" ...(省略)... )
guile> (use-modules (hello))
guile> (hello-everybody '("Alice" "Bob" "Carol" "David"))
*** Hello,Alice!!! ***
*** Hello,Bob!!! ***
*** Hello,Carol!!! ***
*** Hello,David!!! ***
guile> 
これは,-L スイッチを使ってREPLを起動していて, 起動直後に %load-path の値($1)を確認しています. リストの先頭に
/home/user/mymods
が登録されています. そのあとの内容は,前に示した実行例と同じです.

次にスクリプトを使った具体例を示します.add-to-load-path の代わりに -L スイッチを使っている点を除くと,前に示したスクリプトとまったく同じです.
#!/usr/bin/guile \
-L /home/user/mymods -e main -s
!#

;; use-hello.scm

(use-modules (hello))

(define (main args)
  (let ((names (cdr args)))
    (hello-everybody names)))
$ ./use-hello.scm Alice Bob Carol David
      ...... コンパイルメッセージ ......
*** Hello,Alice!!! ***
*** Hello,Bob!!! ***
*** Hello,Carol!!! ***
*** Hello,David!!! ***

▹ (補足) -L スイッチを使ってカレントディレクトリ %load-path に登録するには,次の形式を利用します.
・REPLを起動するとき:
$ guile -L .
GNU Guile 3.0.5
      ...... 起動メッセージ ......
guile> %load-path 
$1 = ("." "/usr/share/guile/3.0" ...(省略)... )
guile> 
・プログラムを実行するとき:
$ guile -L . -s file
または
$ guile -L . -e proc -s file
・スクリプトを実行するとき:
#!/usr/bin/guile \
-L . -e main -s
!#
      ......(プログラム)......

▹ (補足) -L スイッチに対してロードパスは1つしか指定できません. しかし,-L スイッチそのものは幾つでも指定できます. つまり,guileコマンドを次のような形式で使用できます.
guile -L dir$_1$ ... -L dir$_n$  arg ...
これを実行すると,dir$_1$dir$_n$ の順にロードパスが %load-path の先頭に登録されます. 以下の実行例は,試しに,-L スイッチを3つ指定してREPLを起動したあと, %load-path の値($1)を確認しています.
$ guile -L /home/user/mod-a -L /home/user/mod-b -L /home/user/mod-c
GNU Guile 3.0.5
      ...... 起動メッセージ ......
guile> %load-path
$1 = ("/home/user/mod-a" "/home/user/mod-b" "/home/user/mod-c" "/usr/share/guile/3.0"  ...(省略)... )
guile> 

環境変数 GUILE_LOAD_PATH を利用する方法

▹ 環境変数 GUILE_LOAD_PATH は次の形式で使用します(PATH環境変数と同じです).
GUILE_LOAD_PATH="dir$_1$: 〜 :dir$_n$"
ここで,dir$_i$ はロードパスで, ロードパスの間はコロン(:)で区切ります. これを指定すると, dir$_1$dir$_n$ の順にロードパスが %load-path の先頭に登録されます.

▹ 以下では,envコマンドを使って, 一時的に GUILE_LOAD_PATH を設定してREPLを起動しています. 起動後, %load-path に GUILE_LOAD_PATH に設定したロードパスが登録されていること($1)を 確認しています.
$ env GUILE_LOAD_PATH="/home/user/mymods" guile
GNU Guile 3.0.5
      ...... 起動メッセージ ......
guile> %load-path
$1 = ("/home/user/mymods" "/usr/share/guile/3.0" ...(省略)... )
guile> 
以下では,環境変数を使ったスクリプトの実行例を示しています. スクリプトは次の通りです.add-to-load-path も -L スイッチも使っていないことに注意して下さい.
#!/usr/bin/guile \
-e main -s
!#

;; use-hello.scm

(use-modules (hello))

(define (main args)
  (let ((names (cdr args)))
    (hello-everybody names)))
envコマンドを使って, 一時的に GUILE_LOAD_PATH を設定してスクリプトを実行しています.
$ env GUILE_LOAD_PATH="/home/user/mymods" ./use-hello.scm Alice Bob Carol David
      ... コンパイルメッセージ ......
*** Hello,Alice!!! ***
*** Hello,Bob!!! ***
*** Hello,Carol!!! ***
*** Hello,David!!! ***

上記の実行例では GUILE_LOAD_PATH を一時的に設定していますが, ログインセッションやシェルの初期設定ファイル(~/.profile や ~/.bashrcなど)に記述するのが一般的でしょう.

dir$_i$ として,省略記号(「...」のこと;ellipsis)を指定すると,その省略記号(...)を %load-path の初期値に置き換えて得られるロードパスのリストが %load-path に設定されます.%load-path の初期値の前だけでなく,後ろにロードパスを登録したいときにこれを使用します.

以下では,2つのロードパスの間に省略記号(...)を指定してREPLを起動しています.起動後, %load-path の値($1)を確認しています.
$ env GUILE_LOAD_PATH="/home/user/mod-a:...:/home/user/mod-b" guile
GNU Guile 3.0.5
      ...... 起動メッセージ ......
guile> %load-path
$1 = ("/home/user/mod-a" "/usr/share/guile/3.0" "/usr/share/guile/site/3.0" "/usr/share/guile/site" "/usr/share/guile" "/home/user/mod-b")
guile> 
$1を見ると, 環境変数に指定した2つのロードパスの間に %load-path の初期値が設定されています.

自作モジュールのテスト

▹ モジュールは,テスト用のコードも含めて,1つのファイルにまとめることができます. 具体例を使って説明しましょう.

スクリプト化とテストコードの埋め込み

▹ 以下のプログラムは, 以前に示した (hello) モジュールのソースコードに, テスト用のコードを追加したものです.
#!/usr/bin/env sh
exec guile -e '(@@ (hello) main)' -s "$0" "$@"
!#

;; hello.scm

(define-module (hello)
  #:export (hello-everybody hello-somebody))

(define (hello-everybody names)
  (for-each hello-somebody names))

(define (hello-somebody name)
  (display (make-hello name))
  (newline))

(define (make-hello name)
  (string-append "*** Hello," name "!!! ***"))

;; テスト用手続き
(define (main args) 
 (format (current-output-port) 
         "test for make-hello:~A\n"
         (make-hello "maker"))
 (display "test for hello-somebody:\n") 
 (hello-somebody "somebody")
 (display "test for hello-everybody:\n") 
 (hello-everybody '("Alice" "Bob" "Carol" "David")))
紫色は追加した部分を示しています. まず,ファイルの冒頭に,スクリプトにするためのシェバン行を追加しています.2行目は,シェル(sh)のexecコマンドがguileコマンドを実行し, そのguileコマンドは (hello) モジュール内の main 手続きを実行します. 従って,main 手続きを定義して, そこにテスト用のコードを記述すればテストが行えます. 上記の具体例では,ファイルの最後に main 手続きを定義し, その中で各手続きを(適当に)実行しています. 以下に実行結果を示します.
$ pwd
/home/user/mymods
$ ./hello.scm
test for make-hello:*** Hello,maker!!! ***
test for hello-somebody:
*** Hello,somebody!!! ***
test for hello-everybody:
*** Hello,Alice!!! ***
*** Hello,Bob!!! ***
*** Hello,Carol!!! ***
*** Hello,David!!! ***

スクリプトでもあり,モジュールでもある

▹ 上のように変更したとしてもモジュールとして機能します. その理由は次の通りです. 結局,他のプログラムから見たら, 上記のコードは元々のモジュールと何も変わっていないと言えます.

スクリプト化の一般的な形式

▹ モジュールのソースコードをスクリプトにするためには,一般に, ソースコードの冒頭に次の3行を追加します.
#!/usr/bin/env sh
exec guile -e '(@@ module name proc)' -s "$0" "$@"
!#
ここで,module nameはモジュール名であり,procはモジュール内の手続きです.これを追加して,モジュールファイルに実行属性を与えて実行すると,シェル(sh)のexecコマンドがguileコマンドを実行し,そのguileコマンドはモジュール内の proc を個別的に引き抜いて実行します.従って,proc 手続きを定義して,そこにテスト用のコードを記述すれば,テストが行えます.

▹ なお,上記の追加部分は,proc が非公開であることを仮定しています.もし公開されている手続きを実行したいのであれば,@@ を @ にします.引き抜きに関しては モジュールの利用 を参照して下さい.

define-module のオプション

▹ define-module の一般的な形式は次の通りです.
(define-module module name option ... )
ここで,module name はモジュール名で, そのうしろはオプションoption)の列です.option は,次の形式をしています.
keyword value
ここで,keyword は #:export などのキーワードで,value はそのキーワードに対する値です.define-module は色々なオプションが指定できます.以下,主なオプションを説明します.

#:use-module

▹ #:use-module オプションは次の形式で使用します.
#:use-module iSpec
ここで,iSpecインターフェース仕様 です.これは,
(use-modules iSpec)
と等価です.つまり,他のモジュールをロードするためのオプションです.

▹ 前に示した (hello) モジュールを使った具体例を示します. まず (hello) モジュールの中の meke-hello 手続きを取り出して, 以下のような新たなモジュールを作ります. そのロードパスは /home/user/mymods にして, モジュール名は (submod) にします.

;; submod.scm

(define-module (submod) 
  #:export (make-hello))

(define (make-hello name)
  (string-append "*** Hello," name "!!! ***"))

次に (hello) モジュールから make-hello 手続きを削除して, その代わりに (submod) モジュールをロードします.(submod) モジュールをロードする方法は2つあります. 1つの方法は use-modules 形式を使う方法です, つまり,(hello) モジュールの define-module のあとに, 以下のものを追加します.
(use-modules (submod))
もう1つの方法は #:use-module オプションを使う方法です. 今回の具体例では,こちらの方法を使用します.(hello) モジュールは以下のようになります.紫色の部分は, 前に示した (hello) モジュールからの変更箇所を示しています.make-hello 手続きは,削除する代わりにコメントアウトしています.
;; hello.scm

(define-module (hello)
  #:use-module (submod)
  #:export (hello-everybody hello-somebody))

(define (hello-everybody names)
  (for-each hello-somebody names))

(define (hello-somebody name)
  (display (make-hello name))
  (newline))

;; (define (make-hello name)
;;   (string-append "*** Hello," name "!!! ***"))

以下は,(hello) モジュールをテストするためのプログラムと実行例です.
#!/usr/bin/guile \
-L /home/user/mymods -e main -s
!#

;; use-hello.scm

(use-modules (hello))

(define (main args)
  (let ((names (cdr args)))
    (hello-everybody names)))
$ ./use-hello.scm Alice Bob Carol David
*** Hello,Alice!!! ***
*** Hello,Bob!!! ***
*** Hello,Carol!!! ***
*** Hello,David!!! ***

#:autoload

▹ #:autoload オプションは次の形式で使用します.
#:autoload module name name list
ここで,module name はモジュール名で,name listmodule name が示すモジュール内の束縛名のリストです. 以下,混乱を避けるために,module name モジュールのことをサブモジュールと呼び,module name モジュールを利用するモジュールのことをメインモジュールと呼ぶことにします.

このオプションを指定すると, 例えば,先ほどの #:use-module オプションの代わりに次のように指定できます.
;; hello.scm

(define-module (hello)
  #:autoload (submod) (make-hello)
  #:export (hello-everybody hello-somebody))

      ...... (以下同じ) ......

#:autoload オプションは, 機能的には #:use-module オプションと同じですが, 束縛名がアクセスされるまでサブモジュールのロードを延期する点が異なります. この延期機能は次のような場合に有効と思われます.

#:export

▹ #:export オプションの一般的な形式は次の通りです.
#:export (name-spec$_1$ ... name-spec$_n$)
ここで,name-spec$_i$ は
name または (orig-name . new-name)
のいずれかです.ただし,nameorig-name はモジュール内の束縛名で,new-nameorig-name の代わりに利用可能な新たな名前です. 前にも説明したように,#:export オプション(エクスポートリスト)は, 公開したい名前を指定するためのものです.namenew-name が公開されます.逆に,エクスポートリストに含めなかった束縛は,引き抜き方式を使わない限り, 他のプログラムでは利用できません.

▹ 楽しいことに,1つの束縛を複数の名前で公開できます. 長い名前の短縮名を公開したいときに役立つでしょう. 以下の (hello) モジュールは,hello-everybody 手続きを hello-everybodyhellohi の3つの異なる名前で公開します.
;; hello.scm

(define-module (hello)
  #:use-module (submod)
  #:export (hello-everybody 
            (hello-everybody . hello)
            (hello-everybody . hi)
            hello-somebody))

      ...... (以下同じ) ......

以下はテスト用のプログラムと実行結果です.
#!/usr/bin/guile \
-L /home/algo/mymods -e main -s
!#

(use-modules (hello))

(define (main args)
  (let ((names (cdr args)))
    (display "* use hello-everybody:\n")
    (hello-everybody names)
    (display "* use hello:\n")
    (hello names)
    (display "* use hi:\n")
    (hi names)))
$ ./use-hello.scm Alice Bob
* using hello-everybody:
*** Hello,Alice!!! ***
*** Hello,Bob!!! ***
* using hello:
*** Hello,Alice!!! ***
*** Hello,Bob!!! ***
* using hi:
*** Hello,Alice!!! ***
*** Hello,Bob!!! ***

#:re-export

▹ #:re-export オプションの一般的な形式は次の通りです.
#:re-export (name-spec$_1$ ... name-spec$_n$)
ここで,name-spec$_i$ は #:export オプションのものと同じです.

▹ #:re-expoprt オプションは,コアモジュールや他のモジュールからロードした束縛を, モジュール内の束縛であるかのように見せかけて又貸しするためのものです. 又貸しは次のような場合に有効と思われます.

• 旧バージョンとの互換性を維持する場合:例えば,Guileの (srfi srfi-16) モジュールは次のように定義されています.
(define-module (srfi srfi-16)
  #:re-export (case-lambda))
実質的なコードは何もありません. 旧バージョンでモジュールとして提供されていた case-lambda がコアモジュールに組み込まれたために,このようなことになったのだと思います.しかしながら,(srfi srfi-16) モジュールを積極的に利用しているアプリケーションがあるかも知れないので,そのような場合をサポートするためにこのモジュール(の定義)を残しているのだと思います.

• モジュールのインターフェースとして定義された手続き等がコアモジュールや他のモジュールのものと機能的に一致していて,新たに開発する必要がない場合:例えば,Guileの (srfi srf-1) モジュールは,リストを処理するための色々な手続きを提供しているのですが,srfi-1の仕様書は car,cdr,cons などのコアモジュールが提供する手続きもインターフェースの一部として定義しています.そのため,Guile の (srfi srfi-1) モジュールは次のような又貸しを行っています(又貸し部分のみ掲載). ちなみに,:re-export はシャープ記号(#)がありません(#はなくてもよいのでしょう).
;; srfi-1.scm

  :re-export (cons list cons* make-list pair? null?
              car cdr caar cadr cdar cddr
              caaar caadr cadar caddr cdaar cdadr cddar cdddr
              caaaar caaadr caadar caaddr cadaar cadadr caddar cadddr
              cdaaar cdaadr cdadar cdaddr cddaar cddadr cdddar cddddr
              list-ref last-pair length append append! reverse reverse!
              filter filter! memq memv assq assv set-car! set-cdr!
              iota)

#:replace

▹ #:replace オプションの一般的な形式は次の通りです.
#:replace (name-spec$_1$ ... name-spec$_n$)
ここで,name-spec$_i$ は #:export オプションのものと同じです.

▹ 正確に理解できていないのですが,このオプションは, コアモジュールが提供する束縛を変更するときに利用するようです. つまり,name-spec$_i$ で指定した名前を公開するとともに, コアモジュールが定める名前と重複したときには, その名前の束縛をモジュール側の束縛に置き換える, といった機能を発揮するようです. 例えば,(srfi srfi-1) モジュールは, コアモジュールが提供している map や for-each を置き換えるべく, 次のように宣言しています(replaceの部分のみ掲載).
  :replace (map for-each map-in-order list-copy list-index member
            delete delete! assoc)
なお,上記の :re-export と同様に,:replace はシャープ記号(#)がありません.

▹ ちなみに,束縛の置き換えがコアモジュールに限定されることなのかが,マニュアルを読んだだけでは,残念ながら筆者には分かりません. ちょっとした実験をしてみると, コアモジュールの束縛は確かに置き換えられるのですが, 独自に定義した束縛を置き換えることはできませんでした. さらに,GNU Guile 3.0.0 のリリースアナウンスメントの中に #:re-export-and-replace といった類似のオプションに関する説明文があるのですが,その説明文の中では「replace core bindings」と明言しています. なので,置き換えはコアモジュール限定ではないかと推測しています.

その他

▹ これまでに説明したオプション以外に, #:re-export-and-replace, #:version, #:duplicattes, #:pure といったものがあります.これらの説明は省略します.
(おしまい)