Fork me on GitHub
#clojure-japan
<
2015-06-22
>
athos04:06:10

関数定義も普通にできるみたい

athos04:06:15

セッションタイムアウトは設定されてると思うけど、どのくらいの時間有効なんだろう

ayato_p04:06:41

有効というのは定義がいつまで使えるか、というこtです?

athos04:06:43

です。サンドボックスにはclojailを使ってると思うんですけど、仕組み上一定時間で定義というか名前空間を捨てていかないとGCできないので。

ayato_p04:06:54

僕もそれ前に気になりましたけど、気にしないことにしました(

athos04:06:09

うーん

athos04:06:42

あと、clojailはサンドボックスライブラリとしてもわりとザルなので、あまり使うのもどうなんだろうという気が

ayato_p04:06:23

具体的にどのへんがです?(ザル

athos04:06:13

clojailってコードをトラバースしてブラックリストに載ってるシンボルがコード中に出現しないかチェックしてるんですけど、ブラックリストに載ってるシンボルでもメタプログラミング的に生成してしまうと使えてしまう、とか

athos04:06:38

みたいな感じで、evalとかリスクのある名前は使おうとするとSecurityErrorになるようになってます。これがブラックリスト。

ayato_p04:06:53

闇の技術だ…

athos04:06:52

こんな感じに、evalってシンボルそのものじゃなくて、シンボルevaとシンボルlをコンパイル時につなげるみたいなことをすると、簡単に突破できます

ayato_p04:06:46

理屈は分かりますけど実際それを防ぐのだいぶしんどいですよね

athos04:06:39

なので、Clojureの真面目なサンドボックスを作ろうと思った場合、Clojureのコードを見るんじゃなくて、Javaのサンドボックス機能を使うなりして、実行時に「fooクラスのbarメソッドが呼ばれた」みたいなのを検知しないとダメだと思います

ayato_p04:06:13

なるほど

athos04:06:13

ただ、Javaのセキュリティ機能でメソッド呼び出しを禁止するためには、そのクラスがそういう実装になってないとダメなので、「Clojureのコアクラスのこのメソッドを呼び出しちゃいけない」っていうのを後づけで指定できないので、現実的にはさっきの方針はかなり難しいです

ayato_p04:06:53

Clojure そのものを修正する必要が出てくるということですかねー。真面目にやるなら。

athos04:06:51

もうちょっと現実的な方法としては、自前でクラスローダを用意して、Clojureコアのクラスをロードするときに特定のメソッドにセキュリティチェックを挿入するロードタイムウィービングとかが考えられますけど、まぁこっちにしてもヘビーなことには変わりないですね。

ayato_p04:06:47

(Java ってそんなこと出来るんですねっておどろいているところ)

athos04:06:08

クラスローダはバイト配列としてのJavaバイトコードからクラスを生成できるんで、そのバイト配列の段階ならお好きなように煮るなり焼くなりできます😉

ayato_p04:06:10

Java を煮る技術…

athos04:06:20

Clojureの場合、コンパイラとクラスローダがかなり密に結合してる感じがするんで、自前のクラスローダを差し挟んだときにまともに動くのかは分からないですね。やったことない。

icalo3505:06:22

なんかヤバい事してるー!?(ガビーン)

icalo3505:06:54

これ無限シーケンス評価しようとしたら止めてくれるんですよね?(不安顔)

athos05:06:34

やばいことはしてません!(まだ)

athos05:06:18

さすがに他のチャンネルに影響でそうでこわい

icalo3505:06:53

タイムアウトしてても割とこわい

athos05:06:36

タイムアウトまでの間、メモリ食うのは避けられないですからね

icalo3505:06:53

ClojureベースのDSLみたいなの作ろうとする場合、サンドボックスというかクリーンルーム?的なものを作るのって結構大変なんですね~

icalo3506:06:03

いや、ちゃんと機能限定すればそうでもない……?

athos06:06:07

汎用的なものを作るのは難しいと思います。今だとむしろ、仮想環境を作っては捨てる運用の方がよっぽど安く実現できそうな気がします。

icalo3506:06:03

仮装環境といいますと、REPLサーバみたいなものを作っては捨てる、みたいなイメージなんでしょうか(ピンときてない)

athos06:06:35

コンテナ型の仮想化技術を使って、リクエストごとにコンテナを作るようなのをイメージしてました

ayamada07:06:14

あるある > alter-var-root!

ayamada07:06:40

clojureはいまいち ! の有無の統一が取れてない感ある。 set! swap! とかは ! 付き、 aset alter-var-root とかは ! なし

athos07:06:09

どこかで、トランザクションの中で呼び出すべきじゃないものに!がついている、という説を見た気がしますけど、これもすべてをカバーできるルールではないでしょうね

athos07:06:23

ただ、この説だと破壊的操作でない io! マクロになんで!がついているのかっていう説明にはなるんですよね

ayamada07:06:13

おー、なるほどです

ayamada10:06:12

そういえばかなり前に出てた話題ですが、condのインデントが深くなる問題、自分は勝手に cond* という名前のマクロを書いて使ってます。ただこれを他の人もいじるコードでも使うべきかはかなり悩むところ

ayamada10:06:43

(他にも let1 とか、scheme(gauche)由来のマクロが結構あります)

ayamada10:06:03

ちょっとテスト
これでいいのかな

ayamada10:06:54

;;; (cond*
;;;   [(very-long-pred? arg1 arg2 arg3)
;;;    (very-long-proc! arg4 arg5 arg6)]
;;;   [(pred2?) => #(proc2 arg1 arg2 % arg3)]
;;;   [(pred3?) (do-aaa!) (do-bbb!) (do-ccc!)]
;;;   [:else (proc3! arg1 arg2 arg3)])
(defmacro cond*
  "cond came from scheme"
  [& clauses]
  ;; clausesが空の場合はnil(再帰展開時用。ifの第二引数が省略可能なのを利用)
  (if (empty? clauses)
    nil
    (let [current-clause (first clauses)
          left-clauses (rest clauses)
          pred (first current-clause)
          bodies (rest current-clause)
          body-fn (when (= '=> (first bodies))
                    (second bodies))]
      (cond
        ;; => がある場合は、predの結果をbodyに適用(優先順位高)
        body-fn `(if-let [r# ~pred]
                   (~body-fn r#)
                   (cond* [email protected]))
        ;; 'else もしくは :else の時はショートカット可能
        ;; TODO: 数値等の、他の真値にも対応してもよい
        (or
          (= 'else pred)
          (keyword? pred)) `(do [email protected])
        ;; それ以外の場合は普通にifに展開
        :else `(if ~pred
                 (do [email protected])
                 (cond* [email protected]))))))

ayamada10:06:13

こんなの

ayamada10:06:50

これまた話が戻るんですが、自分は :pre を使わずに、関数の最初の方で (assert ...) を書くようにしてます。理由は :pre だと例えば、 (defn foo [m] {:pre [(map? m)]} (do-something)) で m がmapじゃなかった時に「じゃあmには何が入っていたのよ?」というのを表示してくれないから…

ayamada10:06:54

とは言え (assert (map? m)) でもそれは同様なので、いちいち (assert (map? m) (str "Invalid map " m)) みたいに毎回書く事が多いです。

ayamada10:06:41

この辺、便利機能が実は既にあったりしないでしょうか…

ayato_p10:06:08

アサートの話だと個人的にそれはテストで解決できる気がするんですが、それじゃダメなんでしょうか?

ayamada10:06:05

あー、なるほどです、テストを通すなら、テスト側で渡してる値が分かってるからokですね。自分はあまりテスト書かないので…

ayato_p10:06:31

アサーションは「この関数にはこういうものを期待してます」という表明なので、そこにそれ以外のものが来る場合は呼び出し側の実装が悪いはずで、そっち側を僕なら調べるかなぁと。

ayato_p10:06:56

あと、そういうときは spyscope とか使うと楽ですかねー。

ayamada10:06:14

おー、こんなのあるんですね > spyscope

ayato_p10:06:09

僕が clojure 始めたときに教えてもらって結構重宝してました。(最近そういえばあまり使ってない気がしますが

icalo3512:06:32

おっ、丁度こんな感じの欲しいと思ってたのです。早速導入しよう。

icalo3512:06:11

なんかこう、1つの関数がズントコでかくなっていって、途中の動きがわからず混乱する事が多いのです。

ayato_p12:06:39

僕が書いたやつであれですけど、デバッグ周りで悩んでるならこの辺参考になると思いますー。 http://ayato.hateblo.jp/entry/20150419/1429437366 ついでに言うと Cursive ならステップ実行とか普通に出来て便利でした。

icalo3512:06:52

わー、ドンピシャなテクやたらあるー! ありがとうございます!

icalo3512:06:35

なんというか「あとがき」に共感することしきり。

icalo3512:06:17

というか暇なときにあやぴーさんのブログ全体的に読み漁った方が色々良い気がしてきた。ググって見付けて参考にした事は何度もあるのですが。

ayato_p13:06:15

(全体的に読み漁られると闇しかないです…)

ayamada13:06:59

ここしばらく、cljsでゲームを作っていました。 http://vnctst.tir.jp/ja/games/op0012-3.html

ayato_p13:06:03

オープニングがシュールで笑いましたw でも、凄いですね。こういうゲームとか作ったこと無いので作ってみたいです simple_smile

icalo3514:06:08

あばばば…cljsというか、js環境でこんだけ出来るんですか……しゅごい……

icalo3514:06:35

(非推奨とされているスマホプレしつつ)

icalo3514:06:21

canvasとかで昔のスーファミとかの疑似3Dやってるんですよねこれ。

ayamada23:06:48

はい。きちんと3Dの処理はしてないです。canvasを使ってます