Category Archives: Haskell

haskell-ide-engine を使ってみて

ここ数日 haskell-ide-engine(HIE) 試してみてます。これは、Haskell による開発をサポートするさまざなツールを統合して Language Server Protocol によって扱えるようにしようというものです。

まず、私の場合 Windows 上では GHC 8.2.2 でのビルドしか成功できてません。GHC 自体なにかと Windows では切ないのでこれはちょっと諦めています。

HIE を使うための Visual Studio Code の機能拡張「Haskell Language Server」のページをみると、GHC 8.0.2 にしか対応していないようなことが書いてありますが、これは HIE が ghc-mod を使用しているからであり、ghc-mod が GHC 8.0.2 までしか対応していないからだと思います。つまり、Visual Studio Code に限らず HIE を使うならば GHC 8.0.2 までということになると思います。

コードフォーマットは、Visual Studio Code 上ではちょっと動きが怪しかったです。なぜかフォーマットするたびに末尾に空行が1行ずつ追加されていきました。ちなみに HIE のコードフォーマット機能には brittany が使われているようです。

text-icuをWindowsで使う

いきさつ

  • Windows 上の Visual Studio Code で Haskell の開発環境を整えようとする。
  • haskell-ide-engine を Visual Studio Code から使う機能拡張が GHC 8.0.2 にしか対応していない(この記事の執筆時点での最新版は GHC 8.2.2)
  • haskell-ide-engine を GHC 8.0.2 の設定でビルドしてみたら text-icu のコンパイルでコケた(以下のようなエラーが出た)
 libraries:
 * Missing C libraries: icuuc, icuin, icudt
 This problem can usually be solved by installing the system packages that
 provide these libraries (you may need the "-dev" versions). If the libraries
 are already installed but in a non-standard location then you can use the
 flags --extra-include-dirs= and --extra-lib-dirs= to specify where they are.

原因

設定されている include パスおよびライブラリパス上に ICU 関連のライブラリがない。

解決方法

まず ICU のダウンロードページから ICU4C 49.1.2(icu4c-49_1_2-Win64-msvc10.zip)をダウンロードする。text-icu のドキュメントによれば ICU 4.0 より新しければいいようだが、あまり新しくても text-icu のコンパイルだけは通るが、haskell-ide-engine のビルドが途中でエラーになるのでこのバージョンにした。

ICU4C のインストールは解凍して適当なフォルダに置くだけ。その置き場を C:\sr\config.yaml に記述する。

extra-include-dirs:
  - /path/to/your/icu/include

extra-lib-dirs:
  - /path/to/your/icu/lib64

これでとりあえず ICU がらみのエラーはでなくなったが……haskell-ide-engine 自体のビルドは未だ成功していないのであった。

cabal-devとghcmod.vim

[追記] Cabal 1.18 で sandobox という機能が入ったため、cabal-dev はその役目を終えたそうです。トホホ…

VimでHaskellプログラミングをする際、ghcmod.vimには大変お世話になっています。

ただ、cabal-devを使っている場合、なんか上手く動かないな、どうしたら上手く動くように設定できるのだろうなとずっと悩んでいました。その対処法がだいたい見えてきたので紹介します。

localrcとの合わせ技です。

まず、そもそもghc-modをコマンドラインで使うにはどうすればいいかです。かりにhello.hsのチェックをするとすれば以下のようになります。

ghc-mod check hello.hs

これはhello.hs内でcabal-devでinstallしたモジュールを使っているとエラーがでます。なので、cabalは見ないけどcabal-devは見るパッケージのconfを指定してやります(以下はGHC 7.6.3を用い、カレントディレクトリがプロジェクトルートの場合)。

ghc-mod check -g -package-conf=cabal-dev/packages-7.6.3.conf hello.hs

このコマンドラインで追加したオプションは、ghcmod.vimにおいてはghcmod_ghc_optionsで指定できます。ただ、Vimを開いた時にカレントディレクトリがどこになっているのかわかりませんので、できればパッケージのconfは絶対パスで指定したいものです。そこで、開いたHaskellソース・ファイルのパスからルートに向かって順次「cabal-dev/packages-7.6.3.conf」が存在するかを見ていき、存在した場合、フルパスを取得してオプションに引き渡すというやりかたにします。そのための関数が以下です(初めて書いたVimスクリプトなので不格好かもしれませんが)。これを.vimrcに記述します。GHCやcabal-devのバージョンが変わると「cabal-dev/packages-7.6.3.conf」というディレクトリ名が変わってしまうかもしれないので引数でもらうようにしています。

" ghcmod.vimをcabal-dev環境下で使うための設定関数
function! g:add_cabal_dev_conf(cabal_dev_conf)
  let l:kw = '%:p:h'
  let l:path = expand(l:kw)
  while l:path != ''
    if isdirectory(l:path . a:cabal_dev_conf)
      if !exists('b:ghcmod_ghc_options')
        let b:ghcmod_ghc_options = []
      endif
      call add(b:ghcmod_ghc_options, '-package-conf=' . l:path . a:cabal_dev_conf)
      break
    else
      if l:path == '/'
        break
      else
        let l:kw = l:kw . ':h'
        let l:path = expand(l:kw)
      endif
    endif
  endwhile
endfunction

次にcabal-devプロジェクトルートに.local.haskell.vimrcというファイルを以下の様な内容で作ります。これはlocalrcに読み込ませる設定ファイルです。

call g:add_cabal_dev_conf('/cabal-dev/packages-7.6.3.conf')

以上です。もっとCoolなやり方があったらぜひ教えて下さい。

Xcode 5.0 で不調になった Haskell 環境をなんとかする

