haskell

Haskellで複雑度測定

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/binhomplexity-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でファイル保存のトリガーで実行してくれる方が良いのかな。そこで警告などが出てくれればソースコードを見直そうという気になるかもしれません。

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です