Guile入門/Guileの準備

変更履歴

概 要

Guileのインストール

(追記:2022.3.8) Guile 3.0.6 をリリースしたときにマニュアルの編成を変更したようです. 筆者が気づいた限りでは,3.0.5 の「6.7 Foreign Objects」と「6.8 Smobs」が,最新版のマニュアルでは「6.20 Foreign Objects」と「6.21 Smobs」に変わっています. ついでに言うと,僅かなバージョン番号の違いとはいえ,バグ修正だけでなく機能追加も行われています.筆者が勉強していて気づいた限りで,例えば call-with-port は 3.0.5 では実装されていなかったのですが,3.0.6 以降では実装されています.

動作確認:REPLを動かしてみる

REPLの起動

REPLの終了

REPLの設定変更

履歴機能の設定

プロンプトの変更

その他

Geiser on Emacs

Geiser はEmacs専用のパッケージです.Schemeのプログラムを編集する際に処理系依存のマイナーモードを設定したり,ファイル名補完機能を持ったREPLがEmacs上で利用できたりします. Guile[4.5 Using Guile in Emacs] でも検討事項の1つとして紹介しています.

以下,筆者が試した範囲で,Geiser について説明します. お試し程度の内容ですが,手がかりにはなると思います.

Geiserのインストール

Deiban 11(bullseye) では,以下のパッケージをインストールすれば, 直ちに Geiser が使えるようになります.
$ sudo apt -y install elpa-geiser
(参考) GeiserのWebページ では,elpa を使う方法を説明しています.こちらでインストールしたほうが,より新しい Geiser が使えるかも知れません.

Guile マイナーモード

後述する設定をいっさい行わずに, Emacsを起動してSchemeのプログラムファイルを開いてみると,「Guile」がマイナーモードに設定されています(下図参照).

Guile-minor-mode

Geiser のデフォルトの設定では,拡張子が .scm のファイルを開くと, 従来の Scheme モード(メジャー)に加えて Guile モード(マイナー)を設定するようです.筆者が経験した限りでは,Guile Scheme に固有の幾つかの構文キーワード(lambda* や let*-values など)がハイライトされます.

それから,「Guile/A」の /A は autodoc モードというものを表しているようなのですが,プログラム編集中にそれがどのように機能するのかは分かりません.

Geiser REPL の起動

Emacs上でREPLを起動するには,Emacsのミニバッファで下図のコマンド(M-x run-guile)を実行します.

M-x run-guile

これを実行すると,REPLが画面分割されて表示されます(下図). 以後,このREPLをGuileのREPLと同様に操作できます.

Geiser RELP on Emacs

注意 Geiser REPLは,Guile の REPL をエミュレートするために,Emacs Lispを使って開発されたREPLです.そのため,Guile REPL に対して利用可能な機能が使えないこともあります. 例えば,プロンプトを変えることは難しそうです.また,Cntr-D は使えません.

利点 筆者が思いつく範囲で,Geiser REPL は,Guile REPL に比べて次のような利点があります.

Geiser REPL の操作

最低限のキー操作を以下に示します.
キー 意味
TAB 定義済みの手続き名や変数名,およびファイル名の補完.
C-c C-q
Geiser REPL を終了します.ただし,バッファは閉じません.
,q(exit)(quit) でも終了できます.
C-c C-z REPLを再開します.
C-↑
C-↓
履歴操作.過去の入力が再利用できます. ただし,履歴は巡回的に変化して,最古入力や最新入力で止まることはありません. 履歴を巡回してみると,現在入力として空白が表示されるので,それによって履歴中の現時点を把握することができます.なお,履歴操作は下記のプロンプトジャンプ(C-c C-p と C-c C-n)とエンターキーを組み合わせても実行できます.
C-c C-k
C-c C-c
処理中の評価に割り込んで,エラーとして中止します. なお,Ctrl-C は,Geiserにおける多くのキー操作の接頭辞になっていて Geiser に捕捉されてしまうので,割り込めません.
C-c C-p 1つ前のプロンプトにジャンプします.Geiser:Cheat sheet には記載されていませんが,Geiser REPL のプログラム(geiser-repl.el)の中で定義されています. プロンプトにジャンプしてエンターキーを押すと,そのプロンプトの入力を引き連れて現在のプロンプトに戻ることができます.
C-c C-n 1つあとのプロンプトにジャンプします.Geiser:Cheat sheet には記載されていませんが,Geiser REPL のプログラム(geiser-repl.el)の中で定義されています. プロンプトにジャンプしてエンターキーを押すと,そのプロンプトの入力を引き連れて現在のプロンプトに戻ることができます.
C-c C-d C-i 手続きや変数に関するinfoドキュメントを表示します. マニュアルは頻繁に参照するので,これは便利かも知れません.
これ以外のキー操作は,Geiser:Cheat sheet を参照して下さい.

