kyotolisp#1 LT3 美しいLispの書き方 (1)

Preview:

DESCRIPTION

kyotolisp#1 (kyotolisp.github.com) で発表した内容です。スライドは(2)に続きます。

Citation preview

美しい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 ... )

Recommended