GNU GuileでECMAScriptとEmacs Lispを試してみた

[GNU Guile](https://www.gnu.org/software/guile/)は，主にGNUソフトウェアに組み込む機能拡張用言語の処理系として開発されている．たとえば，[GNU Emacs](https://www.emacswiki.org/emacs/GuileEmacs)や[GNU make](https://www.gnu.org/software/make/manual/html_node/Guile-Integration.html)に組み込み，設定ファイルをその拡張言語で記述することができる…はずなのだが，組込用としては未だほとんど利用されていない．

ただ，デフォルトの言語であるSchemeの処理系単体としては（今では）よくできており，最新の仕様（RnRSやSRFI）に沿っていることはもちろん，REPLコマンドは比較的使いやすい．今年1月にメジャーバージョンアップした3.0系列では[JITネイティブコード](https://www.gnu.org/software/guile/docs/master/guile.html/Just_002dIn_002dTime-Native-Code.html)を生成するようになっている．

利用例は次の通り（サンプル記述は，手前味噌の[不動点コンビネータ記事](https://qiita.com/ytaki0801/items/596efbbcb07d38630bc9)その他より）．

```scheme
$ guile --use-srfi=1,42
GNU Guile 3.0.4
Copyright (C) 1995-2020 Free Software Foundation, Inc.

Guile comes with ABSOLUTELY NO WARRANTY; for details type `,show w'.
This program is free software, and you are welcome to redistribute it
under certain conditions; type `,show c' for details.

Enter `,help' for help.
scheme@(guile-user)> (let ((n 5)) (list-ec (: y 1 (+ n 1)) (: x 1 y) (list x y)))
$1 = ((1 2) (1 3) (2 3) (1 4) (2 4) (3 4) (1 5) (2 5) (3 5) (4 5))
scheme@(guile-user)> (define (fix f) (f (lambda (x) ((fix f) x))))
scheme@(guile-user)> ((((fix (lambda (fib) (lambda (f1) (lambda (f2) (lambda (n)
                               (if (= n 0) f1 (((fib f2) (+ f1 f2)) (- n 1))))))))
                        0) 1) 40)
$2 = 102334155
scheme@(guile-user)> (map
                       (((fix (lambda (fib) (lambda (f1) (lambda (f2) (lambda (n)
                                (if (= n 0) f1 (((fib f2) (+ f1 f2)) (- n 1))))))))
                         0) 1)
                       (iota 6 0 10))
$3 = (0 55 6765 832040 102334155 12586269025)
```

ところで，SchemeはあくまでGuileの**デフォルトの言語**であり，他の言語にも対応している．それがECMAScriptとEmacs Lispであり，REPLを利用している間は，`,language`または`,L`で言語を切り替えることができる．

```
scheme@(guile-user)> ,L ecmascript
Happy hacking with ECMAScript!  To switch back, type `,L scheme'.
ecmascript@(guile-user)> ,L elisp
Happy hacking with Emacs Lisp!  To switch back, type `,L ecmascript'.
elisp@(guile-user)>
```

ECMAScriptに切り替え，上記とほぼ同じ内容を実行した例は次の通り．比較するとわかるが，Schemeで利用できる関数がECMAScriptでも利用できる．ただし，ECMAScript対応は開発が止まっており，[ES3対応未完](https://www.gnu.org/software/guile/docs/master/guile.html/ECMAScript.html)という状態らしい．

```javascript
ecmascript@(guile-user)> function fix(f) { 
                           return f(function(x) {
                                     return fix(f)(x);
                                    });
                         };
ecmascript@(guile-user)> (fix(function(fib) {
                               function(f1) {
                                 function(f2) {
                                   function(n) {
                                     return n == 0 ? f1 : fib(f2)(f1+f2)(n-1);
                                   };
                                 };
                               };
                         })(0)(1)(40));
$1 = 102334155
ecmascript@(guile-user)> (map(fix(function(fib) { function(f1) { function(f2) { function(n) {
                                   return n == 0 ? f1 : fib(f2)(f1+f2)(n-1);
                                 }; }; }; })(0)(1), iota(6,0,10)));
$3 = (0 55 6765 832040 102334155 12586269025)
```

Emacs Lispに切り替え，上記とほぼ同じ内容を実行した例は次の通り．Schemeのレキシカルスコープは維持されており，Common Lispとほぼ同じ記述となる．なお，GNU Emacsのデフォルト設定である[ダイナミックスコープを利用する](https://www.gnu.org/software/guile/manual/html_node/Emacs-Lisp.html)ことも可能（本家GNU Emacsでも，最近はレキシカルスコープに切り替えることが可能）．

```emacs-lisp
elisp@(guile-user)> (defun fix (f)
                      (funcall f (lambda (x) (funcall (fix f) x))))
$1 = fix
elisp@(guile-user)> (funcall (funcall (funcall
                      (fix (lambda (fib) (lambda (f1) (lambda (f2) (lambda (n)
                        (if (= n 0) f1 (funcall (funcall (funcall fib f2) (+ f1 f2)) (- n 1))))))))
                    0) 1) 40)
$2 = 102334155
elisp@(guile-user)> (defun map (f l) (if (null l) '() (cons (funcall f (car l)) (map f (cdr l)))))
$3 = map
elisp@(guile-user)> (map (funcall (funcall
                      (fix (lambda (fib) (lambda (f1) (lambda (f2) (lambda (n)
                        (if (= n 0) f1 (funcall (funcall (funcall fib f2) (+ f1 f2)) (- n 1))))))))
                      0) 1) '(0 10 20 30 40 50))
$4 = (0 55 6765 832040 102334155 12586269025)
```

#備考

##更新履歴
* 2020-08-13：初版公開