xmonad-oid on emacs & more functional emacs lisp | 関数型lt大会

30
XMonad-oid on Emacs & More functional Emacs Lisp 岡岡 岡 (keno)

Upload: takeshi-okada

Post on 27-Jun-2015

887 views

Category:

Technology


1 download

DESCRIPTION

関数型LT大会 @クックパッド, 東京 2014/05/11 の発表スライドです.

TRANSCRIPT

Page 1: XMonad-oid on Emacs & More functional Emacs Lisp | 関数型LT大会

XMonad-oid on Emacs &More functional Emacs

Lisp岡田 健 (keno)

Page 2: XMonad-oid on Emacs & More functional Emacs Lisp | 関数型LT大会

自己紹介

岡田 健 (keno, Twitter: @keno_ss)

京都大学 数理解析研究所

Lisper, Schemer

最近は Haskell に興味がある ( 質問していいですか )

けど実際は Emacs Lisp ばっかり書いてる

Emacs Lisp って不便な言語ですよね

Page 3: XMonad-oid on Emacs & More functional Emacs Lisp | 関数型LT大会

作ったもの

個人的テーマ「 ( 最小の性能の犠牲で ) 読みやすく書きやすくデバッグしやすい Emacs Lisp の環境を整備しよう」

ewm.el (EmacsWMonad; XMonod-oid on Emacs)

debug-print.el (Gauche’s nice printf-debugging)

ERFI (SRFI in Emacs Lisp)

hairs.el (Haskell influenced record syntax)

次は R7RS の define-library 的な仕組みが欲しい ( 名前空間がないので )

Page 4: XMonad-oid on Emacs & More functional Emacs Lisp | 関数型LT大会

今日の話

XMonad-oid on Emacs

ewm.el

More functional Emacs Lisp

ERFI の紹介

hairs.el の紹介

Page 5: XMonad-oid on Emacs & More functional Emacs Lisp | 関数型LT大会

XMonad-oid on Emacs

Page 6: XMonad-oid on Emacs & More functional Emacs Lisp | 関数型LT大会

そもそもこれがやりたくて( 作り直したくて )

色々整備してる

Page 7: XMonad-oid on Emacs & More functional Emacs Lisp | 関数型LT大会

今日は時間ないので無理興味ある人は個人的に

Page 8: XMonad-oid on Emacs & More functional Emacs Lisp | 関数型LT大会

More functional Emacs Lisp

Page 9: XMonad-oid on Emacs & More functional Emacs Lisp | 関数型LT大会

Lisp って関数型なの ?

Lisp

No (by “Let Over Lambda”).

Scheme

Maybe yes.

参照透明性は言語では保証されないけど書き方としては推奨されている

Common Lisp

知らない

CLer も不満らしい ? ( 深町英太郎「誰向けかわからない Common Lispでの関数型プログラミング入門とその未来」 )

Page 10: XMonad-oid on Emacs & More functional Emacs Lisp | 関数型LT大会

Emacs Lisp は関数型 ?

Definitely no.

dynamic binding (not lexical binding)

let 内で lambda 使うときは注意しないとデバッグできないバグが

Emacs Lisp は CL 寄りな Lisp ( でも「 cl.el 使うな」って言われる )

末尾呼び出しの最適化がない (CL 処理系の多くでは使えるけど )

ループは while か CL 系の loop とか do-times とか

函数が破壊的版しかないことが多い

append-map! (mapcon) はあるけど append-map がない

Page 11: XMonad-oid on Emacs & More functional Emacs Lisp | 関数型LT大会

けど最近は (emacs 24.3)

lexical-binding や closure が使えるように ! (>= 24)

cl.el が cl-lib.el になって ( 一応 ) 使ってもいいことになった

けど cl.el でできたことが一部できなかったり面倒だったり

gv.el (generalized variables; 汎変数 ) の仕組み ( 高階函数 )

pcase.el (ML-style pattern-matching macro for Elisp)

でもまだ足りない . 動き始めたばかり .

Page 12: XMonad-oid on Emacs & More functional Emacs Lisp | 関数型LT大会

ライブラリ書いた( 書いてる )

Page 13: XMonad-oid on Emacs & More functional Emacs Lisp | 関数型LT大会

ERFI

