モノむド、セミグルヌプ、すべおすべお

実際にOOPを䜿甚する堎合は、 「デザむンパタヌン」などに粟通しおいたす。 この暙準リストに収たらない䟿利なパタヌンがたくさんあるこずをご存知ですか 残念ながら、それらの倚くは「関数型プログラミング」に関連付けられおおり、䌝説によるず、これは耇雑で難解です。 「モノむド」ずいう蚀葉を10回蚀うず、悪魔を呌ぶこずができたす。


Mark Seemanが関数型プログラミングに぀いおすばやく簡単に語っおいたす。 これを行うために、圌はデザむンパタヌンずカテゎリ理論の関係に関する䞀連の蚘事を曞き始めたした。 15分の空き時間があるOOPshnikは、機胜だけでなく、適切なオブゞェクト指向の蚭蚈に関する根本的に新しいアむデアず掞察のセットを手に入れるこずができたす。 決定的な芁因は、 すべおの䟋が実際のC、F、およびHaskellコヌドであるこずです。 このhabrapostは、サむクルの最初の郚分を翻蚳したもので、最初の3぀の蚘事は理解しやすいようにたずめられおいたす。


さらに、2017幎11月12〜13日にモスクワのスラビャンスクラディ゜ンで 開催されるDotNext 2017 Moscowカンファレンスに参加しお、Markずラむブでチャットできたす。 マヌクは「䟝存性泚入から䟝存性拒吊たで」に関する講挔を読みたす。 チケットはこちらで入手できたす 。


゚ントリヌ。 モノむド、セミグルヌプ、すべおすべお


このテキストはデザむンパタヌンずカテゎリヌ理論の間のリンクに関する新しいシリヌズの䞀郚です。
関数型プログラミングは通垞、その特殊な専門甚語で批刀されたす。 接合䜓前圢態前症などの甚語は、初心者に本質を䌝えるのに圹立ちたせん。 しかし、石を投げる前に、たず自分のガラスの家を出なければなりたせん。 オブゞェクト指向蚭蚈では、 Bridge 、 Visitor 、 SOLID 、 接続性などの名前が䜿甚されたす。 蚀葉はおなじみのように聞こえたすが、コヌドで蚪問者パタヌンを説明たたは実装したり、「接続性」ずは䜕かを説明したりできたすか


ブリッゞずいう蚀葉自䜓は、オブゞェクト指向の甚語を改善するものではありたせん。 おそらくそれは圌女をさらに悪化させたす。 最終的に、この蚀葉はあいたいになりたした。2぀の異なる堎所を組み合わせた実際の物理オブゞェクトを意味するのでしょうか、それずもデザむンパタヌンに぀いお話しおいるのでしょうか。 もちろん、実際には、コンテキストからこれを理解したすが、これは事実をキャンセルしたせん-誰かが橋のパタヌンに぀いお話す堎合、事前にそれを孊んでいない堎合、あなたは党く䜕も理解したせん。 単語がおなじみのように聞こえるからずいっお、それが䟿利になるわけではありたせん。


倚くのオブゞェクト指向プログラマヌは、「匕数ずしお受け取ったものず同じ型を返す操䜜」の有甚性を発芋したした。 それにもかかわらず、そのような蚘述、そのような蟞曞は非垞に䞍䟿です。 この操䜜を䞀蚀で説明する方が良いず思いたせんか モノむドですか、 セミグルヌプですか


オブゞェクト指向の掞察


ドメむン駆動蚭蚈の本で、 Eric EvansはClosure of Operationsの抂念に぀いお語っおいたす。 名前が瀺すように、これは「戻り倀の型が匕数の型ず䞀臎する操䜜」です。 Cでは、眲名public Foo Bar(Foo f1, Foo f2)持぀メ゜ッドである可胜性がありたす。 このメ゜ッドは、2぀のFooオブゞェクトを入力ずしお受け取り、 Fooオブゞェクトも返したす。


゚ノァンスが指摘したように、このように蚭蚈されたオブゞェクトは、たるで算術を圢成しおいるように芋え始めたす。 Fooを受け入れおFooを返す操䜜がある堎合、それは䜕でしょうか たぶん远加 乗算 他の数孊挔算はありたすか


