Haskellでサイクロマティック複雑度(循環的複雑度)を測定する場合、2つツールがあるようです。
githubのスター数はargonの方が多いのですが、コミット履歴を見るとhomplexityの方が今もメンテナンスされているようなのでhomplexityがオススメです。機能的な違いは特に調べていません。
homplexityの機能
公式サイトによれば、以下の統計値を出してくれるようです。
- Cyclomatic complexity
- Code metric
- Lines of code
- Branching depth
- Type metric
- Comment metric
- Code to comments ratio
- Type tree nodes
- Number of function arguments
本題から外れますがBranching depthってなんでしょうか。説明には
Branching is used with conditionals and has been used as a criterion for both software complexity and logic.
と書いてあります。分岐の数のことなんでしょうか。ちょっと試して見たところ、if分岐がある場合でも0でしたが、ifの中にifをネストさせると1になり、ifの中にifの中にifとネストさせると2になりました。だから分岐の深さってことか。なんかサイクロマティック複雑度と似ていますね。
homplexityのインストール
stackが入っていれば簡単です。クローンしてstack installするだけです。
git clone https://github.com/mgajda/homplexity.git
cd homplexity
stack install
で、~/.local/bin
にhomplexity-cli
ができるはずです。パスを通すなら、~/.bashrc
や~/.bash_profile
などに
PATH=$PATH:~/.local/bin
を追記すれば良いです。
これで簡単にインストールできるのは良いのですが、このリポジトリで指定しているGHCバージョンが入っていない場合、そのインストールと依存パッケージのインストールから始めるので結構時間がかかるかもしれません。これがstackのデメリットですよね(メリットなのか?cabal hellという言葉を思い出した、そしてWhat is Cabal Hell?というStackoverflowを見つけたw)。
使い方
githubリポにも書いてありますが、以下のようにメインモジュールファイルと依存モジュールのルートディレクトリを指定します。
# プロジェクトディレクトリ構成がこのようになっている場合
root --- app --- Main.hs
|
|- src --- Lib.hs
|- ...
homplexity-cli app/Main.hs src/
これで、メインモジュールと依存モジュールファイル群を見つけて各メソッドの複雑度を測定してくれます。出力は、
Warning:src/InterfaceAdapter/Presenter/StockAPIHandler.hs:SrcLoc "src/InterfaceAdapter/Presenter/StockAPIHandler.hs" 34 1:function stockServer has 48 lines of code should be kept below 20 lines of code.
Correctly parsed 16 out of 16 input files.
こんなのが出てきます。このファイルのこのメソッドが48行もあるから20行未満に抑えた方が良いよ、と言っています。これは複雑度ではありませんが。
もっと詳細な情報を出して欲しい場合は--severity
というオプションを使います。ログレベルの設定のような感じです。homplexity -h
を見てもらえばわかりますが、
# ヘルプの説明
--severity={Debug|Info|Warning|Critical} level of output verbosity (Debug Info Warning Critical) (default: Warning, from module: Main)
とのことなのでデフォルトはWarningですがInfoにすればもっと色々出てきます。以下はInfoで解析した時の出力の一部です。
homplexity-cli --severity=Info app/Main.hs src/
Info:app/Main.hs:SrcLoc "app/Main.hs" 1 1:module Main has 14 lines of code
Info:app/Main.hs:SrcLoc "app/Main.hs" 8 1:type signature for main has type constructor nesting of 1
Info:app/Main.hs:SrcLoc "app/Main.hs" 8 1:type signature for main has 1 arguments
Info:app/Main.hs:SrcLoc "app/Main.hs" 9 1:function main has 8 lines of code
Info:app/Main.hs:SrcLoc "app/Main.hs" 9 1:function main has cyclomatic complexity of 1
Info:app/Main.hs:SrcLoc "app/Main.hs" 9 1:function main has branching depth of 0
- メソッドの行数
- コンストラクタのネスト数
- 引数の数(+1された数字が出ている?Haskellの関数には暗黙的な引数が一つあるのかな?それか単に型一つにつき1とカウントしているのかな?)
- サイクロマティック複雑度
- Branching depth
が出てきました。
これは、Makefileなどに書いておいて、毎回ビルドの時に一緒に測定してもらうなどが良さそうですね。それかIDEでファイル保存のトリガーで実行してくれる方が良いのかな。そこで警告などが出てくれればソースコードを見直そうという気になるかもしれません。