ただのブログです

技術的な物とかを主に。主にWeb系がメイン。いつか、職業エンジニアになりたい。

圏論とかモナドについて①

圏論とかモナドとかについて話さなければいけないので先にちょっと下書き的に書きます。
マサカリの燃料投下になればいいなぁ…(推敲できるし)

目的

圏論とかモナドとかという言葉に敏感に反応してマサカリを投げたりしない(できない)人を対象に、Scalazを使うため(?)の基礎知識を身につけることを目的にします。
モナドとか、色々な解説を読む上での必要となる言葉が何を意味しているのかを簡単にイメージできるレベルになることを目的とします

概要

説明の厳密さは除いてあります。
厳密さを除いたら数学じゃなくなりますが、厳密さを知る前の概要程度にとどめてください。
ただ、理解していなくても概要を知っておくとモから始まるアレを利用した解説や、trait達の意味を理解する"きっかけ"になるとは思います。
そして、これらのことを理解していなくてもある程度までの実用には耐えると考えています。
先に言いますが、これらの概念に対する理解をしていなくてもモナドを扱うことは可能です。モナドを意識しなくても、モナドは呼吸をするがごとく自然に、コードの至るところに出現しているからです。
よいコードを書こうとすればするほど、そのコード上の形式はなんであれ自然とモナドはそこにいます。

群論について

群論についての知識を持つことで、よりスムーズに理解できると思うので、先に説明します。
(ちなみに理系の大学でも学科によっては群論扱わなかったりするらしいですね。自分は大学行けなかったので、噂で聞きましたが)

群について

群そのものは知らなくても理解できますが、群というものの定義を知ることでモノイドや半群についての理解の助けになると思うので簡単に説明します。
元という言葉を使いますが、元=要素です。
集合の元といわれたら、集合の要素と認識して下さい。

まず群は下記の4つを満たすものを指す

  • 集合の元の間に一意的な演算が成り立ち、その演算に関して元は閉じている
  • その演算に対して結合則が成り立つ
  • 演算には単位元が存在する
  • 演算には逆元が存在する

定義ではすぐ分からないと思うので、厳密さもそんな書く気じゃないし特定の例で説明します。

ex)実数の集合Rと加法演算子+に関して群を成す

集合の要素の間に一意的な演算が成り立ち、その演算に関して集合は閉じている

a,b ∈ R
なら
a + b ∈ R

1 + 2 = 3
3も実数ですね。証明とかしないけど実数+実数=実数ですね。

その演算に対して結合則が成り立つ

(a , b) + c = a + (b + c)

(1 + 2) + 3 = 1 + (2 + 3) = 6 これは小学生のときにやりました。

演算には単位元が存在する

a + 0 = 0 + a = a

0が単位元になります。
ついでに乗法だと単位元は1です。x * 1 = 1 * x = x 単位元とは、演算の結果何も起こらない(変化しない)元のことです。

4つめ

演算には逆元が存在する

a + (-a) = 0

逆元が存在します。(逆数が一般化したもの、ある任意の元に対して単位元へ導ける元)

もし興味があれば、アーベル群について調べると上記の例をより高度な次元で理解できるようになると思います。

半群について

次に半群と呼ばれるものを説明します。
半群は群を更に弱めた概念で、群の内下記の二つを満たすものを指す

  • 集合の要素の間に一意的な演算が成り立ち、その演算に関して集合は閉じている
  • その演算に対して結合則が成り立つ

群と違い単位元や逆元が求められていない物です。

モノイドについて

モナドを理解する上で重要な単語の一つであるモノイドについて

モノイドは半群をより群に近づけた概念で、群の内下記の三つを満たすものを指す

  • 集合の要素の間に一意的な演算が成り立ち、その演算に関して集合は閉じている
  • その演算に対して結合則が成り立つ
  • 演算には単位元が存在する

群から逆元を除いたもので、半群に単為元の制約をつけたものですね。


モノイド等、これらは代数的な構造と呼ばれます。
その他代数的構造はいっぱいありますが、まずはこれだけとりあえず知っていればいいです。

英語では
群=group
半群=Semigroups
モノイド=Monoidとなります。

以上を踏まえてScalazのSemigroup.scalaを見てみると

trait Semigroup[F]  { self =>
  def append(f1: F, f2: => F): F

  trait SemigroupLaw {
    def associative(f1: F, f2: F, f3: F)(implicit F: Equal[F]): Boolean =
      F.equal(append(f1, append(f2, f3)), append(append(f1, f2), f3))
  }  
  def semigroupLaw = new SemigroupLaw {}

という定義を確認できると思います。 appendを定義しろ(FとFの二つの引数からappend演算した結果Fになるものを定義しろ)
SemigroupLawで、appendで結合則が成り立つか確かめるよ。 と言っています。

モノイドは半群をより群に近めた概念。半群を継承したと考えられるので
ScalazのMonoid.scalaには下記のように定義してあります

trait Monoid[F] extends Semigroup[F] { self =>
  def zero: F
}

Semigroupに加えて、zero(零元)を定義しろと言っていますね。
ちょっと零元=単位元と考えてもられば、コードの意味が分かると思います。

MonoidやSemigroupsというtraitを継承したtraitが沢山あるので、少しはそれらのtraitの名前から概念・意味が理解できるようになったと思います。


一度群の物語はここで終了です。
ここで一度話しが変わります。(変わるというか、別方向から眺めたい圏というものを説明します)
Scalazでたくさん出てくるモナドさんと戦うにはもう一つ別の方向から、いくつかの概念を知る必要があります。