䞀郚の゚ンタヌプラむズ開発者は、単に「ビゞネスをしお先に進みたい」だけで、数孊にたったく悩たされおいたせん。 圌らにずっお、コヌドをより「数孊的な」ものにするずいう考えは非垞に物議をかもしたす。 それにもかかわらず、「数孊が奜きではない」堎合でも、加算、乗算などの意味を確実に理解できたす。 すべおのプログラマヌが理解しおいるように、算術は匷力な隠phorです。


圌の有名な本「 Test-Driven DevelopmentExample by」では、 Kent Beckは同じ考えを利甚したようです。 私は圌がどこかでこれに぀いお盎接曞いたずは思わないが。


゚ノァンスが曞いたのは、モノむド、セミグルヌプ、および抜象代数からの同様の抂念です。 公平を期しお、最近圌ず話をしたしたが、今では圌はこれらすべおに粟通しおいたす。 圌はDDDが曞かれた2003幎にそれらを理解したしたか-私は知りたせんが、私は間違いなく理解したせん。 ここでの私の仕事は、指を突くのではなく、非垞に賢い人々がOOPで䜿甚できる原則を開発したこずを瀺すこずです。


これはすべおどうやっお぀ながっおいるの


モノむドずセミグルヌプは、マグマず呌ばれるより倧きな操䜜グルヌプに属したす。 これに぀いおは埌で説明したすが、モノむドから始めおセミグルヌプに進み、マグマに移りたす。 すべおのモノむドはセミグルヌプであり、その逆は圓おはたりたせん。 ぀たり、モノむドはセミグルヌプのサブセットを圢成したす。



これらは、2぀のFoo倀を入力ずしお受け取り、出力でFoo型の倀を返す操䜜の圢匏でバむナリ操䜜を蚘述したす。 䞡方のカテゎリは盎芳的な法埋で蚘述されおいたす。 違いは、モノむドの法則がセミグルヌプの法則よりも厳密であるこずです。 甚語に固執しないでください。「法」ずいう蚀葉は、深刻な耇雑な数孊が関係しおいるように聞こえるかもしれたせんが、これらの「法」はシンプルで盎感的です。 それらに぀いおは、次の郚分で説明したすそのうち玄15個。


それらはすべお数孊ず密接に関連しおいるずいう事実にもかかわらず、ずりわけ、優れたオブゞェクト指向蚭蚈のための倚くのアむデアを提䟛するように蚭蚈されおいたす。


たずめ


通垞のオブゞェクト指向プログラマヌの堎合、 モノむドやセミグルヌプのような甚語は、建築の宇宙飛行士が䜏む数孊、アカデミヌ、象牙の塔のような匂いがしたす。 しかし、実際には、これらはシンプルで䟿利なアむデアであり、誰もが誰がこれに15分を費やすのが面倒ではないかを理解できたす。


パヌト1.モノむド


結論OOPプログラマヌのためのモノむドの玹介。
このセクションは、モノむド、セミグルヌプ、および関連する抂念に関する䞀連の蚘事の䞀郚です。 このセクションを孊習するず、モノむドずは䜕か、セミグルヌプずはどのように異なるかを理解できたす。



モノむドは、セミグルヌプのサブセットを圢成したす。 モノむドが機胜するルヌルは、セミグルヌプよりも厳密です。 最初にセミグルヌプに察凊し、それらに基づいおモノむドに進む方がよいず刀断するこずもできたす。 厳密に蚀えば、階局の芳点では、これは理にかなっおいたす。 しかし、モノむドははるかに盎感的だず思いたす。 モノむドの最初の䟋を芋るず、それらが日垞生掻からのものを蚘述しおいるこずがすぐにわかりたす。 モノむドの䟋を芋぀けるのは簡単ですが、セミグルヌプの良い䟋を遞択するには、詊しおみる必芁がありたす。 したがっお、モノむドから始めたす。


モノむド法


加算 40 + 2 ず乗算 6 * 7 の共通点は䜕ですか
これらの操䜜の䞡方



モノむドを圢成するために必芁なのはこれだけです。 䞭立的な芁玠の結合性ず存圚は、「モノむド法」たたは「モノむド法」英語ではモノむド法 ず呌ばれたす。 モノむドはデヌタ型ず操䜜の組み合わせであるこずは泚目に倀したす。 ぀たり、単なる型ではなく、この型で機胜する関数たたはメ゜ッドです。 たずえば、加算ず乗算は、数倀に䜜甚する2぀の異なるモノむドです。


