ecl (3)
今年もアプルのWWDCが開催された。WWDCって、ワールドワイド開発者会議の事だ。 アプルに取って大事な大事な開発者に新製品をお披露目して、開発ヨロってお願いする 大事な会議。
大方の予想を裏切って、大事なものは何も無かった。アプル信者はがっかりして、 うらみつらみを、 WWDC 2014でアップルが発表しなかった7つの製品 にまとめている。
iOS7がいOS8になるぐらいの事じゃ、驚きませんよ。改修工事なんだから。新しい器を 出してこそのアプルでしょう。ジョブズの遺産を食い潰してしまったら、もう新しい物は 有りませんじゃ、停滞モードに停滞中ですな。それとも衰退モードか?
唯一注目されたのが、Swiftって言語。邪すれば、これもジョブズの遺産だな。ちゃんと 資料まで公開してんだから、開発は大分前から始まっていたはず。仕様決めは鶴の声が 有ったに違いない。
Swiftはすごいで考察されてるように、 アプルの信者獲得作戦に違いない。なんたって、『コンピュータ、ソフト無ければ、ただの箱』 ですから、Appを量産するシンパを大事にするのは道理ですよ。
でもね、この言語、箱を選ぶんだろうな。ああ、箱と言うより、動くOSを選ぶんだろうな。 OS Xでしか動きません。ipadとかじゃ駄目ですから、箱も買ってね。
かつて、SunのJava、Googleのgo、エリクソンのerlangみたいに、どこでも動くって 事で、世界に広まった事をアプルは学習して欲しいぞ。
Appleが新言語、Swiftを発表するも、すでに閉鎖的すぎて絶望しかない 同じ事を危惧してる方がおられました。そう、アプルの甘言に騙されてはいけません。
おいらもちらっと解説書を読んでみましたよ。甘い誘惑が待ってますねぇ。i電話の センサーとかGPSはどうやってコントロールするの? どうやって使うの? そういう事が何も書かれていない、ただの誘惑書でした。 これじゃ、なんにもならんぞ。まあ、大体、厳しい所は隠すのが世の慣わし。 厳しい目で眺めておきましょ。
もう少しslime
前回に引き続いて、もう少しslimeの探求。
別マシンで動かしているecl上のswankサーバーに、ローカルから接続する方法は有るのか? 実際に別マシン(名はfb10)で、eclを起動して、replから start-swank.lispし、4005番で 待機させる。
そして、ローカル機(名はuB)から、つついてみる。
sakae@uB:~$ telnet fb10 4005 Trying xxx,xxx,xxx,xxx ... telnet: Unable to connect to remote host: Connection refused
接続が拒否された。そりゃそうだよな。ループバックアドレスで起動してるんで、接続出来る 訳が無い。だったら、グローバルアドレスで起動させればいいんでないかい。 探してみたら、swank.lispに
(defparameter *loopback-interface* "127.0.0.1")
なんてのが有って、こやつを使ってソケットを開いてる。ここをグローバルIPに書き換えちゃう手 があるな。でも、swankサーバーには、認証機構が全く無いですぜ。CL資源を世界に公開しちゃって いいものか、悩む所だ。
で、slimeのマニュアルを見てた所、sshでトンネルを掘って、接続する方法が紹介されてた。
sakae@uB:~$ ssh -N -f -L 4005:localhost:4005 fb10 Password for sakae@fb10: sakae@uB:
これで、トンネルが掘れた。(-fでプロセスをBGに追いやり、かつ、-Nでリモート側では 静かにしてるって言う仕様です) そして、このトンネルは、-Lで、uB側の4005番が、リモート側の127.0.0.1:4005番に 転送されるっていう限定的な物だ。 後は普通にuB側でemacsを立ち上げ、slime-connectし、ローカルの4005番に接続すればOK。
お遊びで、複数台のlocalhostからリモートのswankに接続出来るかやってみたら、2台目は こんなエラーが出て、接続を拒否されたぞ。
channel 2: open failed: administratively prohibited: open failed
だめじゃんswank君、君はまだ修行が足りんぞ。そういうおまえはアホか。老人性誇大妄想症かに かかってますなあ。
こういう機能を使って、CLで立ち上げたWebサーバにslimeで接続するって離れ業を やってる方がおられた。 Clack & Swank によるインタラクティブなWebシステム開発
上記は、start-swankって事で、身も蓋もなくswankサーバーを建てちゃったけど、この スクリプトを使わない、いわゆる、emacsがこっそりやってる事を、晒してみよう。目を皿に してコードを眺めていたら、こんなのが出来るのね。
sakae@uB:~/.emacs.d/slime$ cat z.lisp (load "./swank-loader.lisp") (setq swank-loader::*fasl-directory* "/tmp/fasl/") (swank-loader:init) (load "./swank-backend.lisp") (load "./swank.lisp") (swank:start-server "zPORTSNO")
ちょっとしたローダーを書いておいて、起動する。
sakae@uB:~/.emacs.d/slime$ echo '(load "z.lisp")' | ecl : ;;; Loading "/tmp/fasl/swank.fas" ;;; Warning: These Swank interfaces are unimplemented: (ACTIVATE-STEPPING ADD-FD-HANDLER ADD-SIGIO-HANDLER BACKGROUND-SAVE-IMAGE DUP EXEC-IMAGE FRAME-CALL LIST-CALLEES LIST-CALLERS MACROEXPAND-ALL MAKE-FD-STREAM REMOVE-FD-HANDLERS REMOVE-SIGIO-HANDLERS RESTART-FRAME RETURN-FROM-FRAME SAVE-IMAGE SLDB-BREAK-AT-START SLDB-BREAK-ON-RETURN SLDB-STEP-INTO SLDB-STEP-NEXT SLDB-STEP-OUT TOGGLE-TRACE) ;;; Loading "/home/sakae/.emacs.d/slime/swank-backend.lisp" ;;; Loading "/home/sakae/.emacs.d/slime/swank.lisp" ;;; Loading "/home/sakae/.emacs.d/slime/contrib/swank-repl.lisp" ;; Swank started at port: 49538. CL-USER> ;; swank:close-connection: NIL
一応立ち上がったみたい。別端末からslime-connectすると、接続はされるんだけど、 直ぐに遮断されちゃった。注意書きにあるやつを丁寧に消していけば、旨く繋がるのかな。
今後の実験の為に、telnet接続の例を載せておく。slimeのプロトコルって、先頭に 6byteのHEXで、メッセージ長を表す事になってた。
sakae@uB:~$ telnet localhost 4005 Trying ::1... Trying 127.0.0.1... Connected to localhost. Escape character is '^]'. 00002d(:emacs-rex (swank:connection-info) nil t 1) 000447(:indentation-update (("with-output-to-cdb" 1 ("ECL-CDB")) .......
所で、接続する度にport番号が変わる。emacsはこの番号をどうやって知るのか。 ちょいと疑問になったので、前回やったトレース結果を探ってみる。
vfork(Process 1425 attached <unfinished ...> : pipe([4, 5]) = 0 pipe([6, 7]) = 0 : [pid 1425] close(0) = 0 [pid 1425] close(1) = 0 [pid 1425] close(2) = 0 [pid 1425] dup2(6, 0) = 0 [pid 1425] dup2(5, 1) = 1 [pid 1425] dup2(5, 2) = 2 [pid 1425] close(6) = 0 [pid 1425] close(5) = 0 : [pid 1425] execve("/usr/local/bin/ecl", ["/usr/local/bin/ecl"], [/* 27 vars */]) = 0 : waitpid(-1, [{WIFEXITED(s) && WEXITSTATUS(s) == 0}], WNOHANG|WSTOPPED) = 1425 waitpid(-1, 0xbfe723cc, WNOHANG|WSTOPPED) = -1 ECHILD (No child processes)
pid1425は、生まれた子供であるecl。子供の標準入出力を、親側で用意したパイプに すげ替えるという工事をやってる。これで、親子の絆が深く結ばれましたね。 サーバーのport番号も、親側からは丸見得。
[pid 1424] write(7, "(progn (load \"/usr/share/common-"..., 200) = 200 [pid 1424] write(3, "\33[?25lPolling \"/tmp/slime.1424\" "..., 101) = 101
どんな指令が親から子へ伝わってるか調べてみると、どこもこれっぽい。prognで、命令を まとめているのが認められるけど、残念ながら途中で切れてしまっている。その後、ポーリングして、 子が起動したか確認してんだな。
子供がどんなTCPポートで待機してるかemacsは分かっているんで、後は涼しい顔して TCPでやり取りすればいいんだ。ユーザーが何かヘマをやらかして、緊急で子共に連絡 したいような場合も、この絆チャネルが使えるとな。このテクニックって、ftpのコントロールポートと データポートの考え方だな。納得しましたよ。
ftpの場合、20と21が使われていたはずだけど、どうだったかな? /etc/servicesを見ろ
ftp-data 20/tcp ftp 21/tcp
20番ポートがウニャムニャって話があって、盛んにpassiveなんて事騒いでいたな。全ては、 遠い過去になりつつあるのう。
profileをreplから
前回はprofileをreplから使おうとして、あえなく失敗した。profileなんてパッケージが 見つからなかったんだ。そこでCL界のgemことASDFが悪さをしてるんじゃないかと、予想を 立てたんだ。
eclの場合contribの中にasdfが収納されてた。ファイルの冒頭をみたら、使い方は、 ASDF Manualを見ろとな。 ぐぐったら、有り難い事に勇士の方が ASDF 日本語を公開して下さっていた。 これが有れば、鬼に金棒!
ASDFって、Another System Definition Facilityの4文字熟語じゃなくて略語らしいけど、 これは後からのこじつけ。キーボードの左手系ホームポジションを思い出してみろ。 指をあちこちに移動せずとも素直に打てるではないか。Lisperは、本来ものぐさ太郎なのさ。
> (require :asdf) ;; ASDFを使えるようにする ;;; Loading #P"/usr/local/lib/ecl-13.5.1/asdf.fas" ("ASDF" "CMP") > (require :profile) ;; ASDFの力を借りてPROFILEを使えるようにする NIL > (use-package :profile) ;; profileの定義が見えるようにする T > (report) ;; 測定器のキャリブレーション measuring PROFILE overhead..done seconds | consed | calls | sec/call | name ---------------------------------------------------- ---------------------------------------------------- 0.000 | 0 | 0 | | Total estimated total profiling overhead: 0.00 seconds overhead estimation parameters: 5.6e-8s/call, 1.3760001e-6s total profiling, 5.8399996e-7s internal profiling > :ld cup.lisp ("cup.lisp") ;; 被測定物をロード > (profile tak fact) ;; 測定対象を登録 > (tak 12 6 0) ;; 監視下で実行 1 > (fact 100) ;; 他のやつも実行 93326215443944152681699238856266700490715968264381621468592963895217599993229915608941463976156518286253697920827223758251185210916864000000000000000000000000 > (report) ;; 結果表示 seconds | consed | calls | sec/call | name -------------------------------------------------------- 0.000 | 4,579,352 | 63,609 | 0.000000 | TAK 0.000 | 7,360 | 101 | 0.000000 | FACT -------------------------------------------------------- 0.000 | 4,586,712 | 63,710 | | Total estimated total profiling overhead: 0.09 seconds overhead estimation parameters: 5.6e-8s/call, 1.3760001e-6s total profiling, 5.8399996e-7s internal profiling
本業の時間取得はおかしいのに、余計な時間報告だけは旨くいってるっぽい。
> (reset) NIL > (tak 18 9 0) 9 > (report) seconds | consed | calls | sec/call | name ---------------------------------------------------------------- 0.000 | 2,501,121,775 | 15,829,689 | 0.000000 | TAK ---------------------------------------------------------------- 0.000 | 2,501,121,775 | 15,829,689 | | Total estimated total profiling overhead: 21.78 seconds overhead estimation parameters: 5.6e-8s/call, 1.3760001e-6s total profiling, 5.8399996e-7s internal profiling These functions were not called: FACT
随分待たされた挙句に結果が出てきた。オーバーヘッドが、とんでもない時間に なってるな。callの度に余計なルーチンが走るのだろうから、無理ないか。
abcl
家にあるCommonLisp本を見てたら、abclこと Armed Bear Common Lisp (ABCL)が紹介されてた。熊さんの強さは 兵器なみって事で、あやかったのでしょうか。土台がJavaって所に越えられない一線、 shiroさん風に言うと、『ガラスの天井』ってのがあるのかも知れません。
あれ、この言い回し、苦労じゃ本に出てたのかな。最近はclojureが人気みたいだけど、 最初から苦労じゃと名乗っているんじゃ、そりゃ大変だわな。
そう言えば、人気の一端は、出版された書籍と相関があると思うんだ。最近、 日本国内で出版されたLisp系の書籍なんてサイトを 見つけて、しげしげと眺めております。ああ、同HPの処理系の年譜も感慨深いものが あるなあ。gaucheも息が長いな。
何故、人はJavaでLispを実装するか? それは、昔のJavaにLAMBDAが無かったからさ。 一種の憧れ、ミーハー族に違いない。Javaなら、素直にオブジェクト嗜好してればいいのに。 世の中、一定のはみ出し者が居るって事です。
冗談はさておき、オイラーも変な人になってみる。
[sakae@manjaro .abcl]$ java -jar abcl.jar Armed Bear Common Lisp 1.3.1 Java 1.7.0_51 Oracle Corporation OpenJDK Client VM Low-level initialization completed in 1.294 seconds. Startup completed in 5.842 seconds. Type ":help" for a list of available commands. CL-USER(1): :help COMMAND ABBR DESCRIPTION apropos ap apropos bt backtrace n stack frames (default 8) cd change default directory cf compile file(s) cload cl compile and load file(s) continue cont invoke restart n describe de describe an object error err print the current error message exit ex exit lisp frame fr set the value of cl:* to be frame n (default 0) help he print this help inspect in inspect an object istep i navigate within inspection of an object ld load a file ls list directory macroexpand ma macroexpand an expression package pa change *PACKAGE* pwd pw print current directory reset res return to top level rq require a module trace tr trace function(s) untrace untr untrace function(s) Commands must be prefixed by the command character, which is ':' by default.
7秒ぐらいかけて、もっさりと起き上がってきました。(ゆったりしてるから、熊さんなのね。 納得させられましたよ。) そして、ACLみたいに、replで愛嬌を 振りまくのも熊さんだな。嘘を言うな。山菜取りに行った人が熊さんに襲われて瀕死の 重傷を負ったとか、熊さんが通学路に現れて、厳戒態勢で猟友会の人が見張っていたとか、 この地ではよくニュースになるから、童話の世界とは違うんよ。
eclにも、このコロンコマンドが有って 、便利に使ってますよ。作者のにくい心使い、今風に言うと、お・も・て・な・し だな。
このabclはtar玉に入れられていたのを捕まえてきて、万次郎Linuxの中へ放牧したけど、 おまけで、abcl-contrib.jarなんてのが付いていた。これって一体何? jarファイルって zipなんで
[sakae@manjaro .abcl]$ unzip -l abcl-contrib.jar Archive: abcl-contrib.jar Length Date Time Name --------- ---------- ----- ---- 0 2014-04-29 22:50 META-INF/ 101 2014-04-29 22:50 META-INF/MANIFEST.MF 2279 2014-04-17 12:48 README.markdown 0 2014-04-29 22:50 abcl-asdf/ 6176 2014-04-17 12:48 abcl-asdf/README.markdown 1550 2014-04-29 22:50 abcl-asdf/abcl-asdf.asd 5936 2014-04-17 12:48 abcl-asdf/abcl-asdf.lisp : 0 2014-04-17 12:48 quicklisp/ 1326 2014-04-17 12:48 quicklisp/quicklisp-abcl.asd --------- ------- 236528 49 files
この他にも、jfliとかjssとか言う、謎の餌が付いてきた。これらは、どうやって使うのだろう? javaのマナーとCLのマナーを習得しないと、餌に有り付けなさそう。
CLのマナーは、統一インターフェースslimeに任せてしまえばいいのか。
slimeを便利に使う
slimeで起動するCLは、inferior-lisp-program に設定してたけど、CLが大集合してくると いちいち設定し直すのが面倒。複数CLを切り替える便利な技がきっとあるはずと思って 調べてみると、ありましたねぇ。その前に、abclを起動する薄いラッパーを用意しておく事にする。 ええ、、aliasに登録したものじゃ、emacsが認識してくれないものですから。
[sakae@manjaro ~]$ cat .abcl/abcl #!/bin/sh cd /home/sakae/.abcl exec java -jar abcl.jar
熊さんは、人目に付かないように、ドットの中に隠してしまいましたよ。そして、slimeの 設定も変更。
(add-to-list 'load-path (expand-file-name "~/.emacs.d/slime")) (setq inferior-lisp-program "ecl") ;; default (setq slime-lisp-implementations '((ecl ("ecl")) (abcl ("/home/sakae/.abcl/abcl")) (clisp ("clisp")) (sbcl ("sbcl") :coding-system utf-8-unix))) (global-set-key "\C-cs" 'slime-selector) (require 'slime) (slime-setup '(slime-repl slime-fancy slime-banner)) ;; Hyperspec C-c C-d h (require 'hyperspec) (setq common-lisp-hyperspec-root (concat "file://" (expand-file-name "~/.emacs.d/HyperSpec/")) common-lisp-hyperspec-symbol-table (expand-file-name "~/.emacs.d/HyperSpec/Data/Map_Sym.txt")) (setq browse-url-browser-function 'w3m-browse-url)
普通に、M-x slime すると、eclが上がってくる設定。M-- M-x slime か C-u M-x slime すると 、起動するCLを聞いてくる設定だ。
起動した後、slime関係のbufferの切り替えは、C-c s で出来る。このslime-selectorを 起動すると、一文字で各種bufferを切り替え・閲覧出来る。詳しくは?で。cコマンドを 選択してみた。
Nr Name Port Pid Type -- ---- ---- --- ---- 1 ecl (127.0.0.1 46982) 10626 ECL 3 clisp (127.0.0.1 57351) 10628 CLISP 5 sbcl (127.0.0.1 47608) 10629 SBCL * 7 abcl (127.0.0.1 45478) 10641 Armed Bear Common Lisp
こうしておけば、性能比べも楽になるぞ。おいおいやってみよう。所で、フットプリントが 小さいって事で、eclをデフォにしたけど、その点よろ。topしてみっか。
PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND 10629 sakae 20 0 571468 58844 16092 S 3.3 5.7 0:27.22 sbcl 10641 sakae 20 0 670048 91192 8780 S 3.3 8.9 0:59.07 java 10626 sakae 20 0 41068 27556 3980 S 0.7 2.7 0:06.10 ecl 10628 sakae 20 0 13600 5580 1988 S 0.3 0.5 0:02.72 lisp.run 10619 sakae 20 0 49604 17036 7176 S 0.0 1.7 0:05.55 emacs
ふーん、clispが一番軽いのね。まあ、これ起動しただけだから、負荷をかけていったら どうなるかは、疑問ですけどね。
遅いのも良いものだ
前の方で、もう少しslimeでemacsがどうやってCLを起動しているかを調べた。残念ながら、 肝心な所がstraceの制限から闇になってたんだ。
でも、abclの起動が遅いものだから、ちらっと起動のやり取りがemacsのbufferに表示されちゃったぞ。 新幹線は速くて便利だけど、鈍行でのんびり行くと、見えなかったものが見えてきて、いい ものだな。
(progn (load "/home/sakae/.emacs.d/slime/swank-loader.lisp" :verbose t) (funcall (read-from-string "swank-loader:init")) (funcall (read-from-string "swank:start-server") "/tmp/slime.10619")) Armed Bear Common Lisp 1.3.1 Java 1.7.0_51 Oracle Corporation OpenJDK Client VM :
あれれ、おいらがやったのと同じ方法で起動してるよ。