1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

圏論を SQL と LINQ で説明してみる

1
Posted at

はじめに

Youtube をのぞいていたら、下記の動画が表示されたので観てみました。
圏とは?プログラマに人気と噂の圏について話します。 プログラマ向け圏論入門 1 - Youtube

数学的な説明だとよく分からなくなるので、知っているプログラムに変換してもらってから、数学的な説明に戻ると理解が深まるかなと、そういえば圏論とSQLは相性がいいと思っていたので、ChatGPTに「圏論をSQLで説明できますか」とお願いしてみました。

回答

むしろ圏論は「構造と合成」を扱うので、SQLの世界とかなり相性がいいです。無理に難しい用語で説明するより、対応関係で見ると一気に腑に落ちます。

圏論 ↔ SQL の対応イメージ

1. 対象(Object)= テーブル

圏論の「対象」は、SQLではテーブルだと考えられます。

Users(id, name)
Orders(id, user_id, total)

この場合

  • Users = 対象
  • Orders = 対象

2. 射(Morphisms)= 関係(JOIN可能な対応)

射は「対象から対象への写像」ですが、SQLでは外部キーやJOINの関係に相当します。

Orders.user_id → Users.id

これは Orders → Users という「射」

3. 合成(Composition)= JOINの連鎖

圏論の核心は「射は合成できる」ことです。

SQLだとまさにこれ

SELECT *
FROM Orders o
JOIN Users u ON o.user_id = u.id
JOIN Countries c ON u.country_id = c.id;

これは、Orders → Users → Countries という射の合成です。

圏論では、

結合法則
(f ∘ g) ∘ h = f ∘ (g ∘ h)

SQLでもJOINの順序を変えても意味は同じ(最適化は別)

4. 恒等射(Identity)= そのまま返すSELECT

圏論では、すべての対象に「何もしない射」があります。

SQLならこれ

SELECT * FROM Users;

Users → Users の恒等射

5. 関手(Functor)= クエリ(構造を保つ変換)

関手は「圏から圏への写像」で、構造(合成)を保ちます。

SQLでは

テーブル構造を変換するクエリ
SELECT id, name FROM Users;

これは

  • 元のUsersテーブル(圏A)
  • 投影されたテーブル(圏B)

への変換

重要なのは、JOIN構造(関係)を壊さない → 関手っぽい

6. 自然変換(Natural Transformation)= クエリの変換ルール

例えば

SELECT name FROM Users

SELECT UPPER(name) FROM Users

これは「関手間の変換」と見なせます。
データの構造は同じで、処理だけ違う。

直感的まとめ

圏論的にSQLを見ると

  • テーブル = モノ(対象)
  • 外部キー = 矢印(射)
  • JOIN = 矢印の合成
  • SELECT = 関手的な変換

つまり、SQLは「データの圏」に対する操作言語と見なせる。

  • 普通のSQLの理解:テーブルをJOINしてデータを取る
  • 圏論的な理解:関係(射)を合成して結果を得る

LINQでの圏論対応

1. SQL → LINQ → 圏論 の対応

まずは同じ処理を並べます。