バむナリ


最も単玔なものから始めたしょう。 2぀の倀で機胜する堎合、操䜜は「バむナリ」です。 おそらく、「バむナリ」ずいう蚀葉に蚀及するず、䞻に101010などのバむナリデヌタが衚瀺されたすが、この単語はラテン語に由来し、「アリティ2」に関連するものを意味したす。 倩文孊者は時々バむナリスタヌに぀いおも話したすが、珟圚ではこの蚀葉は䞻にコンピュヌタの文脈で䜿甚されおいたす。バむナリデヌタに加えお、おそらくバむナリツリヌに぀いお聞いたこずがあるでしょう。 二項挔算ずいえば、䞡方の入力倀が同じ型であり、戻り倀の型も入力型ず䞀臎するこずを意味したす。 蚀い換えれば、Cでは、このようなメ゜ッドは有効なバむナリ挔算です。


 public static Foo Op(Foo x, Foo y) 

OpがFooクラスのむンスタンスメ゜ッドである堎合、次のようになりたす。


 public Foo Op (Foo foo) 

䞀方、これはもはやバむナリ操䜜ではありたせん。


 public static Baz Op(Foo f, Bar b) 

2぀の入力匕数を取りたすが、型は異なり、戻り倀の型も異なりたす。


すべおの匕数ず戻り倀は同じ型であるため、バむナリ操䜜はEric EvansがDomain-Driven Designの Closure of Operationsず呌んだものです。


連想性


モノむドの圢成のために、二項挔算は必ず連想的でなければなりたせん。 これは単に、蚈算の順序が重芁でないこずを意味したす。 たずえば、远加の堎合、これは次のこずを意味したす。


 (2 + 3) + 4 = 2 + (3 + 4) = 2 + 3 + 4 = 9 

乗算に぀いおも同様


 (2 * 3) * 4 = 2 * (3 * 4) = 2 * 3 * 4 = 24 

䞊蚘のOpメ゜ッドに぀いお説明する堎合、結合性では、次のコヌドに察しおareEqualがtrueである必芁がありたす。


 var areEqual = foo1.Op(foo2).Op(foo3) == foo1.Op(foo2.Op(foo3)); 

巊偎では、 foo1.Op(foo2)蚈算され、次に結果がfoo3に適甚されfoo3 。 右偎では、 foo2.Op(foo3)最初に蚈算され、結果はfoo1.Op匕数にfoo1.Op 巊偎ず右偎は==挔算子を䜿甚しお比范されるため、結合性ではareEqualがtrue必芁がありtrue 。


この構造党䜓がCで機胜するためには、䜕らかの自䜜モノむドFoo堎合、 Equalsをオヌバヌロヌドし、 ==挔算子を実装する必芁がありたす。


䞭立芁玠


モノむドの3番目のルヌルは、䞭立的な芁玠が存圚する必芁があるずいうこずです。 通垞、 ナニットず呌ばれたす最適な名前ではありたせんが、かさばる「䞭立芁玠」よりも優れおいたす。 将来的にはそれを呌び出したす。


単䜍は、「䜕もしない」倀です。 たずえば、远加の堎合は0です0れロを远加しおも元の倀ずは䜕も倉わらないため、倉曎されたせん。


 0 + 42 = 42 + 0 = 42 

簡単な挔習乗算の単䜍を掚枬したす。


䞊蚘の合蚈の蚘録は、ナニットが巊偎で䜿甚される堎合ず右偎で䜿甚される堎合の䞡方で、ナニットが䞭立的に動䜜する必芁があるこずを意味したす。 Fooオブゞェクトの堎合、これは次のように蚘述できたす。


 var hasIdentity = Foo.Identity.Op(foo) == foo.Op(Foo.Identity) && foo.Op(Foo.Identity) == foo; 

ブヌル倀を凊理するモノむドがいく぀かありたす allずanyです。 あなたはどう思いたすか、圌らはどのように機胜したすか 圌らのナニットは䜕ですか


挔習ずしお、 any およびそれらをグヌグルでを振り返るこずができたす。 次のセクションでは、他のより興味深いモノむドを瀺したす。 このハブポストでは、行、リスト、およびシヌケンスのみが考慮されたす-残りの蚘事はただ執筆䞭です。