iOS 7 のリリースに合わせて Xcode 5.0 がリリースになり、思わずアップデートしてしまった人はきっと私だけじゃないはず。しかし、これによって私の Haskell 環境は調子が悪くなってしまいました。特に Haskell コード中のシングルクォートの扱いがおかしいケースが目立ちました。

なお、私の Haskell 環境は Homebrew でインストールしたものです。

GHC に使わせる gcc を変更する

問題の原因は、Xcode 5.0 付属の gcc のようです。そこで、gcc のバージョン 4.7 を別途インストールします。

brew tap homebrew/versions
brew install gcc47

GHC の設定を変更するため /usr/local/Cellar/ghc/7.6.3/lib/ghc-7.6.3/settings を修正します。

2
 ("C compiler command", "/usr/bin/gcc"),

↑これを↓このように修正します。

2
 ("C compiler command", "/usr/local/bin/gcc-4.7"),

これでだいたい良さそうでしたが、これだけでは Yesod が依存している entropy のコンパイルが通りませんでした。どうも単純にパス上の gcc を見ているくさいのです。ですので、Homebrew を使っているのでしたら、/usr/bin よりも /usr/local/bin を優先してパスを通しているかと思いますので、ここにシンボリックリンクを作成してしのぎましょう。

ln -s /usr/local/bin/gcc-4.7 /usr/local/bin/gcc

ただしこれをやると、ほとんど Xcode の gcc をパス上から隠蔽することになるわけですから、その点十分に注意してください(コマンドラインで Xcode に依存するような開発をする場合など)。

それから、haskell-platform も念の為に再インストールしたほうがいいかもしれません。

brew uninstall haskell-platform
brew install haskell-platform

Yesod-Auth の HashDB を使うサンプルできたよ

日々のすきをみてドン亀のごときペースで Haskell の勉強をしているのですが、入門書片手に学ぶのも飽きてきたので、実際になんか作ってみようとしています。

そこで Haskell の Web  アプリケーションフレームワーク「Yesod」に挑戦しています。

Yesod はデフォルト状態(プロジェクトを作成直後の状態)ですでにログイン認証の仕組みが使えるようになっているのですが、この認証方式が Google の仕組みを使う一種のシングルサインオン方式になっているのです。これはこれで技術的に興味深いのですが、しかしじゃあ実際にどれぐらい使うんだというと、実案件ではそうそう使うものではないと思います。実際によく使う方式といえばやはり DB にユーザ名とパスワードを保存しておき、入力値がそれにマッチするかを見る方式が多いのではないでしょうか?

Yesod には標準で Yesod-Auth という認証の仕組みがあって、このサブパッケージによって前述の Google を使った認証方法や BrowserID を使った認証方法など様々な認証方法を提供しています。そして当然 DB を使った認証方法も提供しています。それが Yesod.Auth.HashDB パッケージ(以下 HashDB)です。

しかし、これを使うためのドキュメントでわかりやすいのがなかなか見つからないのです! 英文のリーディング自体がたどたどしい Haskell と Yesod の初学者にはイバラの道以外の何物でもないような状態だと私は感じました。しかしなんとか最低限何をすればいいのかまとめることができましたので紹介します。

なお、今回使用した Yesod のバージョンは 1.2.3.3 です。この記事に関するソースコードは Github で公開しています。diff を見てみるとどこに手を付けたのかわかりやすいかもしれません。

手を付けるべきファイルは3つ

そもそもデフォルトの状態から最低どのファイルに手をいれるべきかもなかなか定かにならなかったのですが、以下の3つが修正対象です。

  • config/models
  • Models.hs
  • Foundation.hs

config/models

まず、デフォルトでもユーザ情報を格納するためのテーブル定義を行っているのですが、HashDB で使用するには若干手直ししなければなりません。ポイントは以下の点です。

  • パスワードは必須項目でなければなりません。
  • salt カラムが必要です。

デフォルトでは password カラムに Maybe がついているのでパスワードが必須項目になっていません。また、HashDB ではパスワードは SHA-1 でハッシュ化して扱います。その際に salt の値を使います。具体的な例をあげると、パスワードが “secret”、salt が “shio” だとすると “shiosecret” をハッシュ化した値を使用するわけです。この salt を保存するカラムが必要になります。

以上を踏まえると、config/models の User 定義は以下のようになります。

1
2
3
4
5
6
User
    ident Text
    password Text
    salt Text
    UniqueUser ident
    deriving Typeable

Model.hs

config/models の定義によってモデルが User 型が定義されますが、HashDB で使用するには HashDBUser 型クラス制約を満たさなければなりません。それのための修正を Model.hs で行います。まず、HashDB を Import します。

5
import Yesod.Auth.HashDB (HashDBUser(..))

次に型インスタンスの作成です。

17
18
19
20
21
22
instance HashDBUser User where
  userPasswordHash = Just . userPassword
  userPasswordSalt = Just . userSalt
  setSaltAndPasswordHash s h p = p { userSalt = s
                                  , userPassword = h
                                  }

Foundation.hs

最後の Foundation.hs で認証方式を設定します。まず、BrowserID 認証方式と GoogleEmail 認証方式を使用しないので、以下の Import は削除してかまいません。

7
8
import Yesod.Auth.BrowserId
import Yesod.Auth.GoogleEmail

かわりに HashDB のための Import を追加します。

7
import Yesod.Auth.HashDB (authHashDB, getAuthIdHashDB)

getAuthId と authPlugins を修正します。

124
125
126
127
    getAuthId = getAuthIdHashDB AuthR (Just . UniqueUser)
 
    -- You can add other plugins like BrowserID, email or OAuth here
    authPlugins _ = [authHashDB (Just . UniqueUser)]

以上で完了です。

その他、使い方等は Github にあげている readme.md を参照してください。