えるふぃ ( ポケモンではない )

Scheme の標準的なライブラリ SRFI(+Gauche の拡張 ) の Emacs版 ( にできたらいいな ) とか既存のイディオム ( 覚えられんし読めん )の改善とか

SRFI 1: リストライブラリ

SRFI 2 26 61 67: and-let* cut cute cond case ecase

SRFI 5: 名前付き let, 末尾再帰の最適化

「 Elisp は CL じゃねぇ ! 」と言ってるしこういうのあってもいいかなと

Page 14: XMonad-oid on Emacs & More functional Emacs Lisp | 関数型LT大会

named let

普通の変数束縛 + 名前付き函数の束縛

Page 15: XMonad-oid on Emacs & More functional Emacs Lisp | 関数型LT大会

(defun fact-slow (n) ; 非末尾再帰 (if (zerop n) 1 (* n (fact-slow (- n 1)))))

(defun fact/tco (n) ; 末尾再帰 (fact-aux n 1))(defun fact-aux (n r) (if (zerop n) r (fact-aux (- n 1) (* r n))))

(defun fact (n) ; 名前付き let (let iter (n r) (if (zerop n) r (iter (- n 1) (* r n))))

Page 16: XMonad-oid on Emacs & More functional Emacs Lisp | 関数型LT大会

末尾再帰の最適化

「 Emacs Lisp に末尾再帰の最適化が欲しい」という話は少なくとも 2004年に GNU のメーリングリストに流れてる . (Oliver Scholz, “tail recursion hack in Emacs Lisp?”)

2013 年までは nlet.el というのもあった . ( 今は消えてる )

「末尾再帰最適化は理論上は実装可能 (unwind しちゃまずいケースを除いて ) です。が、末尾再帰最適化が行なわれることを想定したコードを古いEmacs で実行すると悲惨なことになるので、結局のところ永遠に実装されないと思います。」 ( わからん , Emacs Lisp で末尾再帰 , コメント欄 )

個人的な意見 : 古い Emacs のことを考えて読めないプログラムを書きたくないし読みたくない .

Page 17: XMonad-oid on Emacs & More functional Emacs Lisp | 関数型LT大会

Scholz 版 と nlet.el

コンパイル時にマクロで再帰呼び出しを while ループに書き換える

だがバグがあり使い物にならない

引数の評価の順序に依存 (nlet.el)

制限も強い

末尾再帰の形でなかったり progn を混ぜるとバグる

本当に扱いたいのは末尾再帰の場合だが書いてるときは色々試したい

funcall とか mapcar できない

実は変換後のループが函数呼び出ししていて効率が悪い

Page 18: XMonad-oid on Emacs & More functional Emacs Lisp | 関数型LT大会

erfi:let

インターフェースは SRFI 5 (named let)

R5RS から末尾文脈の概念

実装は Scholz 版を参考に

nlet.el も bug は取れたけど生成されるコードの質が良くなかった

Page 19: XMonad-oid on Emacs & More functional Emacs Lisp | 関数型LT大会

(defun fact (n) (erfi:let iter ((n 5) (r 1)) (if (zerop n) r (iter (1- n) (* r n)))))

(defun fact (n) (let ((--erfi-continue-- t) (--erfi-result-- nil) (G4201 n) (G4202 1) (n nil) (r nil)) (while --erfi-continue-- (setq n G4201) (setq r G4202) (catch '--erfi-repeat-- (setq --erfi-result-- (if (zerop n) r (progn (setq G4201 (1- n)) (setq G4202 (* r n)) (throw '--erfi-repeat-- nil)))) (setq --erfi-continue-- nil))) --erfi-result--)

展開

変数適用順序に非依存

Page 20: XMonad-oid on Emacs & More functional Emacs Lisp | 関数型LT大会

入れ子の例 : リストの等価性

(defun erfi:list= (elt= &rest xss) (if (let1 len (length (car xss)) (not (erfi:every1 (lambda (xs) (eq len (length xs))) (cdr xss)))) nil (erfi:let outer-iter ((xss xss)) (if (null (cdr xss)) t (erfi:let inner-iter ((xs (car xss)) (ys (cadr xss))) (if (null xs) (outer-iter (cdr xss)) (and (funcall elt= (car xs) (car ys)) (inner-iter (cdr xs) (cdr ys)))))))))

