はじめに
前の記事で、Haskell言語のデータ型と機能で構成されるHaskカテゴリーのコンテキストにおけるカテゴリーとファンクターの概念について説明しました。 ここで、すでに知られている概念から構築されたカテゴリの別の例と、モノイドの非常に重要な概念について説明します。
指定
前回、文字
f
でモーフィズム/関数を示したいと思っていましたが、タイプ
f
ファンクター/変数を示すために使用されました-Haskell言語の観点からは問題はありませんが、注意深く読むと混乱しやすく、文字をモーフィズムに使用しました
g
。 些細なことですが、それでも、性質の異なるエンティティを視覚的に分離することは有益だと思います。 私は普通の名前で普通の型を呼び出しますが、型変数はアルファベットの先頭から単純な(
∗
)文字、アルファベットの最後からパラメトリック(
∗ → ∗
)文字(末尾からではなく
θ
X
あまりにも似ている
χ
よりも良く見えます。 そのため、 Haskカテゴリの用語では:
- オブジェクト:
α, β, γ, δ ∷ ∗
- ファンクター:
θ, φ, ψ, ω ∷ ∗ → ∗
- 形態素:
f, g, h ∷ α → β
用語に関する別の発言:既にお気づきのように、私が最後に「種類」という言葉を呼んだもの(今)、私は今、「ソート」という言葉を呼んでいます-これは一般に受け入れられている翻訳と見なされます。
Haskカテゴリ
Haskカテゴリー自体というオブジェクトが1つだけ存在するカテゴリーを見てみましょう。 このカテゴリの射とは何ですか? これらは何らかの種類のマッピングHask →Haskである必要があり、このタイプのマッピングを既に知っています-これらはHaskカテゴリの内的ファンクター、つまり
Functor
クラスの実施形態である多様性
∗ → ∗
タイプです。 ここで、公理を満たすために、このカテゴリで単一の射と構図をどのように配置するかを考える必要があります。
アイデンティティファンクター
単位射であるアイデンティティファンクター、つまり、何も変更しないファンクターを選択するのは自然です。 Haskellでは、特別な指定はありません-何もしないので、なぜそれを指定する必要があるのですか? Haskカテゴリーのオブジェクトに対するこのファンクターのアクションを「視覚化」できる「ダミー」タイプのコンストラクターを紹介します。
type Id α = α
Haskellでは、型シノニムのインカネーションを宣言できないため、他のファンクターとまったく同じ方法で
Id
を入力することはできませんが、可能であれば、これを記述します。
-- warning: - instance Functor Id where fmap ∷ (α → β) → (Id α → Id β) fmap f = f
つまり、射手のこのファンクターの動作は、 fmap ≡ id ∷ (α → β) → (α → β)
-ここにはコンストラクター
Id
はありませんが、これは同じシグネチャです。 したがって、2つのファンクターマッピングは次のようになります。
α ∷ ∗ ↦ Id α = α ∷ ∗ f ∷ α → β ↦ id f = f ∷ α → β
このファンクタは役に立たないように見えますが、カテゴリの定義を完了するために必要です。 さらに、任意のタイプの
α ∷ ∗
を
α ∷ ∗
として、つまり同一のファンクターの作用下で認識することを可能にします。これは、モナドと出会うときに依然として有用です。
ファンクターの構成
私たちが検討しているカテゴリーでは、射(内ファンクター)の構成だけが欠けています。 1つのオブジェクトの射を考慮するため、それらはすべて同じ領域と共領域を持っているため、合成は任意の2つの射に適用できることをアプリオリに知っています。
例から始めましょう。 私たちに知られている2つのファンクター、
Maybe
と
[]
を考えてください。 オブジェクトに対する動作は次のとおりです。
α ∷ ∗ ↦ Maybe α ∷ ∗ β ∷ ∗ ↦ [β] ∷ ∗
構成とは、最初に1つのマッピングを適用し、結果に2番目のマッピングを適用することを意味します。
α ↦ Maybe α ↦ [Maybe α]
または別の順序で:
α ↦ [α] ↦ Maybe [α]
タイプコンストラクターに構成を適用するために、特別なものは何も必要ありません-最初に1つ、次にもう1つを書くだけです。 この操作のために、通常の構成に似た表記法を導入します(演算子の形式の型コンストラクターはコロンで始める必要があり、2番目は対称性のために置きます)。
type (φ :.: ψ) α = φ (ψ α)
次に、射のマッピングで何が起こるか見てみましょう。 このファンクターの名前はすべてのファンクターに対して
fmap
ため、一般に、1つの
fmap
をモルフィズムに適用する必要があり、2番目のファンクター、つまりファンクターの構成のマッピングは
λf → fmap (fmap f)
または
fmap . fmap
fmap . fmap
これら2つの
fmap
(1つの多態性関数の異なる仮説)には、それぞれ独自の型と対応する定義があることを理解する必要があります。 これを明確に示しましょう。
instance Functor Maybe where -- fmap ∷ (α → β) → (Maybe α → Maybe β) fmap f = f' where f' Nothing = Nothing f' (Just x) = Just (fx) instance Functor [] where -- fmap ∷ (α → β) → ([α] → [β]) fmap g = g' where g' [] = [] g' (x:xs) = (gx) : (g' xs)
次に例を使用して構成を検討し
[] :.: Maybe
(fmap . fmap) ∷ (α → β) → ([Maybe α] → [Maybe β]) (fmap . fmap) even [Just 2, Nothing, Just 3] ≡ [Just True, Nothing, Just False]
そして別の順序で:
Maybe :.: []
(fmap . fmap) ∷ (α → β) → (Maybe [α] → Maybe [β]) (fmap . fmap) even Just [1, 2, 3] ≡ Just [False, True, False] (fmap . fmap) even Nothing ≡ Nothing
ファンクターの構成を
Functor
クラスの具体化にしたい場合、 TypeComposeパッケージの作成者が行うのとほぼ同じ方法で行います 。 ただし、値を追加のデータコンストラクターでラップする必要があるため、これにはあまり意味がありません。 したがって、ファンクタをマップのペアとして(オブジェクトと射影上で)話す場合、2つのファンクタ
(φ, fmap
)
および
(ψ, fmap
)
場合、その構成はマップのペアになります:
(φ :.: ψ, fmap
)
、すなわち
α ∷ ∗ ↦ (φ :.: ψ) α ∷ ∗
f ∷ α → β ↦ (fmap
) f ∷ (φ :.: ψ) α → (φ :.: ψ) β
正式には、このマッピングのペア自体がHaskカテゴリの内部機能であることを確認する必要があります。つまり、単一のモーフィズムとモーフィズムの構成を保持しますが、1つの
fmap
が保存されるため、2つの連続して適用される
fmap
が保存されます:
(fmap . fmap) id ≡ fmap (fmap id) ≡ fmap id ≡ id (fmap . fmap) (f . g) ≡ ≡ fmap (fmap (f . g)) ≡ ≡ fmap ((fmap f) . (fmap g)) ≡ ≡ (fmap (fmap f)) . (fmap (fmap g)) ≡ ≡ ((fmap . fmap) f) . ((fmap . fmap) g)
証明するために必要でした。 ファンクターと同一のファンクターの構成は、自然でシンプルな構造ですが、それでも有用ですが、モナドに到達したときに再び必要になります。
そのため、Haskオブジェクトと内包関数を射として使用してカテゴリを構築するには、2つの公理をチェックする必要があります。
- 連想性の構成
((φ :.: ψ) :.: ω) α ≡ (φ :.: ψ) (ω α) ≡ ≡ φ (ψ (ω α)) ≡ ≡ φ ((ψ :.: ω) α) ≡ (φ :.: (ψ :.: ω)) α
(fmap
)
は普通の構成であり、前回その結合性をテストしました。 - 単射の中立性
(φ :.: Id) α ≡ φ (Id α) ≡ φ α ≡ Id (φ α) ≡ (Id :.: φ) α fmap . id ≡ id ≡ id . fmap
モノイド構造
実際、上記のカテゴリはモノイドであるため、モノイドとの知り合いはすでに発生しています。 それに関する冗長な情報を除外し、定義のみを残しましょう。
モノイドは単一のオブジェクトカテゴリです
とても簡単です。 この言葉遣いから、「モノ」とは「1」という意味の名前がすぐに明らかになります。
カテゴリーモノイドのもう1つの例を示します。 1つの
Int
持つHaskカテゴリのサブカテゴリを考えます。 射として、次の形式の関数をとります
λn → n + k
または各
k ∷ Int
(+k)
(+100500)
、すなわち
(+(-1))
、
(+7)
、
(+100500)
(+7)
(+100500)
など 通常の構成と
(+0) ≡ id
とともに、カテゴリが取得されます。 次のように、数値行の
Int
型のすべての値と、これらの射の影響を想像できます。
Int: … -5 -4 -3 -2 -1 0 1 … (+(-2)) ∷ ↑ ↑ ↑ ↑ ↑ ↑ ↑ ↑ Int: … -3 -2 -1 0 1 2 3 … (+3) ∷ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ Int: … 0 1 2 3 4 5 6 …
つまり、視覚的には、これらは指定された数の位置による原点( 0
)のシフトです。ただし、一般的に数値の線は同じままです。 カテゴリの構造を提供するもの:最初に、すべてを所定の場所に残す単一のモーフィズムがあります。次に、モーフィズムの構成があります。それは連想的であり、
id
中立です。
m
と
n
が2つの整数の場合、構成は次のとおりです。
つまり、実際には合計として機能します。 各モルフィズム(+m) . (+n) ≡ (+(m+n))
(+m)
を単純に数値
m
(原点を移動した場所:
(+m) 0 ≡ m
と考えることができ、「組成」
m
および
n
は実際にはそれらの合計です:
(m+n)
、そして、単一の射は単純にゼロです。
このビューは、モノイドの集合論的定義に対応しています。
モノイドは、セット、このセットでの連想バイナリ操作、およびこの操作に関して中立な要素で構成されるトリプルです。
この例では、これはそのようなトリプルです:
(Int, (+), 0)
。
(l + m) + n ≡ l + (m + n) m + 0 ≡ m ≡ 0 + m
この定義では、カテゴリの公理は単純に再定式化されます。集合は射の集合であり、連想二項演算は射の合成であり、中立要素は単一の射です。 モノイドの構造をどう見ても、主なことは二項演算と中立要素の存在であるということを理解することが重要です。
Haskellの
Monoid
クラスを見てみましょう。
class Monoid μ where mappend ∷ μ → μ → μ mempty ∷ μ
ここで、 μ
は検討中のセット、
mappend
は連想的である必要がある操作、
mempty
は
mappend
に関して中立でなければならない要素です。 トリプル
(Int, (+), 0)
、このクラス
(Int, (+), 0)
実施形態は直接になります。
instance Monoid Int where mappend = (+) mempty = 0
同様に、他のトリプル、たとえば
(Float, (*), 1)
または
([α], (++), [])
または
(Bool, (||), False)
、ここで
||
-論理的な「または」。 しかし、再びカテゴリ間の関係を示すカテゴリ定義を実現できます。
type Mor α = α → α instance Monoid (Mor α) where mappend = (.) mempty = id
この場合、タイプα ∷ ∗
はHaskカテゴリーのオブジェクトであり、タイプ
Mor α
関数はこのオブジェクトの射である。 通常の構成と単一のモルフィズム
id
とともに、1つのオブジェクト、つまりモノイドでカテゴリを形成します。
Data.Monoid
モジュールの
Endo
タイプにも同様のインカネーションがあります。 ちなみに、このモジュールのほとんどすべての化身は、「ラッパー」の型用に作成されています。 これは、同じセットでモノイド構造をさまざまな方法で導入できるためです。 たとえば、
Int
型の場合、考慮される構造
(Int, (+), 0)
(
Sum
参照)に加えて、
(Int, (*), 1)
(
Product
参照)、および
Bool
型の場合、前述の
(Bool, (||), False)
(
Any
参照)は
(Bool, (&&), True)
(
All
参照)です。
そのようなトリプルモノイドの多くの例は、Habrの記事「 Monoids and their Applications:Monoidal Computing in Trees」にあります。 さらに、Haskellでのモノイドの使用については、ジャーナルFunctional Programming Practiceの 翻訳記事を読むことができます。 ええと、写真がなくて本当に悲しい場合は、 「Haskell for Great Good」教科書のモノイドに関する章の翻訳があり ます!
おわりに
読者には、これらのモノイドのさまざまな定義ですべてを逆さまにしたように見えるかもしれませんが、構造の重要性ではなく、その上に構築されたものではなく、セットの要素、カテゴリのモーフィズム、または他の何かに重要であることを示したかっただけです。
実際、この理論全体を通して、さまざまな種類の矢印(マッピング、変換)について話しています。 単一の射を持つオブジェクトを識別できますが、それ以上の構成ではオブジェクトについて詳しく話すことはできません(指定として単一の射のインデックス付けのみが必要であり、射の領域と共領域は構図の正しい構成にのみ必要です)。
オブジェクト間の矢印としての射についてはすでに知っています。カテゴリ間の矢印のようなファンクターについては知っています。 そして、ファンクター間の矢印の性質は何ですか? これらの矢印は自然変換と呼ばれ、次回それらについて説明します。
この記事を要約するために、検討した主な概念をもう一度繰り返し、それらをリンクさせたいと思います。
Haskカテゴリの内関手と関手の構成および同一の関手はモノイド構造を持っています
このキーフレーズは、ファンクターと自然な変換から構築する(厳密に)モノイダルカテゴリに関して非常に有用です。