Aquamacs で対応する括弧がハイライトされない場合

Carbon Emacs から Aquamacs に乗り換えたのですが、Clojure のコードを読み込んだら対応する括弧がハイライトされないことに気付きました。原因は ~/.emacs.el に以下の記述がされていたからでした。

(show-paren-mode)

この記述は Carbon Emacs だと対応する括弧を光らせるんですが、Aquamacs だと (show-paren-mode nil) に解釈されるようです。そのため、この記述を削除するか

(show-paren-mode t)

と書き直す必要があります。

ちょっとつまずきましたが、Aquamacs はキャレット(カーソル位置)が細くて見易く、前回終了時のウインドウ位置を覚えてくれるのがいいですね。

Clojure + Emacs でのシンボル補完を M-Tab ではなく Tab でやりたい。

swank-clojure(slime) を使うと REPL では Tab でシンボル補完してくれますが、ソースファイル内では M-Tab で補完になります。
インデントは C-i や C-j でできるので、ソースファイル内でも Tab キーひとつで補完したいです。

(add-hook 'slime-mode-hook
          '(lambda ()
            (local-set-key "\t" 'slime-complete-symbol)))

この設定だと Tab だけで補完してくれるんですが C-i がインデントじゃなくなってしまいます。C-i は Tab を意味しているそうです。C-i だけはインデントのままにする方法が分かりましたらどなたか教えてください。

2010/10/21 追記

kitokitokiさんに教えてもらいました。ありがとうございます。
~/.emacs.el

(add-hook 'slime-mode-hook
          '(lambda ()
             (define-key slime-mode-map [(tab)]
               'slime-complete-symbol)
             (define-key slime-mode-map (kbd "C-i")
               'lisp-indent-line)))

とすればokでした。Mac OS X 10.6.4 + Emacs 23.2.50.1(Aquamacs)で確認。
Emacs 22だとうまくいかなかったです。

参考
モードとキーバインドの基礎と Elisp

Ubuntu 10.04 での Clojure + Emacs + Leiningen の環境構築

2011/12/29更新

時代遅れの手順になってしまいました。

Clojure+Emacsな開発環境を作る(late 2011) - λab's Blogを参照してください。




ネットブックUbuntu 10.04 を入れたので emacs から Clojure を使う手順を最初の最初から書いてみる。2ヶ月ぐらい前にやったインストール方法だとだめでした。

環境
Ubuntu Netbook Edition 10.04
GNU Emacs 23.1.50.1
機種 HP mini 2140
  • emacs をインストールする

emacs23とか22とかいろいろあるみたいですがよく分からないので最新っぽいのをインストール

$ sudo apt-get install emacs-snapshot
$ sudo apt-get install emacs-snapshot-el
$ sudo apt-get install anthy-el

ELPA(Emacs Lisp Package Archive) Installのemacs22の手順でやったが問題ないようだ。emacs の *scratch* バッファを開き (M-x buffer-menuで選択) 、以下をコピペ。

  (let ((buffer (url-retrieve-synchronously
	       "http://tromey.com/elpa/package-install.el")))
  (save-excursion
    (set-buffer buffer)
    (goto-char (point-min))
    (re-search-forward "^$" nil 'move)
    (eval-region (point) (point-max))
    (kill-buffer (current-buffer))))

コピペしたらC-jで評価してやると .emacs が更新される。
emacsを再起動させて M-x package-list-package をやると package一覧が出てくる。swank-clojureのところで i を押して x でインストール。何かエラーが表示されるが気にしなくていいそうです。slime や clojure-mode は選択しなくてもok.多分。

↑ここを参考にしてsunのjavaを入れておきました。openjdkの違いとかはよく知らないです。

  • Leiningen のインストール

今やClojureにはほぼ必須のLeiningenも入れておく。
http://github.com/technomancy/leiningen/raw/stable/bin/lein を実行権をつけて /usr/local/bin/ とかに置いておく。

$ wget http://github.com/technomancy/leiningen/raw/stable/bin/lein
$ sudo mv lein /usr/local/bin
$ sudo chmod +x /usr/local/bin/lein
$ lein self-install
  • 適当に新しいプロジェクトを作る
$ lein new emacs-test
Created new project in: emacs-test
$ cd emacs-test/
$ ls -l
合計 16
-rw-r--r-- 1 fatrow fatrow  120 2010-05-06 03:39 README
-rw-r--r-- 1 fatrow fatrow  173 2010-05-06 03:39 project.clj
drwxr-xr-x 3 fatrow fatrow 4096 2010-05-06 03:39 src
drwxr-xr-x 3 fatrow fatrow 4096 2010-05-06 03:39 test

project.clj に以下を追加。

(defproject emacs-test "1.0.0-SNAPSHOT"
  :description "FIXME: write"
  :dependencies [[org.clojure/clojure "1.2.0"]
                 [org.clojure/clojure-contrib "1.2.0"]]
  :dev-dependencies [[swank-clojure "1.2.1"]]) ;←追記

これがあると、emacsからREPLを使える。

  • ライブラリを取ってくる
$ lein deps
  • emacs からREPL を掴む。

ターミナル

$ lein swank
user=> Connection opened on local port  4005
#<ServerSocket ServerSocket[addr=localhost/127.0.0.1,port=0,localport=4005]>

emacs

M-x slime-connect
Host: 127.0.0.1
Port: 4005
Versions differ: nil (slime) vs. 20100404 (swank). Continue? (y or n) y

下記の方法はM-x package-list-package で swank-clojure をインストールした場合できるが、古いバージョンなので推奨されていない。

M-x swank-clojure-project
~/code/emacs-test

これで emacs の中に クラスパスが通った REPL が現れるはず!一回セットアップできたら後は快適です。

user> (use 'clojure.contrib.classpath 'clojure.contrib.pprint)
nil
user> (pprint (classpath))
(#<File /home/fatrow/code/emacs-test/test>
 #<File /home/fatrow/code/emacs-test/src>
 #<File /home/fatrow/code/emacs-test/classes>
 #<File /home/fatrow/code/emacs-test/lib/clojure-1.1.0.jar>
 #<File /home/fatrow/code/emacs-test/lib/clojure-contrib-1.1.0.jar>
 #<File /home/fatrow/code/emacs-test/lib/swank-clojure-1.2.0-20100502.112537-11.jar>)
nil
user> 

あと、これは作業の途中で適当にやったが必要ないと思う。

M-x slime

Clojureをインストールするかと聞かれるのでy


参考
swank-clojure github UsageとInstallationの項目
Leiningen github Installationの項目
ELPA(Emacs Lisp Package Archive) Install
HP mini 2140 で無線LANを使うための設定

Clojure の式の評価を追跡するツール eyewrap

user> (cap (* 2 (+ (- 3 8) 4)))
0 : + (* 2 (+ (- 3 8) 4))
1 : +   (+ (- 3 8) 4)
2 : +     (- 3 8)
2 :=>     -5
1 :->   (+ -5 4)
1 :=>   -1
0 :-> (* 2 -1)
0 :=> -2
-2

fatrow's eyewrap at master - GitHub

式の返してくる値だけじゃなくて、中でどの式がどう評価されているかを観察したいという目的のために作りました。仕組みは単純で、値を返す前に保存してから同じ値を返し、あたかも元の式のままであるようなコードを生成するマクロになっています。値を返す前に保存してから同じ値を返すという仕組み上、残念ながら末尾再帰の recur は使えません。引数として渡されたコードは完全にマクロ展開されてトレースに表示されます。ですので元のコードとの対応関係が分かりにくいかもしれません。

  • 使い方

Leiningen を使用します。 project.clj に [eyewrap "0.x.x"] を追加。現在のバージョンはClojarsを参照して下さい。

:dev-dependencies [[eyewrap "0.x.x"]]
% lein deps

use しておく。

user> (use 'hozumi.eyewrap)
nil
  • 普通の式をトレースする

cap マクロにそのまま式を渡してやります。

(cap <code>)

簡単な計算

user> (cap (+ 1 (* 2 3)))
0 : + (+ 1 (* 2 3))
1 : +   (* 2 3)
1 :=>   6
0 :-> (+ 1 6)
0 :=> 7
7

左の数字はその行の表示している階層レベルです。階層レベルによってインデントしています。 + は今から評価しようとしている式。 => は評価結果。 -> は子供の式を評価した結果、親の式はどうなったかを表しています。

  • 関数をトレースする
(cap 適当な名前 (defn ...))

以下の引数で与えた関数 touch はどういう動作をするでしょうか。

user> (cap ppp (defn touch [coll target-index]
		 (-> [(coll target-index)]
		     (into (subvec coll 0 target-index))
		     (into (subvec coll (inc target-index))))))
#'user/touch

まず、defn とは別に第一引数で与えられた名前で関数が作成され、その関数を使用してトレースを表示します。

user> ppp
#<user$eval__6979$ppp__6981 user$eval__6979$ppp__6981@680e62df>

まだ、何も入っていません。

user> (ppp)
:const

defn で作成した関数を実行すると、実行時の各値が保存されます。

user> (touch [:a :b :c :d] 3)
[:d :a :b :c]

トレースを表示。

user> (ppp)
0 : + (into (into [(coll target-index)] (subvec coll 0 target-index)) (subvec coll (inc target-index)))
1 : +   (into [(coll target-index)] (subvec coll 0 target-index))
2 : +     [(coll target-index)]
3 : +       (coll target-index)
3 :->       ([:a :b :c :d] target-index)
3 :->       ([:a :b :c :d] 3)
3 :=>       :d
2 :->     [:d]
2 :=>     [:d]
1 :->   (into [:d] (subvec coll 0 target-index))
2 : +     (subvec coll 0 target-index)
2 :->     (subvec [:a :b :c :d] 0 target-index)
2 :->     (subvec [:a :b :c :d] 0 3)
2 :=>     [:a :b :c]
1 :->   (into [:d] [:a :b :c])
1 :=>   [:d :a :b :c]
0 :-> (into [:d :a :b :c] (subvec coll (inc target-index)))
1 : +   (subvec coll (inc target-index))
1 :->   (subvec [:a :b :c :d] (inc target-index))
2 : +     (inc target-index)
2 :->     (inc 3)
2 :=>     4
1 :->   (subvec [:a :b :c :d] 4)
1 :=>   []
0 :-> (into [:d :a :b :c] [])
0 :=> [:d :a :b :c]
[:d :a :b :c]

recur は使えないし、バグも沢山あると思うのであまり実用的じゃないですが、簡単な処理をちょっと見てみたいという時にはいいかもしれません。

cond をスリムにする condp

普通の cond

(defn cond1 [x]
  (cond
    (= x :hey) "Hey!"
    (= x :bye) "Bye!"
    (= x :hi) "Hi!"))

重複するパターンがあります。condp を使うとスマートに。

(defn condp1 [x]
  (cond = x
     :hey "Hey!"
     :bye "Bye!"
     :hi "Hi!"))

次の例はちょっとトリッキーなので、あまり推奨されないかもしれない。

(defn cond2 [x]
  (cond
    (list? x) "List!"
    (map? x) "Map!"
    (vector? x) "Vector!"))
(defn condp2 [x]
  (condp #(%1 %2) x
    list? "List!"
    map? "Map!"
    vector? "Vector!"))


参考 Learning Clojure #12: condp

リストではなくシーケンスに入っている関数呼び出しも実行できる

macroexpand は展開結果をリストじゃなくてシーケンスで返してくるのにちょっと驚いた。

user> (macroexpand '(-> 1 (+ 2) (- 3)))
(- (clojure.core/-> 1 (+ 2)) 3)
user> (type (macroexpand '(-> 1 (+ 2) (- 3))))
clojure.lang.Cons
user> (seq? (macroexpand '(-> 1 (+ 2) (- 3))))
true
user> (list? (macroexpand '(-> 1 (+ 2) (- 3))))
false

えっ、リストじゃないの? ってことは関数はリストじゃなくてシーケンスに入っていても構わないって事か。

user> (eval [+ 1 2])
[#<core$_PLUS___4509 clojure.core$_PLUS___4509@2d0a5068> 1 2]

ベクタはベクタに評価される。けど、シーケンスにしてやれば

user> (eval (seq [+ 1 2]))
3

リストみたいに関数を実行できる。でも list? に対して false を返されるのってどうなんだろ。
関数の入ったリストかを判定するのには list? よりも seq? を使った方がいいのか。それじゃ、リストの存在意義って何なんだろ。

user> (type '(1 2))
clojure.lang.PersistentList
user> (list? '(1 2))
true

これは当然。

user> (type (seq '(1 2)))
clojure.lang.PersistentList
user> (list? (seq '(1 2)))
true

一回 seq を噛ませてもリストに対しては何も変換しないようだ。
ベクタやマップはシーケンスに変換される。

user> (type [1 2])
clojure.lang.PersistentVector
user> (type (seq [1 2]))
clojure.lang.PersistentVector$ChunkedSeq
user> (type {:a 1})
clojure.lang.PersistentArrayMap
user> (type (seq {:a 1}))
clojure.lang.PersistentArrayMap$Seq

シーケンスはリストじゃない。

user> (list? (seq [1 2]))
false

仮引数の評価

Lispの初歩的な事で納得できねぇと悶々していたけど、納得できた。
評価について。
クオートが付いてるのは評価されるとクオートが一つ剥がれてシンボルやリストそのものになる。クオートが付いていないリストは評価される=関数が実行される。

まず、簡単な関数。

(defn a1 [test]
  (if test (println "hello")))

実行すると、期待通り。

user> (a1 true)
hello
nil
user> (a1 false)
nil

次に実行する関数を引数で渡せるバージョン。

(defn a2 [test s]
  (if test s))

呼び出すときに実行したいリストをそのまま引数に渡すと、a2はマクロではなく関数なので最初に引数を全て評価してしまう。

user> (a2 false (println "hello"))
hello
nil

実行したいリストにクオートを付けて、評価を一つ遅らせればいいだろうと考えてやってみると、

user> (a2 true '(println "hello"))
(println "hello")
user> (a2 false '(println "hello"))
nil

あれ、渡したリストが実行されること無くそれ自身が返ってきてしまった。
eval すれば期待通りの動作をする。

(defn a3 [test s]
  (if test (eval s)))
user> (a3 true '(println "hello"))
hello
nil
user> (a3 false '(println "hello"))
nil

しかし、何で a2 ではダメなんだろう。関数の中ではクオートが剥がれた(println "hello")になってるはず。クオートされていないリストは関数が実行されるんじゃ・・。
とここで分かった。関数内の仮引数の存在を忘れていた。仮引数は評価されて初めて値を返してくるシンボルなのだという当たり前のことを理解できていなかった。シンボル=ポインタ。シンボルが評価されると関連付けられた値を返してくる。