実際、「数倀のように振る舞う」デヌタ型がある堎合、ほずんどの堎合、それからモノむドを䜜成できたす。 加算は最も簡単な候補の1぀です。なぜなら、最も簡単に理解でき、枬定単䜍のようなものをいじる必芁がないからです。 たずえば、.NET基本クラスラむブラリには、 Addメ゜ッドを持぀TimeSpan構造がありたす。 挔算子==圌女も持っおいたす。 䞀方、 TimeSpanにはMultiplyメ゜ッドがありたせん。なぜなら、2぀の期間を乗算した結果はどうなるのでしょうか スク゚アタむム 


たずめ


モノむドモナドず混同しないでくださいは、モノむドの2぀の法則を満たす2項挔算です。挔算は連想的であり、䞭立芁玠ナニットが存圚する必芁がありたす。 モノむドの䞻な䟋は加算ず乗算ですが、他にもたくさんありたす。


ちなみに、乗算の単䜍は単䜍1で、 allがブヌル倀andであり、 anyもブヌル倀orです。


パヌト2.行、リスト、シヌケンスのモノむド


䞀番䞋の行文字列、リスト、およびシヌケンスは、本質的に同じモノむドです。
このセクションはモノむドに関する䞀連の蚘事の䞀郚です。
芁するに、 モノむドは、䞭立芁玠 ナニット 、たたは英語の甚語ではidentityず呌ばれるを持぀連想バむナリ操䜜です。


シヌケンス


Cでは、倀の遅延シヌケンスはIEnumerable<T>を䜿甚しおモデル化されたす。 シヌケンスのペアを組み合わせるには、䞀方を他方に远加したす。


 xs.Concat(ys) 

ここで、 xsずysはIEnumerable<T>むンスタンスです。 Concat拡匵メ゜ッドはシヌケンスを結合したす。 次のシグネチャがありたす IEnumerable<T> Concat<T>(IEnumerable<T>, IEnumerable<T>) 、したがっお、バむナリ操䜜です。 連想性があり、ナニットを持っおいるこずがわかった堎合、モノむドであるこずを蚌明したす。


蚈算のシヌケンスは結果を倉曎しないため、シヌケンスは結合的です。 結合性はモノむドプロパティであるため、それを実蚌する1぀の方法は、 プロパティベヌスのテストを䜿甚するこずです。


 [Property(QuietOnSuccess = true)] public void ConcatIsAssociative(int[] xs, int[] ys, int[] zs) { Assert.Equal( xs.Concat(ys).Concat(zs), xs.Concat(ys.Concat(zs))); } 

この自動テストではFsCheckを䜿甚したす はい、Cでも動䜜したす Concat結合性を実蚌したす。 問題を簡玠化するために、 xs 、 ysおよびzs 配列ずしお宣蚀されおいたす 。 FsCheckは配列の䜜成方法をネむティブに知っおいるため、これが必芁ですが、 IEnumerable<T>は組み蟌みのサポヌトがありたせん。 もちろん、FsCheck APIを䜿甚しお自分でiEnumerable<T>䜜成するこずもできたすが、これは䟋を耇雑にし、新しいものを远加したせん。 結合性プロパティは、 IEnumerable<T>他の玔粋な実装にも適甚されたす。 私を信じないなら、詊しおみおください。


Operation Concatにはナニットがありたす。 ナニットは、次のテストで確認された空のシヌケンスです。


 [Property(QuietOnSuccess = true)] public void ConcatHasIdentity(int[] xs) { Assert.Equal( Enumerable.Empty<int>().Concat(xs), xs.Concat(Enumerable.Empty<int>())); Assert.Equal( xs, xs.Concat(Enumerable.Empty<int>())); } 

぀たり、空のシヌケンスがシヌケンスの先頭たたは末尟に接着されおいる堎合、元のシヌケンスは倉曎されたせん。


Concatはナニットを持぀連想バむナリ操䜜であるため、モノむドです。 蚌明されおいたす。 â—Œ


リンクリストずその他のコレクション


FsCheckを䜿甚した䞊蚘のテストは、 Concatが配列のモノむドであるこずを瀺したした。 このプロパティは、すべおの玔粋なIEnumerable<T>実装に察しお保持されたす。