REPLの操作に関する注意点

REPLの操作に関して次の点に注意して下さい.

Geiser REPL の設定

Emacsの初期設定ファイル(init.el)にて,個人的には,以下の設定を行っています.

Geiser REPL on Emacs vs Guile REPL on Gnome-terminal

筆者はこれまで Gnome-terminal 上で Guile REPL を動かしてきました.Emacs も Gnome-terminal 上で動かしていて,Gnome-terminal のタブを使うことによって両者を1つの画面で切り替えながら使ってきました.機能的には Geiser REPL のほうが上であることは疑う余地はありません(あとは,安定性が気になるところですが).特に,ファイル名補完はぜひとも欲しい機能です.rlwrap を使って実現してみようとしたのですが,まことに残念ながら Guile の activate-readline が,その名が示すように,GNU readline ライブラリを使っているようでして,inputrc に何を設定してもすべて activate-readline に捕捉されてしまいます.rlwrap を利用する限り,力技しかなさそうです.

当面,Geiser を使ってみようと思っています. ただ,困ったことに,Geiser REPL は Guile REPL の初期設定ファイル(~/.guile )をうまく処理することができません.その初期設定ファイルの中身によっては,Geiser REPL の応答性能が目に見えて低下してちょっとイライラします.どうも,初期設定の処理として大丈夫なものとダメなものがあるようです.筆者の経験では,Guile REPL のプロンプトを変更する処理はダメなようです.

そこで,~/.guile の内容を次の2つのファイルに分けることにしました.
;;
;; ~/.guile

(use-modules (ice-9 readline))
(activate-readline)

(use-modules (system repl common))

(repl-default-option-set! 'on-error 'report)
;;  'debug or 'backtrace is available instead of 'report. 

(define my-prompt
   (lambda (repl)   ;; need to be a procedure??
    (format #f "guile~A> " 
      (let ((level (length (cond
                            ((fluid-ref *repl-stack*) => cdr)
                            (else '())))))
        (if (zero? level) "" (format #f " [~a]" level))))))

(repl-default-option-set! 'prompt my-prompt)
;; (repl-default-prompt-set! my-prompt)  is also possible 

;;
;; load original procedures

(let ((homedir (passwd:dir (getpwuid (getuid)))))
  (primitive-load (string-append homedir "/.guile-init-more.scm")))
下記のファイルはREPLの中で手軽に使いたい手続きを独自に定義しています.
;;
;; ~/.guile-init-more.scm

;;
;; original procedures
;;

(define (myload filename)
  (let* ((homedir (passwd:dir (getpwuid (getuid))))
         (fname (if (char=? (string-ref filename 0) #\~)
                    (string-append homedir (substring filename 1))
                    filename)))
    (primitive-load fname)))

(define pwd getcwd)

(define cd chdir)

(define* (ls #:optional arg)
  (system (string-append "ls " (if arg arg ""))))

(define (less filename) 
  (system (string-append "less -X " filename)))      
このように分割した上で,Emacsの初期設定ファイル(init.el)に次の設定を追加します. この設定によって,Geiser REPL の起動時に ~/.guile-init-more.scm がロードされ, これに登録した独自の手続きが Geiser REPL において利用できるようになります. さらに,Geiser REPL にとって問題のある処理を ~/.guile に残すことによって応答性能を低下させることもありません.一方,ターミナル上で Guile REPL を起動したときには両方のファイルがロードされます.
(おしまい)