Mac/emacsでHaskell環境を作る

最近会社でHaskell書くタスクがあがってきたので環境を作っていた。
最近sublimtext2ばかり使っていたのだが、haskellはかなり文法がかなり厳密な言語なのでflymake(シンタックスチェッカ)がないと辛い

Haskellのインストール

最近のMac(Lion)は標準だとllvmでないgccは持ってないので以下のリンクからDLしてくる
https://github.com/kennethreitz/osx-gcc-installer
LLVMに対応していないので、Macデフォルトのgcc(llvm-gcc)は使えない

Haskellのバイナリをもらってくる
http://hackage.haskell.org/platform/
Homebrewにもあるが、自前のビルドはかなり時間がかかる

Haskellのパッケージマネージャのcabalは /Developer/usr/bin/gcc を見ようとするのだが、Lion以降のMacは/Developer廃止されてるので、適当にaliasだけ作っておく

$ sudo mkdir -p /Developer/usr/bin
$ sudo ln -s /usr/bin/gcc-4.2 /Developer/usr/bin/gcc


cabal で hlint いれておく。よりよいメソッドの提案などをしてくれる。

$ cabal install happy
$ cabal install haskell-src-exts
$ cabal install hlint

hlint かなり賢いので困ったら頼るといい

Haskell-mode

最近はすべてel-getで管理するようにしている。
dimitri/el-get https://github.com/dimitri/el-get

(あとでel-getについてもまとめた記事を書きたい)

とりあえず実行してみる

ghciでREPL起動

$ ghci
GHCi, version 7.0.4: http://www.haskell.org/ghc/  :? for help
Loading package ghc-prim ... linking ... done.
Loading package integer-gmp ... linking ... done.
Loading package base ... linking ... done.
Loading package ffi-1.0 ... linking ... done.
Prelude> print $ 3 * 7
21
Prelude>

Hello.hs

putStrLn "Hello, Haskell"

runhaskellで実行

$ runhaskell Hello
Hello, Haskell

emacs

主にflymakeの設定

(el-get-install "haskell") ;call once
(load "~/.emacs.d/el-get/haskell-mode/haskell-site-file")

(require 'flymake)
(global-set-key (kbd "M-n") 'flymake-goto-next-error)
(global-set-key (kbd "M-N") 'flymake-goto-prev-error)

(defun flymake-Haskell-init ()
  (flymake-simple-make-init-impl
   'flymake-create-temp-with-folder-structure nil nil
   (file-name-nondirectory buffer-file-name)
   'flymake-get-Haskell-cmdline))
(defun flymake-get-Haskell-cmdline (source base-dir)
  (list "ghc"
        (list "--make" "-fbyte-code"
              (concat "-i"base-dir)
              source)))
(defvar multiline-flymake-mode nil)
(defvar flymake-split-output-multiline nil)
(defadvice flymake-split-output
  (around flymake-split-output-multiline activate protect)
  (if multiline-flymake-mode
      (let ((flymake-split-output-multiline t))
        ad-do-it)
    ad-do-it))
(defadvice flymake-split-string
  (before flymake-split-string-multiline activate)
  (when flymake-split-output-multiline
    (ad-set-arg 1 "^\\s *$")))
(add-hook
 'haskell-mode-hook
 '(lambda ()
    (add-to-list 'flymake-allowed-file-name-masks
                 '("\\.l?hs$" flymake-Haskell-init flymake-simple-java-cleanup))
    (add-to-list 'flymake-err-line-patterns
                 '("^\\(.+\\.l?hs\\):\\([0-9]+\\):\\([0-9]+\\):\\(\\(?:.\\|\\W\\)+\\)"
                   1 2 3 4))
    (set (make-local-variable 'multiline-flymake-mode) t)
    (if (not (null buffer-file-name)) (flymake-mode))
    ))

わざとシンタックスエラーを起こしてflymake-goto-next-errorするとこうなる

これでストレスなくなりました