これはHaskell Advent Calendar 2023の19番目の記事です。
フォーマッター
以前、フォーマッターをいくつか試しましたが、どれもイマイチでした。しかし、fourmoluはいけてます。fourmoluは、Ormoluのフォークで、Ormoluが偉大なのでしょう。両方試しましたが、僕はformoluに決めました。
Hackageに上がっているので好きな方法でインストールしてください。
% cabal install fourmolu
formoluにHaskellのプログラムを渡すと、整形したプログラムを出力してくれます。ファイルの内容を直接書き換えたいときは、-i
オプションを渡します。エディタやIDEと連動できますが、お試しでプロジェクト全体を整形するには、以下のようにするといいでしょう。
% find . -name "*.hs" | xargs fourmolu -i
整形が気に入らない部分は、{- FOURMOLU_DISABLE -}
と{- FOURMOLU_ENABLE -}
で囲んで、手で修正してください。
CPPで #ifdef
している部分は、書き方によって、整形できたり、できなかったりします(ファイル全体の整形を諦める)。関数の一部ではなく、冗長になるかもしれませんが関数の全体を#ifdef
で囲むように変形すると、きっとうまく整形できるでしょう。
整形の方法をfourmolu.yaml
でカスタマイズできます。fourmoluは、起動時にルートディレクトリに向かって、このファイルを探します。設定の各種項目を試せるWebサイトがあるので、そこで遊んでみましょう。僕が使っている設定ファイルは、ここを見てください。
コールグラフ
いつの間にか巨大になってしまったモジュールを分割するとき、コールグラフがあれば、呼び出し関係の薄い部分で分割できると判断できます。認知度が低いのですが、イケてるコールグラフ作成ツールが、calligraphyです。
% cabal install calligraphy
GHCが生成する IDE Infoからコールグラフを作成します。ほーら、イケてる気がするでしょう?使い方は、こんな感じです。
% cabal build --ghc-options=-fwrite-ide-info % calligraphy Network.TLS.Handshake.Client --output-png out.png
この場合のout.pngは、こんな感じです。
オプションがたくさんあるので、いろいろ試してみてください。IDE Infoを使うので、calligraphyをビルドしたGHCのバージョンと、プロジェクトに使う GHC のバージョンは一致させる必要があります。
シリアライザー
Haskellの data
をシリアライズするには、歴史的に binary や cerial が使われてきました。これらのライブラリでは、定義したdata
に対するシリアライザーやデシリアライザーを手書きする必要があります。
最近のライブラリでは、シリアライザーやデシリアライザーを自動生成できます。その一つであるserialiseの使い方を紹介します。
% cabal install serialise
基本型は、デフォルトで扱えるようになっています。
% ghci ghci> import Codec.Serialise ghci> serialise (1 :: Int) "\SOH"
シリアライズできました。では、デシリアライズを試してみましょう。
ghci> deserialise $ serialise (1 :: Int) *** Exception: DeserialiseFailure 0 "expected null"
作成されたバイナリには、型情報がないんですね。(出力が1バイトの時点で気づけって?) 型を補ってみましょう。
ghci> deserialise $ serialise (1 :: Int) :: Int 1
ヤッホー! では、自前の型を作ってみます。シリアライザを自動生成するには、DeriveGeneric 言語拡張が必要です。
ghci> :set -XDeriveGeneric ghci> import GHC.Generics ghci> data Tree = Leaf | Node Int Tree Tree deriving (Generic)
そして、以下の魔法を唱えると、シリアライザ&デシリアライザが自動生成されます。
ghci> instance Serialise Tree ghci> serialise Leaf "\129\NUL" ghci> serialise $ Node 1 Leaf Leaf "\132\SOH\SOH\129\NUL\129\NUL"
やったね!
軽量スレッドのスタック
軽量スレッドが生成されたとき、スタックの最初の大きさは 1KiB です。スタックが消費されたら自動的に伸びますが、伸びる大きさは 32KiBです。つまり、1KiBの次は33KiBになってしいます。なので、たくさん軽量スレッドを使うと、使用するメモリの大半をスタックが占めるようになります。
実際は、スタックの大きさは2KiBでも事足りているかもしれません。伸びたスタックが縮むことはありません。スタックの伸びる大きさを制御する RTS オプションが -kc
です。指定できる最小値は、2KiB(-kc2k
)のようです。
この事実を知ってから、僕が作っているサーバの cabal ファイルには、以下の行を入れるようになりました。(他の RTS オプションの意味は、各自で調べてください。)
ghc-options: -Wall -threaded -rtsopts "-with-rtsopts=-qn1 -A32m -kc2k"