読者です 読者をやめる 読者になる 読者になる

Diary over Finite Fields

515ひかるが書き溜めたメモとコラムと雑記

気がついたら Emacs の Haskell を勉強する環境が整っていた話

エディタ Linux

どうも, 515 ひかるです.

なぜか知らないけれど, 気がついたら Emacs の環境が整っていたので何をしたのか書きます.

Haskell の開発環境

最近, なぜか『すごい Haskell たのしく学ぼう!』(オーム社) を読んでいて, 結構楽しんでいます. あまりにも三日坊主が多いので, 普段こういう話をするのは避けたいのだけど, 今回は数日間続いているので書いてもいいだろうと判断したわけです.

すごいHaskellたのしく学ぼう!

すごいHaskellたのしく学ぼう!

それとは別に, 日記をつけようと思って Emacs の Org-mode を使い始めたのだけど, これが想像していたよりもずっと使いやすくて, もう日本語の文書作成は全てコレでいいのではという気分になってきています1. そこで, この 1 週間くらい Emacs で日本語を書いて, Haskell 書く時だけ違うエディタを使うという感じになっていました.

しかし, どうしてそうなったのかよく覚えていないんですが, 気がついたら Emacs にも Haskell の環境を整備してました. 大規模な開発環境というよりも「すごい H 本」を読むのに最適な環境がいつのまにかできていた, という感じだったので初心者の僕にはすごく嬉しい2. いや, 僕が作ったはずなんだけど.

というわけで, だいたい何をしないといけないのかをメモ程度に書いておく.

覚え書き

前提

以下, StackCabal などで ghc-modhlint をインストールしていることを前提にします.

あと, この覚え書きはだいたい Haskell を快適に利用するための Emacs 環境の構築 を読みながらやりました.

futurismo.biz

haskell-mode

Emacs なのでまずはマイナーモードを入れましょう.

M-x package-install RET haskell-mode

インストールが済んだら次のように設定. 当然 ~/.emacs.d/init.el に書きます.

(autoload 'haskell-mode "haskell-mode" nil t)
(autoload 'haskell-cabal "haskell-cabal" nil t)

(add-to-list 'auto-mode-alist '("\\.hs$" . haskell-mode))
(add-to-list 'auto-mode-alist '("\\.lhs$" . literate-haskell-mode))
(add-to-list 'auto-mode-alist '("\\.cabal$" . haskell-cabal-mode))

ghc-mod

こちらも init.el に追加

(autoload 'ghc-init "ghc" nil t)
(autoload 'ghc-debug "ghc" nil t)

flycheck

エラーとか警告とかを教えてくれる.

M-x package-install RET flycheck

init.el には一行追加するだけ3.

(add-hook 'after-init-hook #'global-flycheck-mode)

より詳しくは Flycheckでモダンなシンタックス を見てください.

qiita.com

company for haskell

company-mode そのものについては, auto-completeユーザのためのcompany-modeの設定 を読んでください. ここでは手順のみ書きます.

qiita.com

インストールについては, 例のごとく

M-x package-install [RET] company
M-x package-install [RET] company-ghc

をすれば O.K. 設定は, init.el

(global-company-mode 1) ;; company-mode を常に ON
(add-to-list 'company-backends 'company-ghc)

を追加すれば動くはずです. ghc-mod の設定が必須. 詳しくは 公式リポジトリをみてください.

hooks

hook はいろいろあるけど, それぞれ目的が違うのでコメントに書いておきます.

(defun my-haskell-mode-hook ()
    (interactive)
    ;; インデント
    (turn-on-haskell-indentation)
    (turn-on-haskell-doc-mode)
    (font-lock-mode)
    (imenu-add-menubar-index)
    ;; GHCi のコマンドを設定
    (setq haskell-program-name "/usr/bin/stack ghci") ;; stack の場合
    (inf-haskell-mode)
    ;; ghc-mod を使えるように
    (ghc-init)
    ;; flycheck を起動
    (flycheck-mode))
(add-hook 'haskell-mode-hook 'my-haskell-mode-hook)
GHCi との連携

「すごい H 本」は, 読んだことある人はご存知だと思うけれど, 序盤(第 1 章 - 第 7 章)は関数定義以外はほとんどGHCi, つまり対話環境のコードがほとんど. そこで, ファイルを GHCi でロードすることが多いんだけど, 上の設定がうまくいっていれば, C-c C-l で現在のファイル(カレントバッファのファイル)を読み込んだ GHCi を起動してくれる.

こんな感じ. ちょっと見にくいけど.

f:id:hikaru515:20160912024636g:plain

左が Haskell のコードで, C-c C-l を叩くと右のバッファが出現する. その際に GHCi が起動するとき自動的に左のバッファの関数はすべて読み込んでくれるというわけ. これで関数定義と「すごい H 本」の GHCi とを行き来するのにかなり楽になった.

だいたい読んで参考にしたリンクは書いたと思うし, こんなもんかな.

実は Haskell の環境とは関係なく Evil-mode も試してみてるんだけど, これはまだ慣れてないので, もう少し慣れて設定とかできるようになったら書こうかと思います.

追記 (20160919)

\C-c\C-l で関数をロードした GHCi は起動できるけど, IO アクション, もっというと main を実行したいだろう. これは実は GHCi のプロンプト内で main を実行すればいい. わざわざコンパイルや端末で runghc する必要はなく, GHCi だけで対話型のプログラムなども動作チェックできる. 想定していたよりもかなり使える環境だった.

なんでか知らないけれどできないと思って quickrun.el とか Emacs から fish シェルを使う設定とかしてしまったよ......まぁこれはこれで違う何かに使えそうだけれども,

追記 (20161001)

上の設定でそのまま \C-c\C-l をすると, inferior-haskell-load-file が働いてカレントバッファの haskell コードをロードした状態で ghci が起動するのはよい. けど, 残念ながらカーソルはカレントバッファ(つまりソース側)のままで, ghci 側に移動してくれない. Emacs の Window 切り替えは \C-x o という超絶面倒なデフォルトキーバインドなのはご周知の通りで, 毎回 \C-c\C-l \C-x o などとやるのは鬱陶しかったのでコマンドを定義した.

(define-key haskell-mode-map
         "\C-cl" (lambda ()
                  (interactive)
                  (inferior-haskell-load-file)
                  (other-window 1)))

これで, \C-c l と入力するだけでロードとカーソル移動をやってくれる. 3 つ以上のウィンドウに分割してたら知らんよ.

Footnotes:

1
実際, この記事も Org-mode で書いている. 正確には, Org-mode で書き上げ, Org-mode の export 機能を使って Markdown 文書に変換し, リンクなどをはてなブログのエディタで追加するという工程でやっている.
2
まるで自分で作ったわけではないというような表現だが, 実際そんなに頑張った記憶もなくいつの間にかできていたので, こんな表現になっている.
3
これは別に Haskell 以外にも使えるらしいですが, 僕は現在では Haskell にしか使っていないので書きません.