

[データ型の1番目の要素にアクセスするにはどうすればいいんでしょうか？](https://ja.stackoverflow.com/questions/69956/%e3%83%87%e3%83%bc%e3%82%bf%e5%9e%8b%e3%81%ae1%e7%95%aa%e7%9b%ae%e3%81%ae%e8%a6%81%e7%b4%a0%e3%81%ab%e3%82%a2%e3%82%af%e3%82%bb%e3%82%b9%e3%81%99%e3%82%8b%e3%81%ab%e3%81%af%e3%81%a9%e3%81%86%e3%81%99%e3%82%8c%e3%81%b0%e3%81%84%e3%81%84%e3%82%93%e3%81%a7%e3%81%97%e3%82%87%e3%81%86%e3%81%8b)
で質問したとおり、Haskellではデータを取り出すときにパターンマッチによる方法しか取り出し方がない場合があるようです。

例（上記リンク回答より）:

[code]

    data Shape = Rect Double Double
    getX (Rect x y) = x
    getX (Rect 1.0 2.0)
    
[/code]

さてHakellは圏論に基づいて言語が作られているようですが、このパターンマッチングは圏論でいうとどこの部分なのでしょうか？

**質問の背景**  
Haskellの勉強は昔からすこしやっては、よくわからなくなり、このパターンマッチングがすんなり理解できたのもSwiftのOptionalでアンラップを体験したことからでした。

[SwiftのOptionalのアンラップの解説](https://qiita.com/maiki055/items/b24378a3707bd35a31a8)

こうなってくると、このデータの取り出し方（アンラップ）はよく使われる言語として、HaskellだけではなくてSwiftでもよく目にするようになってきており、メジャーなプログラム的技法ということになってきたのではないかと考えております。またこの技法（アンラップ）はSwfitだけでなくKotlinにもあります。

（私は勝手にこのアンラップとパターンマッチングの方法が似ているなと思っているのですが、Swiftはおそらく圏論の影響はうけていないので、もしかしたら全く違うものなのかもしれません。というかオプショナルとパターンマッチングはあんまり関係なくてどっちかというと
Swiftの[付属型enumのswitch内のデータの取り出し方](https://ja.stackoverflow.com/questions/69968/swift%E3%81%AE%E4%BB%98%E5%B1%9E%E5%9E%8Benum%E3%81%8B%E3%82%89%E3%83%87%E3%83%BC%E3%82%BF%E3%82%92%E5%8F%96%E3%82%8A%E5%87%BA%E3%81%99%E3%82%88%E3%81%86%E3%81%AA%E3%82%B3%E3%83%BC%E3%83%89%E3%82%92haskell%E3%81%A7%E6%9B%B8%E3%81%8D%E3%81%9F%E3%81%84)のほうがパターンマッチぽいかも）

検索してみると [圏論プログラミング言語 CPL - Ryusei’s Notes (a.k.a.
M59のブログ)](https://mandel59.hateblo.jp/entry/2015/02/02/110621) に

> それでは、仲介射は何に対応するのか。HaskellのEither型には、either :: (a -> c) -> (b -> c) -> Either
> a b -> cという関数が定義されていますけど、これがCPLのcoprodのcaseに相当します。f : A -> C, g : B ->
> Cのとき、case(f,g) : coprod(A,B) -> Cです。

とあり、CPLを今はじめてみたので、なんとも判断つかないのですが、パターンマッチングによるデータの取り出しはこの `仲介射`
なのかと疑問に思ったところです。

そもそも圏論についての勉強が基礎から必要になってくるかもしれないのですが、このパターンマッチングによるデータ取り出しがそもそも圏論と関係ないのであれば、いま知りたいこととは関係なくなってしまうので、まず関係あるのかどうか知りたいという感じです。

ちょっとアンラップとパターンマッチングの２つの技法が登場しているので、どっちに焦点をあてたらよいのか自分でもよくわからなくなってきましたが（今回のHaskellの例だとデータを取り出す（アンラップ）のためにパターンマッチングを使わざるをえない）なんとなくパターンマッチングのほうが圏論ぽい気がしていますので、パターンマッチングが圏論に関係があるのかどうかを聞くことしたほうが良い気がしています。


CC BY-SA 4.0
2020-09-02T00:33:02.773
70062
2021-12-13T13:55:20.247
2020-09-02T01:05:33.327
9008
9008
question
haskell
パターンマッチングは圏論における何ですか？　仲介射ですか？　それともプログラム固有の考え方ですか？


既に自己解決しているかもしれませんが参考までに。

パターンマッチングに対する圏論的な解釈は、強引ではあるかもしれませんが確かに可能であるため、全く関係がないというわけではないと思います。  
少なくとも圏論的に意味のある操作さえあれば、基本的にそれらの組み合わせによって代数的データ型として定義された型の値から、型の構成に用いたよりプリミティブな型の要素を取り出すことはできます。  
(もっと一般的で一律な解釈の仕方があるのかといわれると、そこまでは私も専門ではないためわかりません。)

まず `undefined` や unsafe 関数たちの存在を無視した理想的な状況下において、Haskell の型とその間の関数全体は、1つの圏 Hask
を成します。  
ここで a,b をその圏の任意の対象としたとき、その圏にはそれら a,b の余積対象 ( _coproduct object_ ) や積対象 (
_product object_ )
と呼ばれる圏論的に特別な意味を持った対象たちが常に存在します。(そうなるように言語が設計されているといった方が正しいかもしれないです。また余積は和 (
_sum_ ) とも呼ばれます。)

それら概念の具体的な定義や意味の解説は nLab や 圏論の教科書に任せますが、結論から言うと

  * Hask における余積

    * 余積対象 a+b  
`Either a b`

    * 入射 i1:a→a+b  
`Left::a->Either a b`

    * 入射 i2:b→a+b  
`Right::b->Either a b`

    * **余積対象の** 仲介射 [h,k]:a+b→c  
`either h k`

  * Hask における積

    * 積対象 a×b  
`(a, b)`

    * 射影 p1:a×b→a  
`fst::(a,b)->a`

    * 射影 p2:a×b→b  
`snd::(a,b)->b`

    * **積対象の** 仲介射 <h,k>:c→a×b  
`(((<*>) . fmap (,))::((c -> a) -> (c -> b) -> (c -> (a, b)))) h k`

といったような対応関係になっています。

さて今示したいのは、代数的データ型として定義された型の要素から内部データを抽出することが純粋な圏論的な操作 (つまり対象の内部表現に言及しない圏 Hask
の持つ「すべての有限余積と有限積を持つ」や「カルテシアン閉である」といった圏論的なプロパティだけを用いた演繹)
のみで完結できるのかということですが、例えばリンク先の例をそのまま挙げさせていただくと

[code]

    Shape = Rect Double Double | Tri Double Double
    
[/code]

という代数的データ型は、圏論的には 積対象 Double×Double 同士の余積対象 (Double×Double)+(Double×Double)
であり、  
`Rect` と `Tri` がそれぞれ余積対象への入射
i1:(Double×Double)→(Double×Double)+(Double×Double),
i2:(Double×Double)→(Double×Double)+(Double×Double)
に対応しているというように解釈されます。この時、その対象の要素

x:1→((Double×Double)+(Double×Double))

について考えてみると、まずその余積対象の要素に掛かっている入射は、この例の場合幸いにも同じ対象 (Double×Double)
同士の余積であるため、畳み込み ( _folding_ )
[id,id]:((Double×Double)+(Double×Double))→(Double×Double) (Haskell
の関数を使って表現すると `either id id`) をかけることで

(x⨟[id,id]):1→(Double×Double)

として余積対象の要素の数値の対に掛かっている入射を取り外すことができます。  
あとはこの積対象 (Double×Double) の要素に対して射影をとってあげれば

(x⨟[id,id]⨟p1):1→Double  
(x⨟[id,id]⨟p2):1→Double

といったように対象の内部構成に言及することなく、欲しかった2つの数値データが圏論的な操作のみによって得られます。

もし余積対象が a+b というように2つの異なる対象 a,b
からなる場合は、先程のようにして入射をそのまま取り外すことは原理的にできませんが、一方の型の値を処理する関数 f:b→a を用意することで

[id,f]:a+b→a

というような「型aの値に掛かっている入射は取り外して、入射がかかっている型bの値については別途用意しておいた f
で型aの値へと変換する関数」というもので代替ができます。

ちなみにそのリンク先で説明されている図形の面積を求める関数 ((Double×Double)+(Double×Double))→Double
を考える場合では、敢えて内部データを取り出してそれらに言及しながら関数を定義せずとも、三角形用の関数 (Double×Double)→Double
と四角形用の関数 (Double×Double)→Double から構成される **余積対象の** 仲介射 [uncurry(*),
uncurry(*)⨟(/2)] として直接定義することもできます。  
Haskell の関数を使って表現し直すとこの射は

[code]

    either (uncurry (*)) ((/2) . uncurry (*))
    
[/code]

となりますが、実際以下を実行してみるとわかるように正しく動作してくれます。

[code]

    either (uncurry (*)) ((/2) . uncurry (*)) $ Left (3,4) -- 12.0
    either (uncurry (*)) ((/2) . uncurry (*)) $ Right (3,4) -- 6.0
    
[/code]

[Wiki](https://wiki.haskell.org/Algebraic_data_type)
に書いてあるように代数的データ型は基本的に余積と積との組み合わせとして構成されるため、この例に限らず同様にして圏論的に内部データを取り扱うことができます。実際例えば
1 を終対象つまり Hask における型 `()` とすると

[code]

    data Bool = True | False -- 1+1
    data Maybe a = Nothing | Just a -- 1+a
    data Foo = Blah1 | Blah2 Double | Blah3 -- (1+Double)+1
    
[/code]

というようにこういった代数的データ型もしっかりと全て圏論的に構成された対象として解釈できます。

本題に戻ると、この質問のパターンマッチングの場合、`Shape`型というのは

[code]

    data Shape = Rect Double Double
    
[/code]

というよう定義されていますが、これは圏論的には Double と Double の単なる積対象 (Double×Double) と理解でき、「right
object」である積対象に対してリンク先の「left object の仲介射 =
パターンマッチによる分岐」という話はこの場面においてはあまり関係はなく、積対象の伴う2つの射影 `fst::(Double,
Double)->Double`, `snd::(Double, Double)->Double` の内の第一射影 `fst`
によって、代数的データ型として定義された積対象 `(Double, Double)` の値を、1つの `Double`
の値へと還元しているという見方になるではないかと思います。

### 追記

仲介射の意味を、余積の普遍性を満たす圏で共通するコドメインを持つ任意の2つの射から引き起こされる射という意味だと誤解してしまった状態のまま早とちりな回答をしてしまいましたが、仲介射というのはもっと一般的な意味を持っていたため一部訂正させていただきました。

リンク先を改めてじっくり読ませていただきましたが、「圏Dのある対象から関手F:C→Dへの普遍射」あるいは「関手F:C→Dから圏D
のある対象への普遍射」の定義の中に登場する普遍射を通した分解を考える際に一意的に定まる射を一般に「仲介射」と呼んでいるようです。  
具体的には、「2つの共通するコドメインを持つ任意の射 h:a→c, k:b→c の対 <h,k>:<a,b>→Δ(c)」を「Hask×Hask の対象
<a,b> から 対角関手Δ:Hask→Hask×Hask への普遍射 <i1,i2>:<a,b>→Δ(a+b)
(2つの入射の対)」を通した合成の形に分解した際に一意に得られる射 [h,k]:a+b→c (<h,k>=<i1,i2>⨟Δ(f)と factor-
through される f) も確かに仲介射ではあるのですが、他にも

  * 任意の対象 a から終対象 1 を結ぶ一意的な射 !:a→1  
(Haskell の関数で表すと `const ()`)

  * 任意の共通するドメインを持つ2つの射 h:c→a,k:c→b から引き起こされる積対象 a×b への射 <h,k>:c→a×b

  * 任意の射 f:c×a→b から引き起こされる c からexponential 対象 b^a への射 λf:c→(b^a)  
(Haskell の関数で表すと `curry f`)

  * 任意の対象 a の要素 x と a の自己準同形 f:a→a から引き起こされる自然数対象 N から a への射 rec(x,f):N→a  
(N と `Int` は異なるため厳密には違いますが、疑似的に Haskell の関数で表すと `(curry ((!!).(uncurry.flip $
iterate))) x f`)

なども全て仲介射と呼ぶようです。

申し訳ありません。


CC BY-SA 4.0
2021-12-11T16:03:12.697
84068
2021-12-13T13:55:20.247
2021-12-13T13:55:20.247
49461
49461
70062
answer