Fight the Future

Java言語とJVM、そしてJavaエコシステム全般にまつわること

モナドについて調べていく(17)

One Div Zero: Monads are Elephants Part 3の翻訳続き。
ファンクターが出てきました。

WTF - What The Functor?(ファンクターとは何か?)

Usually articles that start with words like "monad" and "functor" quickly devolve into soup of Greek letters. That's because both are abstract concepts in a branch of mathematics called category theory and explaining them completely is a mathematical exercise. Fortunately, my task isn't to explain them completely but just to cover them in Scala.

一般的に「モナド」と「ファンクター」のような単語で始まる記事はすぐにギリシャ文字の海が襲ってきます。
なぜなら「モナド」も「ファンクター」も圏論と呼ばれる数学の分野に置ける抽象的な概念だからです。
またそれらを完全に説明することは数学の行為だからです。
幸運なことに私の仕事はそれらを完全に説明することではなくScalaでそれらをカバーすることだけです。


In Scala a functor is a class with a map method and a few simple properties. For a functor of type M[A], the map method takes a function from A to B and returns an M[B]. In other words, map converts an M[A] into an M[B] based on a function argument. It's important to think of map as performing a transformation and not necessarily having anything to do with loops. It might be implemented as a loop, but then again it might not.

Scalaにおいてファンクターはmapメソッドといくつかの単純な属性を持つクラスです。
M[A]型のファンクターでは、mapメソッドはAからBへの関数を引数に取りM[B]を返します。
言い換えると、mapはM引数の関数に基づいて[A]をM[B]へ変換します。
次のことは重要です。mapを変換を実行するものとして考え、必ずしもループで何かをするものものとして考えないことです。
ループとして実装されるかもしれませんが、そうでなくてもかまわないのです。


Map's signature looks like this
mapのシグネチャは次のようなものです。

class M[A] {
 def map[B](f: A => B):M[B] = ...
}

First Functor Law: Identity(ファンクターの第1法則:同一性)


Let's say I invent a function called identity like so
次のようなidentity関数を作ります。

def identity[A](x:A) = x
jyukutyoコメント

ScalaのクラスPredefにidentityメソッドがあるけど、関係あるかな?

  implicit def identity[A](x: A): A = x


This obviously has the property that for any x
明らかにこれはどんなxをとっても特性があります。

    identity(x) ≡ x

It doesn't do much and that's the point. It just returns its argument (of whatever type) with no change. So here's our first functor law: for any functor m

これ以上何もしませんが、それがポイントです。(どんな型であっても)その引数を何も変えずに返すだけです。
ゆえにファンクターの第1法則はこうなります。あらゆるファンクターmにとって

  • F1. m map identity ≡ m // or equivalently *
  • F1b. m map {x => identity(x)} ≡ m // or equivalently
  • F1c. m map {x => x} ≡ m

In other words, doing nothing much should result in no change. Brilliant! However, I should remind you that the expression on the left can return a different object and that object may even have a different internal structure. Just so long as you can't tell them apart.

言い換えると、何もしないということは何も変えないということになります。すばらしい!
しかしながら、左辺の式が異なるオブジェクトを返し、異なる内部構造を持っていさえするオブジェクトがあるということを思い出してください。
単にそれらを見分けない限りですが。


If you were to create a functor that didn't follow this law then the following wouldn't hold true. To see why that would be confusing, pretend m is a List.

もし法則に従わないファンクターを作成し、あとに続くことが真にならないとします。
なぜこれが混乱することなのか見てみましょう。mはリストを装っています。

  • F1d. for (x <- m) yield x ≡ m

Second Functor Law: Composition(第2法則:コンポジション)

The second functor law specifies the way several "maps" compose together.

2つ目のファンクター法則はいくつかの「map」を一緒に組み合わせる方法を述べます。

  • F2. m map g map f ≡ m map {x => f(g(x))}

This just says that if you map with g and then map with f then it's exactly the same thing as mapping with the composition "f of g." This composition law allows a programmer to do things all at once or stretch them out into multiple statements. Based on this law, a programmer can always assume the following will work.

これは単にもしgをmapしそれからfをmapすると、まさに「gのf」というコンポジションでmapすることと同じであるということを言っています。
このコンポジションの法則でプログラマは一度にすべてを実行するか複数のステートメントにそれらを伸ばすことができます。
この法則に基づいて、プログラマはいつも次のことが動作すると仮定するでしょう。

val result1 = m map (f compose g)
val temp = m map g
val result2 =  temp map f
assert result1 == result2

In "for" notation this law looks like the following eye bleeder
「for」表記法においてこの法則は次のような見づらいものになります。

  • F2b. for (y<- (for (x <-m) yield g(x)) yield f(y) ≡ for (x <- m) yield f(g(x))