はじめに

Data.MonoidFirst 型が定義されています。

First モノイドは EndoDual モノイドとは定義の雰囲気が少し違いますね。

  • getFirst :: a ではなく getFirst :: Maybe a となっている
  • インスタンス宣言にクラス制約が必要無い

使い方は簡単。

常に最初の値を返すという、一見意味の無さそうな First モノイドも実は便利に使えたりします。

Semigroup law の確認

Case (1) (a == Nothing, b == Nothing)

Case (2) (a == Nothing, b == Just b’)

Case (3) (a == Just a’)

Monoid Law

Case (1) (a = Nothing)

Case (2) (a = Just a’)

なぜ getFirst :: a にしないのか?

普通は他のモノイド同様に以下のように定義したくなりますよね。

ではなぜこのような定義にしないのでしょうか?

答えは、Monoid law を満たさないからです。実際に確認してみましょう。

このように成り立ちません。ちなみに Semigroup law は満たします。

つまり、Semigroup からモノイドにするために Maybe でラップしてあると考えて差し支えないと思います。

具体例: コマンドラインパーサー

僕が First モノイドの存在を認識したのは stack のコードで ConfigMonoid というデータ型があり、その設定値に First Bool などの型が使われているという場面でした。

コード

optparse-applicative を使った具体的なサンプルはこんな感じです。(Last モノイドは名前の通りです)

実行結果

実行すると何が起きているかわかります。(手動で改行等を入れて見やすくしています)

# オプション無しで実行
λ> stack run ex2
PartialOptions
  { poInputPath  = First { getFirst = Nothing }
  , poOutputPath = Last  { getLast  = Nothing }
  }

# どちらもオプションを1つずつ指定して実行
λ> stack run ex2 -- -i aaa -o aaa
PartialOptions
  { poInputPath  = First { getFirst = Just "aaa" }
  , poOutputPath = Last  { getLast  = Just "aaa" }
  }

# -i のオプションのみ2つ指定
λ> stack run ex2 -- -i aaa -i bbb -o aaa
PartialOptions
  { poInputPath  = First { getFirst = Just "aaa" }
  , poOutputPath = Last  { getLast  = Just "aaa" }
  }

# -o のオプションのみ2つ指定
λ> stack run ex2 -- -i aaa -o aaa -o bbb
PartialOptions
  { poInputPath  = First { getFirst = Just "aaa" }
  , poOutputPath = Last  { getLast  = Just "bbb" }
  }

# -i, -o のオプションどちらも2つ指定
λ> stack run ex2 -- -i aaa -i bbb -o aaa -o bbb
PartialOptions
  { poInputPath  = First { getFirst = Just "aaa" }
  , poOutputPath = Last  { getLast  = Just "bbb" }
  }

こんな感じで First モノイドや Last モノイドを使ってオプションが複数指定された場合に最初の値か最後の値かを選ぶことができます。

参考