Fork me on GitHub

(code "liquidz.uo")

Clojure 1.4が出ました! 前々から話題になっていたReader Literalsが使えるようになったので ちょっと遊んでみました。

ずは下準備

基本はこちらに書いてある通りです。

$ lein new myreader
$ cd myreader
$ vi project.clj
(defproject myreader "1.0.0-SNAPSHOT"
  :description "FIXME: write description"
  :dependencies [[org.clojure/clojure "1.4.0"]]) ; 1.4.0

readerの定義は src ディレクトリ直下に data_readers.clj というファイルを作り、そこに定義します。 手始めに文字列を大文字に変換するreaderを定義してみましょう。

src/data_readers.clj

{
 upper  myreader.core/upper-case
 }

src/myreader/core.clj

(ns myreader.core)

(defn upper-case [s]
  `(.toUpperCase s))

ここまで書いたらreplで試してみましょう。

$ lein repl
#upper"hello, world"
;=> "HELLO, WORLD"

なおreaderの定義は data-readers にbindingすることでも定義できます。ただ当然のことながらbinding後のreadから有効になるので、それ以前のものには適用されません。

(defn upper-case2 [s]
  `(str "_" (.toUpperCase ~s) "_"))
;=> #'fuga.core/upper-case2
(binding [*data-readers* {'upper fuga.core/upper-case2}]
  (println #upper"hello")
  ;=> HELLO
  (println (eval (read-string "#upper\"hello\""))))
  ;=> _HELLO_

Gaucheのデバッグプリント

少し使えそうなものとしてGaucheのデバッグプリント?=を実装してみましょう。

src/data_readers.clj

{
 ?=   myreader.core/debug-print
 }

src/myreader/core.clj

(ns myreader.core)

(defn debug-print [x]
  `(let [res# ~x]
     (println "?=" res#)
     res#))

こんなものを定義してあげると以下のようなことができます。

(map inc #?=(range 10))
;?= (0 1 2 3 4 5 6 7 8 9)
;=> (1 2 3 4 5 6 7 8 9 10)

ここの値がどうなってるか確認したいけど、わざわざletで囲んでprintlnするの面倒!ということは多いと思いますがこれで解決ですね!

字列中の式を展開

perlの "$var" 然り、rubyの "#{var}" 然り、他言語では文字列中で変数を展開する構文があります。これをClojureで実装してみましょう。

今回は文字列中のバッククオートに囲まれた部分を式として評価するように変換します。

src/data_readers.clj

{
 str myreader.core/expand-sexp
 }

src/myreader/core.clj

(defn expand-sexp [s]
  (let [ls (map-indexed #(if (even? %) %2 (read-string %2))
                        (str/split s #"`"))]
    `(apply str (list ~@ls))))

ではreplで試してみましょう。

(def i 100)
;=> #'hoge.core/i
#str"i = `i`"
;=> "i = 100"
#str"(+ 1 2) = `(+ 1 2)`"
;=> "(+ 1 2) = 3"

イイネ!

後に

使いどころはちゃんと見極めないとですが、可能性がぐんっと広がりますね! そんな感じでReader Literalsで遊んでみました。

コードは一応Gistにも貼っておきます。

» Go page top

blog comments powered by Disqus