はじめに
最近、hylangを早いところ流行らせてこれで仕事しても怒られない雰囲気を作りたいと思っています。
altjsならぬaltpythonくらいのノリでhylangが受け入れられてほしいものです。
さて、今回は言語仕様がどうだとかいう話ではなく、hylangのpythonに対する単純なアドバンテージの話をしたいと思います。
というのも、
hylangはpythonにくらべて編集しやすいです。
カッコがあるからマクロが書けるんだ!同図像性が!みたいな話をするより、わかりやすいと思います。ただ単に編集しやすいんです。
「またうるさいlisperが同図像性の話ししてる。。。」ってなった人は帰ってもらって結構です。
さて、編集しやすい理由は簡単です。コードに一定の規則でカッコ(構造)があるからエディタがコードをパースして入力をアシストできるからです。
どういうことか、実例で見ていきましょう。
先に比較環境の話
これからpythonとhylangの比較をしていきますが、その前に比較環境を明らかにしていきます。
- どちらもemacs
- python: python-mode + elpy
- hylang: hy-mode + paredit-mode (+ smartparens)
あとこの記事は半分くらい冗談なので、hylangばっかりガチガチに準備しているという有利な設定があります。
というのも、paredit-mode-mapによく使う以下のキーバインドを仕掛けちゃいます。
(require 'smartparens)
(require 'paredit)
(define-key paredit-mode-map "\C-t" 'transpose-sexps)
(define-key paredit-mode-map "\M-t" 'reverse-transpose-sexps)
(define-key paredit-mode-map "\C-k" 'kill-my-sexp)
(define-key paredit-mode-map "\M-k" 'paredit-kill)
(define-key paredit-mode-map "\M-f" 'sp-next-sexp)
(define-key paredit-mode-map "\M-b" 'sp-backward-sexp)
(define-key paredit-mode-map "\C-h" 'sp-down-sexp)
(define-key paredit-mode-map "\C-u" 'sp-up-sexp)
(define-key paredit-mode-map "\C-w" 'sp-copy-sexp)
(define-key paredit-mode-map (kbd "C-S-w") 'kill-region)
(define-key paredit-mode-map (kbd "C-,") 'sp-clone-sexp)
(define-key paredit-mode-map "\M-d" 'paredit-forward-down)
(define-key paredit-mode-map "\M-u" 'paredit-forward-up)
(define-key paredit-mode-map "\M-c" 'paredit-convolute-sexp)
(define-key paredit-mode-map "\C-o" 'avy-goto-sexp-begin)
(define-key paredit-mode-map (kbd "C-S-o") 'avy-goto-sexp-end)
(defun reverse-transpose-sexps (arg)
(interactive "*p")
(transpose-sexps (- arg))
(backward-sexp arg)
(forward-sexp 1))
(defun kill-my-sexp ()
(interactive "*")
(if (zerop (length (buffer-substring (point-at-bol) (point-at-eol))))
(kill-line)
(kill-sexp)))
(require 'avy)
(setf avy-background nil)
(defun avy-goto-sexp-begin ()
(interactive "*")
(let ((avy-all-windows nil))
(avy-with avy-goto-char
(avy--process
(avy--regex-candidates
(regexp-quote "("))
(avy--style-fn avy-style)))))
(defun avy-goto-sexp-end ()
(interactive "*")
(let ((avy-all-windows nil))
(avy-with avy-goto-char
(avy--process
(avy--regex-candidates
(regexp-quote ")"))
(avy--style-fn avy-style)))
(forward-char)))
二手詰め
fibonacci数列を得られる関数の条件と処理の対応が逆になっています。直しましょう。
ミスタイプしないようにビクビクしながらやってるので、速度は遅いですが、手数を見てください。
まずはpython。C-rが入っちゃってますが、録画のためだけのhot-keyです。無視してください。
修正が一行単位だったためkill-lineでことなきを得ました。複数行だったら範囲選択が必要だったでしょう。
- 閉じカッコへ移動(avy-goto-sexp-end)
- 今のS式と次のS式を入れ替える(transpose-sexps)
で二手です。
こちらは複数行でしたが、S式単位の編集の前に障害になりえません。
操作対象がS式である場合はかならず"("か")"の場所になるので、avyのhead-charは決め打ちにできます。
avyは超多用するためbackground暗くするやつはoffにします。そうでないと画面がチカチカして失神しそうになります。
七手詰め
withを使ってないコードがありました。直しましょう。
pythonから
インデントブロックでは、ブロックが上に一つ増えるとき、いちいち該当する行に移動して面倒をみないといけません。
- 移動
- setvをwithに書き直す
- ()挿入
- 右飲み込み(paredit-slurp-forward-sexp)
- 右飲み込み(paredit-slurp-forward-sexp)
- S式から出る(sp-up-sexp)
- 右飲み込み(paredit-slurp-forward-sexp)
で七手です。
実例: fizzbuzz
スクラッチからfizzbuzzを書いてみましょう。しかし、条件の順番を間違えたのに気づいて最後に直すというシチュエーションでやります。
pythonから。 気が動転して関数名がfibになっていますが、気にしないでください。
他人がミスタイプを恐れながらダラダラとfizzbuzzを実装するのを見るのは辛いでしょうが、頑張ってください。
if i % ~ == 0:
print(~)
というパターンを使いまわしたいのですが、構造がないためいちいち範囲選択する必要があります。
書き下すときもあとで順番を直すときも大変です。
ちょっと間違えたりしてますが、許してください。三度目の撮り直しは心が折れます。
condの中に入ってからが見どころです。sp-clone-sexpによって範囲選択いらずでS式を複製します。
更に、分岐の順番を直すのも、今のS式と前のS式を入れ替える(reverse-transpose-sexps)を二発で完了です。
まとめ
今回は構造的編集がある場合と無い場合の実例を実際にやってみせたくて記事にしました。
実際にはこれ以外にもたくさんコマンドがあるので、
この素晴らしい紹介
などを見てください。なんならpreditやsmartparensのコマンドは全部一回使ってみた方がいいです。
いやほんとカッコがきもいとか言う前に三時間くらいpareditとかそのあたりを使って編集してみてくれっていつも思います。
カッコがあらゆる場所にあるからアクセスしやすいのです。カッコはなくてはならない空気みたいなものです。
あと、lisperの方の中にもpareditはカッコのバランスとってくれるくらいしか使ってないなーって人も多いと思います。
それだとせっかくのカッコがもったいないので、ガンガン便利なコマンド仕掛けて使ってみてください。
lisp知らない人でもhylangのforとかpythonそのままなので、hylangはpythonと同じロジックでだいたい書けます。
なら、編集しやすい方とそうでない方どちらを選ぶんだという簡単な問題です。
readable codeもいいけどeditable codeっていうのも、楽をしたいプログラマなんだったら大事にしてみませんか。
hylang、腱鞘炎の方にもおすすめです。