流石に展開したのは載せられないので家に帰って macroexpand してね

生で while ループで書きたくないものの一つ

Page 21: XMonad-oid on Emacs & More functional Emacs Lisp | 関数型LT大会

Haskell の話

Page 22: XMonad-oid on Emacs & More functional Emacs Lisp | 関数型LT大会

Haskell って良い言語ですよね

((圏論好きなので )Haskell の方に行ってみたい )

(( でも ) (括弧 ( ない ( と )) 不安 ))

Lisp はどんどん Haskell とかの良い点を取り込むべき

Gauche も $ をマクロとして取り込んだりしてる

括弧があるからこそ簡単に可能な Lisp の強みのマクロ !!

( とか言ったら Haskeller からマサカリ飛んできそう )

Page 23: XMonad-oid on Emacs & More functional Emacs Lisp | 関数型LT大会

レコード構文data Person = Person { firstName :: String , lastName :: String , age :: Int , phoneNumber :: String } deriving (Show)data Book = Book { author :: Person , title :: String , price :: Int } derivin (Show)

let author = Person "Saunders" "MacLane" 30 "unknown" let book = Book author "Categories for the Working Mathematician" 8500 book { author : author book { lastName : "Mac Lane" , age : 95 , phoneNumber : "unknown" } , price : 3000 }コピーして返す (非破壊的 )但しいくつかは新しい値に

再帰的に使える !

Page 24: XMonad-oid on Emacs & More functional Emacs Lisp | 関数型LT大会

Emacs Lisp にもレコード構文を

XMonad-oid on Emacs に使いたい ( 使ってる )

evm-util.el (2013 年 )

hairs.el (2014 年 )

Page 25: XMonad-oid on Emacs & More functional Emacs Lisp | 関数型LT大会

ewm-util.el

cl.el の構造体を再帰的に非破壊的にコピー

構造体の ( 型 )情報を動的に取得している

cl.el は getter は作るけど setter は作らない

setf ( 汎変数の仕組み ) を使わざるを得ない

なので函数として実装 (遅い )

Page 26: XMonad-oid on Emacs & More functional Emacs Lisp | 関数型LT大会

hairs.el(Haskell influenced record syntax)

動的型付け言語の Lisp だけどこういう部分では静的にやった方が良い

型情報を与えてやれば静的にできる

マクロとして DSL を実装

コンパイルで効率の良いコードを生成

でもいちいち指定するのは面倒

型推論 ( というほどのものではないけど )

alist, plist, hash-table, array, struct, eieio (CLOS) に対応

Page 27: XMonad-oid on Emacs & More functional Emacs Lisp | 関数型LT大会

(hairs-copy '((:hoge . hoge) (:fuga . fuga) (:foo . foo) (:bar . (:john "Smith" :jane "Smith"))) (with :alist :hoge 'hogehoge :foo => (lambda (x) (list x x)) :bar (with :plist :john "Hi" :jane "Hello")))

'((:hoge . hogehoge) (:fuga . fuga) (:foo . (foo foo)) (:bar . (:john "Hi" :jane "Hello")))

固定値型情報

古い値から新しい値を作る 再帰的

Page 28: XMonad-oid on Emacs & More functional Emacs Lisp | 関数型LT大会

(cl-defstruct hairs:person name age sex)(cl-defstruct hairs:group name description member)(hairs-register-type '((:struct hairs:group) member) '(:alist (:struct hairs:person)))

(with (:struct hairs:group) :member (with :alist b (with (:struct hairs:person) :name "Neo")))

(with (:struct hairs:group) :member (w/ b (w/ :name "Neo")))

Page 29: XMonad-oid on Emacs & More functional Emacs Lisp | 関数型LT大会

Any questions or comments?

Twitter: @keno_ss

GitHub: https://github.com/kenoss

まだコード上げてない ( 近日中に )

コメント , 要望 , pull-req歓迎

Page 30: XMonad-oid on Emacs & More functional Emacs Lisp | 関数型LT大会

ありがとうございました

Twitter: @keno_ss

GitHub: https://github.com/kenoss

まだコード上げてない ( 近日中に )

コメント , 要望 , pull-req歓迎