Haskellでは、遅延シヌケンスはリンクリストずしおモデル化されたす。 Haskellのすべおの匏がデフォルトの匏であるずいう理由だけで、それらは怠areです。 モノむドの法則は、Haskellのリストにも有効です。


 λ> ([1,2,3] ++ [4,5,6]) ++ [7,8,9] [1,2,3,4,5,6,7,8,9] λ> [1,2,3] ++ ([4,5,6] ++ [7,8,9]) [1,2,3,4,5,6,7,8,9] λ> [] ++ [1,2,3] [1,2,3] λ> [1,2,3] ++ [] [1,2,3] 

Haskellでは、 ++挔算子はCのConcatずほが同じですが、この操䜜は連結ではなくappendず呌ばれたす 。


Fでは、Fのすべおの匏がデフォルトの匏であるため、リンクリストは積極的に遅延ではなく初期化されたす。 ただし、モノむドのすべおのプロパティがただ満たされおいるため、リストはモノむドのたたです。


 > ([1; 2; 3] @ [4; 5; 6]) @ [7; 8; 9];; val it : int list = [1; 2; 3; 4; 5; 6; 7; 8; 9] > [1; 2; 3] @ ([4; 5; 6] @ [7; 8; 9]);; val it : int list = [1; 2; 3; 4; 5; 6; 7; 8; 9] > [] @ [1; 2; 3];; val it : int list = [1; 2; 3] > [1; 2; 3] @ [];; val it : int list = [1; 2; 3] 

Fでは、連結挔算子は++ではなく@ ++が、その動䜜はHaskellの堎合ずたったく同じです。 â—Œ


行


ほずんどのプログラミング蚀語でテキスト倀がstringず呌ばれる理由を疑問に思ったこずはありたせんか 結局のずころ、英語の文字列はロヌプであり 、繊維で䜜られたそのような長い柔軟なものです。


プログラミングでは、通垞、テキストはメモリ内で文字の連続したブロックずしお衚されたす。 通垞、プログラムは、行の終わりの兆候である䜕かに達するたで、このような連続したメモリブロックを読み取りたす。 したがっお、文字列は順序付けられたす。 シヌケンスたたはリストのように芋えたす。


実際、Haskellでは、 String型は扱いにくいものではなく、 [Char]同矩語ですこれは、 Char倀のリストです。 したがっお、他のタむプのリストでできるこずはすべお、 Stringでもできたす。


 λ> "foo" ++ [] "foo" λ> [] ++ "foo" "foo" λ> ("foo" ++ "bar") ++ "baz" "foobarbaz" λ> "foo" ++ ("bar" ++ "baz") "foobarbaz" 

明らかに、 String ++はHaskellのモノむドです。


同様に、.NETでは、 System.StringはIEnumerable<char>実装したす。 類掚により、我々はそれらがモノむドであるこずが刀明するず掚枬するこずができたす-そしおこれはほずんど事実です。 芋おみたしょう、それらは正確に連想的です


 [Property(QuietOnSuccess = true)] public void PlusIsAssociative(string x, string y, string z) { Assert.Equal( (x + y) + z, x + (y + z)); } 

Cでは、 +挔算子がstringに察しお実際に定矩されおおり、このFsCheckのテストからわかるように、これは結合性です。 そしお、圌はほずんどナニットを持っおいたす。 文字列の䞖界で空のリストに盞圓するものは䜕ですか もちろん、空の行


 [Property(QuietOnSuccess = true)] public void PlusHasIdentity(NonNull<string> x) { Assert.Equal("" + x.Get, x.Get + ""); Assert.Equal(x.Get, x.Get + ""); } 

null必芁ないこずを手動でFsCheckに説明する必芁がありたした。 い぀ものように、 nullはコヌドの議論の際に棒を車茪に入れたす。


ここでの問題は、 "" + nullおよびnull + ""が同じ倀- ""返し、入力倀 null ず等しくないこずです。 ぀たり、この特殊なケヌスが存圚するため、 "" +挔算子の実際の単䜍で""たせん。 そしお、ちなみに、 null null + nullは... ""返すため、 nullも単䜍でnullありたせんもちろん、それだけを返したす...。 ただし、これは実装の機胜です。 緎習ずしお、 nullを持っおいるにもかかわらず、 string有効なモノむドにするCのメ゜ッド拡匵を考えおみおください。 思い぀くずすぐに、Haskellず同じように、文字列の連結が.NETのモノむドであるこずをすぐに瀺したす。 â—Œ


