One Div Zero: Monads are Elephants Part 3の翻訳続き。
これでパート3は終了。
The Second Zero Law: M to Zero in Nothing Flat(第2のゼロの法則:すぐにMをゼロにする)
The reverse of the zero identity law looks like this
ゼロの同一法則を逆にすると次のようになります。
- MZ2. m flatMap {x => mzero} ≡ mzero
Basically this says that replacing everything with nothing results in nothing which um...sure. This law just formalizes your intuition about how zeros "flatten."
基本的にこれはすべてを何もないものに置き換えると結果は何もないものになります。うーん。たしかに。
この法則はゼロをどのように「flatten」するかについての直感を承認しただけです。
The Third and Fourth Zero Laws: Plus(第3と第4のゼロの法則:加算)
Monads that have zeros can also have something that works a bit like addition. For List, the "plus" equivalent is ":::" and for Option it's "orElse." Whatever it's called its signature will look this
ゼロを持つモナドはまた加算と少し似た何かを持ちます。リストでは「plus」は「:::」と等価であり、Optionでは「orElse」です。
何度呼ばれようとそのシグネチャは次のようなものです。
class M[A] { ... def plus(other:M[B >: A]): M[B] = ... }
jyukutyoコメント
orElse [B >: A](alternative : => Option[B]) : Option[B]
If the option is nonempty return it, otherwise return the result of evaluating an alternative expression.
Plus has the following two laws which should make sense: adding anything to a zero is that thing.
加算には次の2つの法則があります。どんなものにゼロを加算してもそのままだということです。
The plus laws don't say much about what "m plus n" is if neither is a monadic zero. That's left entirely up to you and will vary quite a bit depending on the monad. Typically, if concatenation makes sense for the monad then that's what plus will be. Otherwise, it will typically behave like an "or," returning the first non-zero value.
加算の法則は「m plus n」の両方ともがモナド的にゼロである場合についてのことは何も言っていてません。
そのことについては完全にあなた次第であり、依存するモナドによってかなり変わります。
通常は、もしモナドに取って連結が意味を成すならそれが加算が意味することになります。
そうでなければ、通常「or」のように振る舞い、ゼロでない最初の値を返します。
Filtering Revisited(フィルタリング再び)
In the previous installment I briefly mentioned that filter can be seen in purely monadic terms, and monadic zeros are just the trick to seeing how. As a reminder, a filterable monad looks like this
前回の連載では純粋にモナド的な意味でフィルターについて簡潔に言及しました。モナド的なゼロはどのようになるのかは単なるトリックです。
思い出してほしいのですが、フィルタリングできるモナドは次のようなものです。
class M[A] { def map[B](f: A => B):M[B] = ... def flatMap[B](f: A=> M[B]): M[B] = ... def filter(p: A=> Boolean): M[A] = ... }
The filter method is completely described in one simple law
フィルターモナドは単純な1つの法則で完全に記述できます。
- FIL1. m filter p ≡ m flatMap {x => if(p(x)) unit(x) else mzero}
We create an anonymous function that takes x and either returns unit(x) or mzero depending on what the predicate says about x. This anonymous function is then used in a flatMap. Here are a couple of results from this
xを引数にとり述語がxについて返すことに依存してunit(x)かmzeroを返す無名関数を作ります。
この無名関数はflatMapで使われます。ここから2,3の結論が出ます。
- m filter {x => true} ≡ m filter {x => true} // identity
- m filter {x => true} ≡ m flatMap {x => if (true) unit(x) else mzero} // by FIL1
- m filter {x => true} ≡ m flatMap {x => unit(x)} // by definition of if
- FIL1a. m filter {x => true} ≡ m // by M1
So filtering with a constant "true" results in the same object. Conversely
ゆえに定数「true」でフィルタリングすると同じオブジェクトとなります。逆に
- m filter {x => false} ≡ m filter {x => false} // identity
- m filter {x => false} ≡ m flatMap {x => if (false) unit(x) else mzero} // by FIL1
- m filter {x => false} ≡ m flatMap {x => mzero} // by definition of if
- FIL1b. m filter {x => false} ≡ mzero // by MZ1
Filtering with a constant false results in a monadic zero.
定数「false」でフィルタリングするとモナド的なゼロになります。
Side Effects(副作用)
Throughout this article I've implicitly assumed no side effects. Let's revisit our second functor law
この記事を通じて私は暗黙的に副作用がないことを前提にしてきました。2つめのファンクターの法則に戻ってみましょう。
- m map g map f ≡ m map {x => (f(g(x)) }
If m is a List with several elements, then the order of the operations will be different between the left and right side. On the left, g will be called for every element and then f will be called for every element. On the right, calls to f and g will be interleaved. If f and g have side effects like doing IO or modifying the state of other variables then the system might behave differently if somebody "refactors" one expression into the other.
もしmがいくつかの要素があるリストであるなら、左辺と右辺では操作の順序が異なるでしょう。
左辺においてgを各要素で呼び出してからfを各要素で呼び出します。
右辺ではfとgを交互に呼び出します。もしfとgにIOしたり他の変数の状態を変更するような副作用があれば、誰かがある式をほかの式に「リファクタリング」してしまうとシステムはことなる振る舞いをするかもしれません。
The moral of the story is this: avoid side effects when defining or using map, flatMap, and filter. Stick to foreach for side effects. Its very definition is a big warning sign that reordering things might cause different behavior.
この話の教訓は次のようなものです。mapやflatMap、filterを定義したり使ったりするときは副作用を避けるということです。
まさにその定義は順序を変えてしまうと異なる振る舞いを起こすかもしれないという大きな警告です。
Speaking of which, where are the foreach laws? Well, given that foreach returns no result, the only real rule I can express in this notation is
そういえば、foreachの法則はどこにあるんでしょう?えーと、何の結果も返さないものであるforeachが与えられると、この表記法で説明できるたった1つのほんとうのルールは、
- m foreach f ≡ ()
Which would imply that foreach does nothing. In a purely functional sense that's true, it converts m and f into a void result. But foreach is meant to be used for side effects - it's an imperative construct.
これはforeachが何もしないということを暗示しています。純粋に関数的な意味ではこれは真実であり、mとfをvoidの結果に変換します。
しかしforeachは副作用のために使われるということを意味しています、これは命令的な構造です。
Conclusion for Part 3(パート3の結論)
Up until now, I've focused on Option and List to let your intuition get a feel for monads. With this article you've finally seen what really makes a monad a monad. It turns out that the monad laws say nothing about collections; they're more general than that. It's just that the monad laws happen to apply very well to collections.
今まで、モナドの感触を直感でつかんでもらおうとOptionとListに焦点を合わせてきました。
この記事では最終的にモナドを真にモナドたらしめているものは何かということを観ていきます。
モナド則はコレクションについて何も言っていないということが結局わかります。コレクションよりも普遍的なものなのです。
モナド則はコレクションに対してたまたまとてもうまく適用できるというだけなのです。
In part 4 I'm going to present a full grown adult elephant er monad that has nothing collection-like about it and is only a container if seen in the right light.
パート4では完全に育った大人の象を紹介し、モナドがまったくコレクション的なものではなく正しい意味でとらえるなら単なるコンテナであるということを紹介します。
Here's the obligatory Scala to Haskell cheet sheet showing the more important laws
ここで必須の、より重要な法則を示すScaalからHaskellへのチートシートを出します。
Scala | Haskell | |
---|---|---|
FM1 | m map f ≡ m flatMap {x => unit(f(x))} | fmap f m ≡ m >>= \x -> return (f x) |
M1 | m flatMap unit ≡ m | m >>= return ≡ m |
M2 | unit(x) flatMap f ≡ f(x) | (return x) >>= f ≡ f x |
M3 | m flatMap g flatMap f ≡ m flatMap {x => g(x) flatMap f} | (m >>= f) >>= g ≡ m >>= (\x -> f x >>= g) |
MZ1 | mzero flatMap f ≡ mzero | mzero >>= f ≡ mzero |
MZ2 | m flatMap {x => mzero} ≡ mzero | m >>= (\x -> mzero) ≡ mzero |
MZ3 | mzero plus m ≡ m | mzero 'mplus' m ≡ m |
MZ4 | m plus mzero ≡ m | m 'mplus' mzero ≡ m |
FIL1 | m filter p ≡ m flatMap {x => if(p(x)) unit(x) else mzero} | mfilter p m ≡ m >>= (\x -> if p x then return x else mzero) |