SQL
SELECT u.name
FROM Users u
JOIN Orders o ON u.id = o.user_id
WHERE o.total > 100;
LINQ(C#)
var result =
    from u in Users
    from o in Orders
    where u.Id == o.UserId && o.Total > 100
    select u.Name;

2. これを圏論で見ると

このLINQ、実はこう分解できます。

C#
Users
  .SelectMany(u => Orders, (u, o) => new { u, o })
  .Where(x => x.u.Id == x.o.UserId && x.o.Total > 100)
  .Select(x => x.u.Name);

ここからが核心です。

3. モナドとしての構造

Select(写像)= Functor

C#
.Select(x => x.u.Name)

「中身だけ変える」
これは圏論の関手(Functor)

Where(フィルタ)= モナド的フィルタ

C#
.Where(...)

条件に合うものだけ残す

これは数学的には

  • Maybe
  • List

などのモナドの「絞り込み」

SelectMany(超重要)= bind(モナドの本体)

C#
.SelectMany(...)

これが本質です。
「入れ子構造を平坦化しながら結合する」
SQLのJOINそのもの

平坦化は後述

モナド則(Monad Laws)とは何か

モナドには3つのルールがあります。

① 左単位元則
return x >>= f  =  f x
② 右単位元則
m >>= return  =  m
③ 結合法則
(m >>= f) >>= g  =  m >>= (x => f(x) >>= g)

これをLINQで見る

LINQに置き換えると

  • return → new[] { x }(または単一要素の列)
  • >>= → SelectMany
① 左単位元則
new[] { x }.SelectMany(f)  ==  f(x)

1個だけのデータに対して処理するのと同じ

② 右単位元則
xs.SelectMany(x => new[] { x })  ==  xs

「何もしないJOIN」は元に戻る

③ 結合法則
(xs.SelectMany(f)).SelectMany(g)
==
xs.SelectMany(x => f(x).SelectMany(g))

JOINの順序を変えても意味が同じ

SQLとの対応(本質)

SQLのこれ

SQL
A JOIN B JOIN C

は実は2通りに解釈できます

(A JOIN B) JOIN C
A JOIN (B JOIN C)

結果は同じ(論理的には)
これが、モナドの結合法則

なぜSelectManyがあの形なのか

C#
.SelectMany(x => f(x))

これの意味は、xから新しい文脈(コレクション)を作る

Users.SelectMany(u => Orders)

これは実は、User → List<Order>という関数を使っている。

なぜSelectではダメなのか?

C#
Users.Select(u => Orders)

これだと、List<List<Order>> となり、入れ子になる。

SelectManyの役割

List<List<T>> → List<T>

平坦化(flatten)とは、入れ子になった構造を1段に潰すこと

平坦化の理解

直感的理解として、配列で見ると

C#
var data = new[]
{
    new[] { 1, 2 },
    new[] { 3, 4 }
};

これは、int[][]で、二重構造(入れ子)

平坦化(flatten)すると、一段になる

C#
[1, 2, 3, 4]

LINQで見ると

C#
data.SelectMany(x => x)

SQLで見ると

SELECT *
FROM Users u
JOIN Orders o ON u.Id = o.UserId;

JOINの結果は、1つのテーブル(平坦)
でも概念的には、UserごとにOrdersのリストがある。
それを潰している。

モナドはこの2つでできています。

map(Select) + flatten

型で見ると

map:
M<T> → (T → U) → M<U>

flatten:
M<M<T>> → M<T>

合わせると

bind(SelectMany):
M<T> → (T → M<U>) → M<U>

ここが圏論の核心

モナドの正体

bind = map + flatten

つまり

  • Select = map
  • SelectMany = map + flatten

4. モナドとして書くとこうなる

抽象的に書くと

M<T> という構造がある(ListやDB結果)

bind:
M<T> → (T → M<U>) → M<U>

SQL/LINQでは

Users: M<User>
Orders: M<Order>

User → Orders を使って結合
これが JOIN

5. 直感的な対応

概念 SQL LINQ 圏論
データ集合 テーブル IEnumerable 対象
関係 外部キー ラムダ
JOIN JOIN SelectMany bind
SELECT SELECT Select Functor
WHERE WHERE Where フィルタ

6. 一番重要な理解

普通の理解

JOINはテーブルをくっつける

圏論的理解

文脈(コンテナ)の中で関数を合成している

7. Maybeモナドとの対応

Maybeはモナドの中でも一番シンプルで、一言でいうなら 「値があるかもしれないし、ないかもしれない」型 です。
しかも、実務に直結します(NULL・Optionalの正体です)

Maybe = NULLを安全に扱う仕組み

Maybe<T> =
    Just T   (値あり)
  | Nothing  (値なし)

NULL

SQLのNULLは、Maybeモナド

C#
User? user = FindUser(id);
user?.Name

これは

Maybe<User> → Maybe<string>

Select(Functor)

JOIN

SQLのJOINには2種類あります

  • INNER JOIN
  • LEFT JOIN

これ、実は

  • INNER JOIN → Listモナド
  • LEFT JOIN → Maybeモナド(存在しない場合はNULL)

8. 圏論的まとめ

SQL/LINQの本質は、モナドの中で関数を合成している

もっとシンプルに言うと

値を直接操作しているのではない
文脈(List / DB / NULL)付きの値を操作している

結論

SQL・LINQ・圏論を一言でまとめると
データではなく“文脈付きデータ”の合成ルール

最後に

ChatGPTだと、どんどん回答を深掘りしていって理解が追いつかず、余計わけが分からなくなるので、一旦これくらいにしておきます。

1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?