COMMON LISP JP > Archives > 2018/06/03

2018/06/03 00:19:07 UTChojo
#
度々すみません!JSCLで (#j:parseInt "999") としたところ、LISPを通してJSの関数を実行でき、とても感動しています!!試しに今度はMath.maxを動かしてみよう!と思い (#j:Math.max 1 2 3) としてみたのですが、こちらは動作しませんでした。もちろん、lispではjsのアクセス演算子(ドット)なんて使えないと思いますし、オブジェクトのプロパティにアクセスするには恐らく (get 'max' #j:Math) のような方法でアクセスするのだろうと考えました。そもそもjsのオブジェクトはJSCLではどのように扱われるのだろう?連想配列のような扱いなのだろうか?と思いまして、common lispではgethashという関数が利用できることを知りました。そこで(gethash "max" #j:Math)としてみたのですがmax関数を取得することはできませんでした。初歩的な質問になってしまいますが、どうすればJSオブジェクトのプロパティにアクセスできるのでしょう。何かアドバイス頂けませんか?質問ばかりで申し訳ないです><
2018/06/03 00:29:54 UTCshiro
#
JSCLのソース中の、repl-web/repl.lispを見ると、#j:console:log みたいに呼んでますね。ドットじゃなくてコロンで区切ってみるとどうなりますか?
2018/06/03 01:04:42 UTChojo
#
いけました!ありがとうございますm(_ _)m
2018/06/03 02:24:42 UTChojo
#
また、以下のようにするとオブジェクトのプロパティにアクセスできるっぽいことがわかりました!
#
(setq obj (#j:JSON:parse "{\"a\":1,\"b\":2}"))
(jscl::oget obj "a") ; 結果:1
2018/06/03 02:35:44 UTChojo
#
うおー!これは感動です!一部うまく行きませんが、jsで書いていた部分をlispで書いて動くようにすることができました!
#
const jscl = require('jscl')

jscl.evaluateString(`
  (progn
    (setq http (require "http"))
    (setq server ((jscl::oget http "createServer") (lambda (req res)
      ; ((jscl::oget res "writeHead") 200 (#j:JSON:parse "{\"Content-Type\":\"text/plainb\"}")) ; うまくいかないのでコメントアウト
      ((jscl::oget res "end") "Hello World!"))))
    ((jscl::oget server "listen") 3000)
    (#j:console:log "Server running."))
`)
2018/06/03 22:19:17 UTChojo
#
度々質問させてください><。上記コードで jscl::oget となっている箇所から jscl:: を取り除きたいと考えています。jscl-repl環境で (in-package :jscl) を評価したところ、以降は jscl::oget としなくても oget で関数を呼び出すことができるようになったのですが、下記のコードのnode環境では oget を読み込んだ瞬間にエラーが発生してしまいます。
#
const jscl = require('jscl')

jscl.evaluateString(`
  (progn
    (in-package :jscl)
    (setq http (require "http"))
    (setq server ((oget http "createServer") (lambda (req res)
      ((jscl::oget res "end") "Hello World!"))))
    ((jscl::oget server "listen") 3000)
    (#j:console:log "Server running."))
`)
#
ERROR: 
Bad function designator `(OGET HTTP "createServer")'
2018/06/03 22:26:56 UTChojo
#
こちらの記事 http://www.asahi-net.or.jp/~kc7k-nd/onlispjhtml/packages.html にこのエラーの原因と同じことが記載されており、おそらくprognとの相性が悪いのだろうなというところまではわかったのですが、「progn式全体がreadで処理されるから」の意味がよくわからず、どうすればこの問題を解決できるのか躓いております(´・ω・`;)
2018/06/03 22:39:41 UTCshiro
#
シンボルの名前解決はread時に行われます。readはフォームを単なるS式(データ)として読むので、その中身がprognであるかとかin-packageであるかとかは一切気にしません。上のprogn全体は単なるリストであって、「in-packageが出てきたからパッケージを切り替えよう」なんてことをreadは考えないわけです。
#
readで読まれたS式が実行される段階でin-packageが認識されますが、その時点ではogetは既にcl-user:ogetとして読まれてしまっているので、そんな関数知らないよとなっちゃうわけです。
2018/06/03 22:47:26 UTCshiro
#
解決策ですが、evaluateStringがreadで式を読む「前」に*package*の値を切り替えておく必要があります。その方法はjsclのソースを読んでみないとわからないですね。ぱっと見では簡単でなさそうです。evaluateStringはprelude.jsで定義されてて、それは最終的にffi.lispのeval_in_lispで評価されますが、それが単純に(eval (read-from-string form))だからなあ。
#
パッケージ切り替えまでやるなら、バッチでclをjsに変換する道でやった方がいいかもしれないですね。
2018/06/03 22:51:56 UTChojo
#
なるほど!ご丁寧な解説ありがとうございます。つまり、jscl-repl環境で期待通りの動作をしたのは、対話する毎にreadされ、その度にin-packageが解釈されるから問題なく動作した。しかしevaluateStringの中で評価したコードは全てのコードが1度でreadされるため、うまくいかないということになるのでしょうか。
2018/06/03 22:53:03 UTCshiro
#
その解釈で合ってます。
2018/06/03 22:54:34 UTChojo
#
なるほど、確かにお話を聞いてるとclをコンパイルする方法でやった方が良さそうですね..(´・ω・`;)。すみません!なんとなくjscl::ogetと書くのが長ったらしく感じてしまったのと、jscl::ogetのようにコロンを二つ並べてアクセスするのは、あまり良くないという文章をどこかで見たので、安易な気持ちで取り組んで見たのですが、どうやら底が深い問題だったようです( ̄▽ ̄;)
#
もう一度頭の中を整理して見ます!貴重な情報と対応に本当に感謝ですm(_ _)m
2018/06/03 23:04:12 UTCshiro
#
というか::で内部シンボルにアクセスするのは良くないっていうのは、モジュールの内部に手を突っ込んでるからです。(in-package :jscl)するのはモジュールの中に入り込んじゃうことですから良くなさで言えばもっと良くない :-) もちろんモジュール自体を改造する意図なら別ですが。どっちかというと、ogetがexportされてないんだったら他の公的な方法があるはずだからそれを探す方がベターな気もしますが… tests/ffi.lispのコメント見る限り、ogetとかをexportするのはまだこれからだから今はjscl::でアクセスするしかなさそう。
2018/06/03 23:23:23 UTChojo
#
なるほど!(in-package :jscl)のはもっと良くない方法だったんですね(^_^;) 大人しくjscl::を付けてプログラミングすることにします!ありがとうございます!