bitterharvest’s diary

A Bitter Harvestは小説の題名。作者は豪州のPeter Yeldham。苦闘の末に勝ちえた偏見からの解放は命との引換になったという悲しい物語

モノイドーHaskellでの表現

13.モノイド

モノイド圏は、集合の要素を射に、二項演算子を射の合成に、そして、単位元を恒等射にし、結合律、単位律が成り立っているものをいう。

例えば、整数の乗算の場合には、
1) 対象:シングルトン(通常星印で表される)
2) 射:整数
3) ドメイン、コドメイン:シングルトン
4) 恒等射:1
5) 合成: \(\times\)
① 結合律:満足
② 単位律:満足

しかし、上記の例でみたように射は集合の要素となるので、圏としては、余りにも細かいものが見えて気持ちよくない。そこで、構成要素を隠すように抽象化したい。しかし、いきなり、抽象化の議論を進めるのは気が引けるので、モノイドとは何かをHaskellを利用して慣れておこう。

13.1 Haskellでのモノイド

HaskellではモノイドはData.Monoidでクラスとして次のように定義されている。

class Monoid m where
  mempty :: m
  mappend :: m -> m -> m
  mconcat :: [m] -> m
  -- defining mconcat is optional, since it has the following default:
  mconcat = foldr mappend mempty

これに従えば、モノイドを使うときは\(mempty\)と\(mappend\)を定義する必要がある。なお、\(mappend\)は、中置関数\(<>\)でしばしば置き換えられる。

x <> y = mappend x y

また、結合律と単位律を満たさなけらばならないので、次の式を満たさなければならない。

-- Identity laws
x <> mempty = x
mempty <> x = x
-- Associativity
(x <> y) <> z = x <> (y <> z)

それでは、文字列の結合をモノイドとして定義しよう。次のようになる。

instance Monoid [a] where
  mempty = []
  mappend x y = x ++ y
  mconcat = concat

積は次のようになる。積は数(\(Num\))に対する二項演算子であるが、モノイドとして定義するときは、\(Product\)と呼ばれる新しいデータ型(コンテナ)を用意する。

newtype Product n = Product n
 
instance Num n => Monoid (Product n) where
  mempty = Product 1
  mappend (Product x) (Product y) = Product (x * y)

和(余積)は次のようになる。ここでも、\(Sum\)と呼ばれる新しいデータ型(コンテナ)を用意する。

newtype Sum n = Sum n
 
instance Num n => Monoid (Sum n) where
  mempty = Sum 0
  mappend (Sum x) (Sum y) = Sum (x + y)

それでは利用してみよう。なお、Data.Monoidでは\(getProduct,getSum\)により、演算結果を数(\(Num\))のデータ型で表す関数を用意している。

Prelude> import Data.Monoid
Prelude Data.Monoid> [1,2,3] <> [5,6]
[1,2,3,5,6]
Prelude Data.Monoid> [1,2,3] <> []
[1,2,3]
Prelude Data.Monoid> [] <> [2,3,4]
[2,3,4]
Prelude Data.Monoid> Product 3 <> Product 5
Product {getProduct = 15}
Prelude Data.Monoid> Product 3 <> Product 1
Product {getProduct = 3}
Prelude Data.Monoid> Product 1 <> Product 2.34
Product {getProduct = 2.34}
Prelude Data.Monoid> getProduct $ Product 32.5 <> Product 2.34
76.05
Prelude Data.Monoid> getProduct $ Product (3 / 4) <> Product ( 6 / 7)
0.6428571428571428
Prelude Data.Monoid> getSum $ Sum 4 <> Sum 5
9
Prelude Data.Monoid> getSum $ Sum 4 <> Sum 0
4
Prelude Data.Monoid> getSum $ Sum 0 <> Sum 5.5
5.5
Prelude Data.Monoid> getSum $ Sum ( 2.3 * 5) <> Sum (5.5 + 3.2)
20.2