kyotolisp#1 lt3 美しいlispの書き方 (1)
DESCRIPTION
kyotolisp#1 (kyotolisp.github.com) で発表した内容です。スライドは(2)に続きます。TRANSCRIPT
美しいlispの書きかた
~コーディングルール事始め~
自己紹介
ます。github.com/insanityです。ときおりWiki{pedia,books}に出没し
得意な方言はSchemeです。
今年で使い初めてから4年目です。
Gaucheが便利過ぎてしねる。
レンダリングされています。ちなみにこのスライドもlibcairo on Gauche with c-wrapper で
er で晒してます。現状カオスなコードですが一応 github/insanity/lightcontain
自己紹介 (続き)エディタはいつもvimを使っています。
える作りが好きなのです。.el を書くまでもなく、その場のコマンドで定型作業をサクッと終
ないです。というわけで、今回の話はemacsに全部お任せでOKという結論では
今日の話題
せっかくなら、読みやすいlispを書きたい。
マクロ展開形やVMの中間表現を少しでも読みやすく表示したい。
過去に書きなぐったコードを、見栄えだけ良くしたい。
元ネタ
Riastradh's Lisp Style Rules by Taylor R. Campbell
(http://mumble.net/~campbell/scheme/style.txt)
名前づけ
常識ですが...
variable-with-long-name
をハイフンを割り当てると便利です。(linuxならxmodmap)
Lispの変数はハイフンでつなぎます。日本語キーボードで変換キー
*global-variable*
かもしれない、という警告の意味で(多分)、強調します。
グローバル変数です。グローバル変数は副作用によって変更される
と思います。
定数を表すときもあります。SOME-CONSTANTよりかは読みやすい
+some-constant+と書く流派もあるようです。
コードレイアウト (スペースの入れ方)
はじめに
ます。
S式のコードの見た目は、スペースと改行の入れ方でほとんど決まり
だから、スペースと改行の入れ方は大事です。
タブか、スペースか
すすめです。
lispではインデント揃えを使うので、タブ幅で悩まないスペースがお
。
どうしてもタブがいい人は、タブ幅を書いてくれると(僕が)うれしい
; -*- tab-width: 8 -*- ; vim:ts=8
ネスト(cdr (assq 'banana '( (banana . 138) (apple . 80))))
普通、括弧はまとめて閉じます。
インデント量はスペース1つでも大丈夫ですが,
(インデント量が
(多すぎると、
(すぐに
(横幅を
(使い切ってしまい
(困ります))))))
そもそも読みにくいと思います。
ネスト (続き)
項目の一覧表では、
((item "banana") (item "apple") (item "orange") )
と閉じ括弧だけの行を残すと、項目の追加がやりやすくなります。
見栄え的にはちょっと微妙ですが。
ネスト (続き)
誤解のおそれのないときは、深さを省略しても大丈夫です(多分)。
(define (get-continuaction) (call/cc (lambda (cont) (cont cont))))
くなるのでちょっと横着しています。
Schemeではlambdaをやたらめったら使うので、ネストがすぐに深
方が読みやすい。
mapfor-eachでは、手続きとリストを区別するために、省略しない
閉じ括弧を優雅に見せようとして、
(define (any pred lst) (cond ((null? args) #f) ((pair? args) (or (pred (car lst)) (any pred (cdr lst)))) ) )
います。(なのでemacsマクロが必須だとか。
と書く人[Gassanenko,2001]もいるそうですが、論理的に矛盾して
るのがベストと思っています。
視覚的情報は括弧に頼らず、インデントの深さで判断するようにす
縦モードと横モード
横モード
普通は横につなげて書きます。
(map * '(1 2 3) (iota 3 2 0.1))
縦モード
括弧の中身が長くなったときは、括弧の中身を縦につなげます。
(map * '(1 2 3) (iota 3 2 0.1))
縦モードと横モード (続き)
横モード → 縦モードへの移行は可ですが、それ以外は不可です。
例(let1 ht (make-hash-table) body ...)
(if (not (is-a? language 'lisp)) (error "Please speak it in Lisp!.") (eval input env))
;XXX(http-post http-client server port path `((id ,(sanitize (get-config 'user-id)) '((timeout 2000) (follow-redirect #f))
の多いS式は、最初から縦モードで書くのがおすすめです。
平行四辺形コードは、上から下へ流れるように読めるので、ネスト
例外
に並べたほうが見やすいです。
:keyword value の組や、arc のように括弧が書略されている時は横
深さインデントと、揃えインデント
深さインデントは、純粋に括弧の深さでスペースの量を決めます。
(define (is-a? obj type) (eq? (car obj) type))
(defun is-a? (obj type) (eqp (car obj) type))))
わせる方式です
揃えインデントは、第一引数の位置に、それ以後の引数の位置を合
(list (apply average lis) (apply min lis) (apply max lis))
さインデントが好みです。
するとコードがすぐに右へ飛んでいってしまうので、個人的には深
揃えインデントの方をCanonicalとみなす人が多いようですが、多用
。
letの束縛部、and, or, list, 四則演算に限って使うことにしています
cond, caseの書きかた
条件部を見やすく揃えるのが大事です。
(cond ((pred a) ...) ((pred b) ...)(cond ((pred a) ... ... ) ((pred b) ... ... ))
どちらかに統一するといい感じです。
空行
に空行を入れるのはありだと思います。
にのみ使います。ただし、「手続き型」の関数で、コメント行の前
基本的には、トップレベル定義の区切り、internal defineの区切り
(define (test) (define (test-aux-1) ... )
(define (test-aux-2) ... )
body ... )