ClojureのメタデータにGroovyのクロージャを仕込む

おそらく原理主義者の方々には怒られそうな内容ですが試してみました。

メタデータとは」みたいなことはおそらく個々人によっても仕組みによっても大きく解釈が異なるだろうし、ひとによっては水かけ論になるであろう内容だと思います。

けれどもデータそのものには直接関係ないという文脈でいけば「データ操作そのものもメタとなりうる」であろうと思うわけですよ。特にユーザインタフェース、自分がよくいう「ガワ」の部分なんかはそういう意味合いがものすごく強いものなんじゃないかなと思います。CMSみたいなRSSフィードを吐くような仕組みはwebサイトのデザインは完全にメタと考えることができるわけです。じぶんもフィードリーダ使ってて「このサイトってこういうデザインだったんだ」と思うこともしばしばですし。意外とメタとしてとらえるられることはおおいんじゃないかなと最近よくかんがえます。わるくいえば責任転嫁みたいなことか。

メタとメタじゃないものをいいかんじに分けると再利用可能なものが量産できるよね、とおもうわけです。

で、web APIへアクセスしてのデータ取得なんかも、そのアプリケーションからすればメタとしてとらえることもできるよねってことでtwitterをネタにして試してみたってのが今回の内容です。

まずこんなGroovyスクリプトを準備します。

import twitter4j.*
def getTwit = {s->
  def twitter = new Twitter()
  def q = new Query(s)
  def res = twitter.search(q)
  def ret = res.getTweets()
}

それをClojureから読みこんでClojureメタデータに仕込んでみまっせ。
ちなみに空リストのメタデータにGroovyクロージャを仕込みます。あとからメタデータにつっこんだGroovyクロージャをつかってひっぱってきたデータで空リストを埋めていくかんじです。

空リストは「じぶんがなにをつっこまれるか」は知るよしもなく、どんなつっこむ操作をされようがどんな言語を使おうが空リストからみればメタデータだよねー
…とか書くから怒られるんですねw

(import '[javax.script ScriptEngine]
        '[javax.script ScriptEngineManager])

(use 'clojure.contrib.seq-utils)

(def script (slurp "twit.groovy"))

(defn get-groovy-obj [gs]
  (let [groovy-script gs
        manager (new ScriptEngineManager)
        engine (. manager getEngineByName "Groovy")]
    (. engine eval groovy-script)))

(def obj (ref ()))
(def obj-extended (with-meta @obj {:doing (get-groovy-obj script)}))

clojure.contrib.seq-utilsを読みこんでるのはあとからflattenをつかいたいから。最近までflattenがseq-uitlsにあるのを知らなくて自前で書いてました。無知は死を意味しますね。

で、あとはこんなかんじで空リストを操作できるってな具合。

(defn get-tweets [word]
  (dosync
    (alter obj conj (.call ((meta obj-extended) :doing) word))))

(defn show-tweets []
  (map #(.getText %) (flatten (map #(seq %) @obj))))

(println @obj) 
(get-tweets "牛丼")
(println (show-tweets))
(get-tweets "豚丼")
(println (show-tweets))

空リストはrefなので、あとからどう使おうが協調してくれるし同期的に動いてくれる。

とか書いたけど、メタがどうのこうのとかじゃなくて単純におもろい。