無料モノ​​むド


以前の蚘事で、数字の加算ず乗算がモノむドであるこずを瀺したこずを思い出しおください。 少なくずも1぀以䞊の数のモノむドがあり、これはシヌケンスです。 ゞェネリックシヌケンス IEnumerable<T> が存圚する堎合、番号を含むすべおを含めるこずができたす。


3ず4 2぀の数倀があり、それらを結合したいず考えおいたすが、それらをどのように正確に結合するかはただ明確ではありたせん。 ゜リュヌションを延期するために、䞡方の数倀を単䞀の配列に入れるこずができたす英語ではこれはシングルトン配列ず呌ばれたす-1぀の芁玠を持぀配列で、これはシングルトンパタヌンずは関係ありたせん。


 var three = new[] { 3 }; var four = new[] { 4 }; 

以前に蚌明したように、シヌケンスはモノむドであるため、安党に組み合わせるこずができたす。


 var combination = three.Concat(four); 

結果は、䞡方の数倀を含むシヌケンスです。 珟時点では情報を倱っおいないため、これらの数倀を組み合わせる方法が明確になり次第、以前に収集したデヌタを蚈算するだけで枈みたす。 これは無料モノ​​むドず呌ばれたす。


たずえば、数倀の合蚈を取埗する必芁があるず刀断したした。


 var sum = combination.Aggregate(0, (x, y) => x + y); 

はい、 Sumメ゜ッドがあるこずは承知しおいたすが、珟圚の目暙は詳现を把握するこずです。 このAggregateは、最初の匕数ずしおseed倀を取り、2番目ずしお組み合わせ関数を取りたす。


そしお、あなたは補品を手に入れるこずができたす


 var product = combination.Aggregate(1, (x, y) => x * y); 

どちらの堎合も、 seed倀は察応するモノむド挔算の1であるこずに泚意しおください0は加算、 1は乗算です。 同様に、集蚈関数は、察応するモノむドを参照するバむナリ挔算を䜿甚したす。


興味深いこずに、これは「無料モナド 」に䌌た「無料モノむド」ず呌ばれたす。 どちらの堎合でも、すべおのデヌタを収集しおすぐに解釈するこずはできたせん。その埌、事前に準備された倚くの「蚈算機」のいずれかにこのデヌタをロヌドしたす。


たずめ


.NETのシヌケンスや配列、FやHaskellのリストなど、倚くのタむプのコレクションは、連結に察するモノむドです。 Haskellでは、文字列はリストであるため、文字列の連結は自動的にモノむドになりたす。 .NETでは、文字列の+挔算子はモノむドですが、 nullが存圚しないふりをした堎合のみです。 ただし、それらはすべお同じモノむドのバリ゚ヌションです。


前のパヌトで瀺したように、远加はすべおのモノむドの䞭で最も盎感的で「自然」であるため、Cが+を䜿甚しお文字列を連結するのは良いこずです。 あなたは孊校の算数を知っおいるので、远加の比phorをすぐに理解できたす。 しかし、モノむドは比phor以䞊のものです。 これは特別なバむナリ挔算を説明する抜象抂念であり、その1぀それが起こったは加算です。 これは抂念の䞀般化であり 、これはすでに知っおいる抜象化です。


おわりに


これでこの蚘事は終わりです。 バックリンクでリンクされたHabréぞの連続した投皿ずいう圢で、オリゞナルず同じ方法で公開される情報がただたくさんありたす。 以䞋蚘事のオリゞナルは©Mark Seemann 2016、翻蚳はJUG.ru Groupが、翻蚳者はOleg Chirukhinです。


2017幎 11月12〜13日にモスクワのスラビャンスカダラディ゜ンで開催されるDotNext 2017モスクワ䌚議にアクセスしお、著者ずラむブでチャットできたす。 マヌクは「䟝存性泚入から䟝存性拒吊たで」に関する講挔を読みたす。 チケットはこちらで入手できたす 。



Source: https://habr.com/ru/post/J340178/


All Articles