Data.Monoid.First と Data.Semigroup.First あるいは Last

Data.Monoid.First のドキュメントを見ていたら次の記述を見つけたことから始まる記事です。

This type will be marked deprecated in GHC 8.8, and removed in GHC 8.10. Users are advised to use the variant from Data.Semigroup and wrap it in Maybe.

すでに GHC 8.10 がリリースされているのにまだなくなっていない気がしますが(GHC 9.0.1-alpha1 にもまだある)、Data.Monoid.First よりも Data.Semigroup.First を使うべきなようです。

追記(2020.11.01)

id:maoe さんに教えていただき、現在はこの deprecation は撤回されたそうです。 mail.haskell.org gitlab.haskell.org

以降、次のようにモジュールを参照します。

> import qualified Data.Semigroup as S
> import qualified Data.Monoid as M

復習

セミグループの復習として、文字列とその結合を見ると次のような挙動をします。

> "a" <> "bc"
"abc"
> "" <> "abc"
"abc"

モノイドの単位元"" です。

> mempty <> "abc"
"abc"

Data.Semigroup.First

さてここにセミグループのインスタンスでない型 Char があります。

> 'a' <> 'b'
<interactive>:13:1: error:
    • No instance for (Semigroup Char) arising from a use of<>’
    • In the expression: 'a' <> 'b'
      In an equation for ‘it’: it = 'a' <> 'b'

Data.Semigroup.First にくるめばセミグループになります。

> S.First 'a' <> S.First 'b'
First {getFirst = 'a'}

左が返り値となります。

Data.Monoid.First

では Data.Monoid.FirstChar をくるめるでしょうか?

> M.First 'a'
<interactive>:16:9: error:
    • Couldn't match expected type ‘Maybe a’ with actual type ‘Char’
    • In the first argument of ‘M.First’, namely ‘'a'’
      In the expression: M.First 'a'
      In an equation for ‘it’: it = M.First 'a'
    • Relevant bindings include
        it :: M.First a (bound at <interactive>:16:1)

Data.Monoid.First でくるめるのは Maybe a だけです。

> M.First $ Just 'a'
First {getFirst = Just 'a'}

Just 同士の演算は Data.Semigroup.First と同じです。

> M.First (Just 'a') <> M.First (Just 'b')
First {getFirst = Just 'a'}

Nothing がからむと Nothing でない最初の要素が返ります。

> M.First Nothing <> M.First (Just 'a') <> M.First (Just 'b')
First {getFirst = Just 'a'}

Data.Maybe

Data.Maybe はモノイドとしては次のような挙動が採用されています(雑になってきた)。

> Nothing <> Nothing
Nothing
> Nothing <> Just "a"
Just "a"
> Just "a" <> Nothing
Just "a"
> Just "a" <> Just "b"
Just "ab"

Maybe aa はモノイドである必要があります。

もういちど Data.Semigroup.First

最初の引用を再掲します。

This type will be marked deprecated in GHC 8.8, and removed in GHC 8.10. Users are advised to use the variant from Data.Semigroup and wrap it in Maybe.

というわけで Data.Monoid.First の代わりに Data.Semigroup.First を使うように置き換えてみます。

元々の Data.Monoid.First を次に再掲します。

> M.First Nothing <> M.First (Just 'a') <> M.First (Just 'b')
First {getFirst = Just 'a'}

なんか M.First Nothing が無視されるのはただの Nothing っぽいので Maybe ? という形になりそうです(ほんまか?)。

Maybe ? の演算は ? の演算をして Just にくるむので ? は左を選ぶやつにすればいいようです。

Data.Semigroup.First がそれなので、M.First a ≡ Maybe (S.First a) になりそうです。

……というのがドキュメントに書いてました。

Note the following equivalence: Data.Monoid.First x === Maybe (Data.Semigroup.First x)