多項式Functor
半年ぐらい前から、
newtype F x = F (M, E -> x)
の形をしたApplicative Functorのすべてを決めるような
M
とE
上の代数的構造を考えていました。
最近この問題が自分の中で再燃し、いろいろ考えたところ、 一般の多項式Functorを考えたほうが自然であるという結論に達しました。
一般の多項式Functor F
を、ここでは次のように定義します:
type M :: Type
type E :: M -> Type -- Dependent Type!
type F :: Type -> Type
newtype F a = F (Σ(x :: M). E x -> a)
instance Functor F where
fmap f (F (x, g)) = F (x, f . g)
ここで Σ(x :: M). E x -> a
はDependent sum typeです。
この型をもつ値は、対(x, f)
であって、x :: M
かつ f :: E x -> a
となるようなものです。
f
の型が値であるx
に依存するので、このような型はHaskellでは(自然には)書くことができませんが、
拡張を駆使していろいろ工夫すると不可能ではないです。
このような型は、多項式Functorを表現します。
例を挙げるなら、M
がちょうど2つの値M0, M1
をもち、
E M0 = Void, E M1 = ()
とすれば、F
はMaybe
と同型になります。
data M = M0 | M1
type E :: M -> Type
type family E x where
E M0 = Void
E M1 = ()
toF :: forall a. Maybe a -> F a
Nothing = F (M0, absurd) -- absurd :: Void -> a ~ E M0 -> a
toF Just x) = F (M1, const a) -- const a :: () -> a ~ E M1 -> a
toF (
fromF :: forall a. F a -> Maybe a
F (M0, _)) = Nothing
fromF (F (M1, f)) = Just (f ()) fromF (
また、M
が自然数で、E m
が『m
より小さい自然数』の型であるようにすると、
E m
はちょうどm
個の異なる値をとる型で、F
は(有限)リスト関手です。
<-> F(0, absurd) -- E 0 ~ Void
[] <-> F(1, \0 -> a)
[a] <-> F(2, \case {0 -> a, 1 -> b})
[a,b] <-> F(3, \case {0 -> a, 1 -> b, 2 -> c})
[a,b,c] :
:
このように定義されたF
がApplicative
になるためには、
M
とE x
上にどのような代数構造が必要か?を次節から考えます。
多項式Applicative Functorの特徴づけ
どんなApplicativeに対しても、モノイドAp f () ~ f ()
を考えられます。
(cf. Data.Monoid.Ap)
ApplicativeであるF
が多項式Functorでもあって、上のような表現がされていれば、Ap F ()
とM
の間に全単射
prj :: F () -> M
F (x, _)) = x
prj (
inj :: M -> F ()
= F (x, const ()) inj x
があります。この全単射でAp F ()
上のモノイド演算をM
上に写せば、
M
はモノイド演算(mempty, <>)
を持ちます。このモノイド演算を前提として、
Applicative F
の実装がどうなっているか考えれば、
parametricityから
pure :: forall a. a -> F a
pure a = F (mempty :: M, const a)
および
liftA2 :: forall a b c. (a -> b -> c) -> F a -> F b -> F c
F (x, x_a)) (F (y, y_b)) = F (z, z_c)
liftA2 abc (where
= x <> y
z z_c :: E z -> c
= abc (x_a (_left i)) (y_b (_right i))
z_c i
_left :: E z -> E x
_right :: E z -> E y
である必要があります。ただし、_left
と_right
は、まだ正体のわかっていない関数です。
天下り的ですが、次の型をもつ関数phi
を考えます。
phi :: Π((x,y,z) :: (M,M,M)). E (x <> y <> z) -> E y
ここでΠ
はDependent productです。
_left, _right
がphi
を使って次のように定義されているとします。
-- 以下に注意:
-- mempty <> x <> y = x <> y = z
-- x <> y <> mempty = x <> y = z
_left :: E z -> E x
= phi (mempty, x, y)
_left
_right :: E z -> E y
= phi (x, y, mempty) _right
このとき、phi
が次の等式を満たすならば、F
のApplicative則が従います。(証明略)
phi (mempty, y, mempty) = id
phi (x, y, z) . phi (x', x <> y <> z, z') = phi (x' <> x, y, z <> z')
逆に、各x, y :: M
の組み合わせにおける_left, _right
を、次の関数leftFactor, rightFactor
にまとめたとします。
leftFactor :: Πx. Πy. E (x <> y) -> E x
rightFactor :: Πx. Πy. E (x <> y) -> E y
このとき、関数phi
を以下のように定義できて、
phi :: Π(x,y,z). E (x <> y <> z) -> E y
= rightFactor x y . leftFactor (x <> y) z phi (x, y, z)
F
のApplicative則からphi
が上記の等式1,2を満たすことが従います。
証明
F
のApplicative則から
pure id <*> (F (y, y_b)) = fmap id (F (y, y_b)) = F (y, y_b)
= let _left = ...
_right = rightFactor mempty y :: E y -> E y
in F (y, \i -> (const id) (_left i) (y_b (_right i)))
= F (y, y_b . rightFactor mempty y)
y_b = y_b . rightFactor mempty y
const <$> F (x, x_a) <*> pure () = F (x, x_a)
= let _left = leftFactor x mempty :: E x -> E x
_right = ...
in F (x, \i -> const (x_a (_left i)) (const () (_right i)))
= F (x, x_a . leftFactor x mempty)
x_a = x_a . leftFactor x mempty
prod :: F a -> F b -> F (a,b)
prod = liftA2 (,)
fabc :: F ((a,b),c)
fabc
= (F (x, x_a) `prod` F (y, y_b)) `prod` F (z, z_c)
= let xy_x = leftFactor x y
xy_y = rightFactor x y
xyz_xy = leftFactor (x <> y) z
xyz_z = rightFactor (x <> y) z
xyz_a = x_a . xy_x . xyz_xy
xyz_b = y_b . xy_y . xyz_xy
xyz_c = z_c . xyz_z
in F (x <> y <> z, (xyz_a &&& xyz_b) &&& xyz_c)
fabc' :: F (a,(b,c))
fabc'
= F (x, x_a) `prod` (F (y, y_b) `prod` F (z, z_c)
= let xyz_x = leftFactor x (y <> z)
xyz_yz = rightFactor x (y <> z)
yz_y = leftFactor y z
yz_z = rightFactor y z
xyz_a = x_a . xyz_x
xyz_b = y_b . yz_y . xyz_yz
xyz_c = z_c . yz_z . xyz_yz
in F (x <> y <> z, xyz_a &&& (xyz_b && xyz_c))
(\((a,b),c) -> (a,(b,c))) <$> fabc = fabc'
より、5つの等式
(1a) rightFactor mempty y = id
(2a) leftFactor x mempty = id
(3a) leftFactor x y . leftFactor (x <> y) z = leftFactor x (y <> z)
(4a) rightFactor x y . leftFactor (x <> y) z = leftFactor y z . rightFactor x (y <> z)
(5a) rightFactor (x <> y) z = rightFactor y z . rightFactor x (y <> z)
が得られる。これらを用いると、
mempty, y, mempty)
phi (-- 定義
= rightFactor mempty y . leftFactor (mempty <> y) mempty
= rightFactor mempty y . leftFactor y mempty
-- 等式(1a)および(2a)
= id . id
= id
. phi (x', x <> y <> z, z')
phi (x, y, z) -- 定義
= rightFactor x y . leftFactor (x <> y) z .
<> y <> z) . leftFactor (x' <> x <> y <> z) z'
rightFactor x' (x -- 内側の2項に注目
= rightFactor x y .
<> y) z . rightFactor x' ((x <> y) <> z)) .
(leftFactor (x <> x <> y <> z) z'
leftFactor (x' -- 等式(4a)で()内を変形
= rightFactor x y .
<> y) . leftFactor (x' <> (x <> y)) z) .
(rightFactor x' (x <> x <> y <> z) z'
leftFactor (x' -- ()のくくりを変更
= (rightFactor x y . rightFactor x' (x <> y)) .
<> x <> y) z . leftFactor (x' <> x <> y <> z) z')
(leftFactor (x' -- 等式(5a) をはじめの()に、等式(3a)をうしろの()に適用
= rightFactor (x' <> x) y . leftFactor (x' <> x <> y) (z <> z')
-- 定義
= phi (x' <> x, y, z <> z')
よって、phi
の満たすべき等式1., 2.が示された。
phi
を関手として見る
前節のphi
の満たすべき法則、
phi (mempty, y, mempty) = id
phi (x, y, z) . phi (x', x <> y <> z, z') = phi (x' <> x, y, z <> z')
とてもFunctor
則に似ていますね?
モノイド M が適当に与えられているとき、圏 MFac を次のように定義します。 (Mの単位元は1で、Mの乗法は単に並べて書くことにします。)
MFacの対象はモノイドMの各要素
MFacの対象(モノイドMの要素)s, t に対して、射はMの要素の三つ組 (x, t, z) であって、s = xtz を満たすもの
MFac(s,t) = { (x, t, z) | s = xtz }
各対象 s に対して、 (1, s, 1) ∈ MFac(s,s) がidentity
(x, u, z) ∈ MFac(t,u) および (x’, t, z’) ∈ MFac(s,t) に対して、射の合成 ∘ が
(x, u, z)∘(x’, t, z’) = (x, u, z)∘(x’, xuz, z’) = (x’x, u, zz’) ∈ MFac(s,u)
phi
の満たす法則は、とりもなおさずE :: M -> Type
が 圏MFac から 圏Hask への関手で、
phi
がMFacの射 (x,y,z)∈MFac(xyz, y) を Haskの射E (x <> y <> z) -> E y
に移すfmap
であることを表しています。
※ちなみに、モノイド(対象が1つの圏と見ることもできる)の代わりに任意の圏で同じようなことをしたものが、Category of factorizationsやtwisted arrow categoryなどと呼ばれているらしいことまでは調べきれたのですが、以降力尽きました・・・
まとめ
多項式FunctorであるF
がM, E
によって次のように表示されているとき、
newtype F a = F (Σ(x :: M). E x -> a)
F上のApplicativeのインスタンスは、M, E
に関する次のデータで必ず表されます。
M
上のモノイド演算E
をこのモノイドM
についての 圏 MFac から Hask への関手にする方法(phi
関数)
例
Maybe
Maybe
の表示
data M = M0 | M1
instance Monoid M where
mempty = M1
M0 <> _ = M0
M1 <> y = y
type family E (x :: M) where
E M0 = Void
E M1 = ()
に対して、圏MFacは
MFac(M1, M1) = { (M1, M1, M1) }
MFac(M1, M0) = {}
MFac(M0, M1) = { (M0, M1, M1), (M1, M1, M0), (M0, M1, M0) }
MFac(M0, M0) = { (*, M0, *) }
phi
が(全く面白くないですが)
phi (M1, M1, M1) = id
phi (M0, _, _) = absurd
phi (_, M0, _) = absurd
phi (_, _, M0) = absurd
となります。
List
有限リストの表示
data Nat = Z | S Nat
data E (x :: Nat) where
EZ :: E (S x)
ES :: E x -> E (S x)
newtype List a = List (Σ(x :: Nat). E x -> a)
に対して、[]
と同じApplicativeを考えると、
Nat
に入るモノイド演算は、自然数としての乗算によるものです。
instance Num Nat where
fromInteger = ...
Z + y = y
S x + y = S (x + y)
Z * _ = Z
S x * y = y + (x * y)
instance Monoid Nat where
mempty = S Z
(<>) = (*)
圏MFacは
MFac(s,t) = { (x,t,y) | x :: Nat, y :: Nat, s == x * t * y }
phi
は次のようになります:
phi :: Π(x,y,z). E (x*y*z) -> E y
| x*y*z == 0
phi (x,y,z) i = case i of { }
-- E (x*y*z) = E 0 is an empty type
| x*y*z > 0
phi (x,y,z) i = (i `div` z) `mod` y
モノイドより強い条件
M
がモノイドであるだけでなくより強い条件を満たす、例えば群でもあるような場合はどうなるでしょうか?
群G
に対する圏GFacは、任意の射に対して逆射がある圏、groupoidになります。
さらに、任意の2対象(= 群Gの任意の2元)の間に射があります。E
がこれからの関手であるため、
どのE x, E y
の間にも全単射があることがわかります。
これはつまり、E x
たちが有限個の値をとる型ならば、
それらは全て同じ個数でなければならないということです。
群Gの単位元1に対して、GFac(1,1)は群になりますが、特にこれは群G自身と同じものです。 実際、GFac(1,1)とGは1対1に対応します
GFac(1,1) = { (x,1,y) | x :: G, y :: G, 1 = x1y } = { (y^(-1), 1, y) | y :: G }
また、Gにおける単位元・合成・逆元がそのままGFac(1,1)におけるそれになります。
さらに、“標準”移動 (s,1,1) :: GFac(s,1) とその逆射 (s^(-1),s,1) :: GFac(1,s)によって、任意のGFac(s,t)とGFac(1,1)に 1対1対応がつけられます。
getPerm :: GFac(s,t) -> GFac(1,1)
getPerm (x,t,y) {- xty == s -}
= (t,1,1) ∘ (x,t,y) ∘ (s^(-1), s, 1)
= (s^(-1)xt, 1, y)
= (y^(-1), 1, y)
これにより、任意の関手E :: GFac -> Hask
は、
そのGFac(1,1)
上での挙動(= 群GFac(1,1)
のE 1
への作用 = G
-作用)および各s
における全単射phi (s,1,1) :: E s -> E 1
のみで定義されることになります。
まとめると、モノイドAp F ()
が群G
になるような多項式 Applicative F
は、
- Functorとして
(G, E -> a)
に自然同型 - どれも曖昧なく以下のデータで表現できる
tr, tr' :: G -> E -> E
,tr x
は全単射でtr' x
がその逆写像。- G-作用
act :: G -> E -> E
data F a = F G (E -> a)
phi :: (G, G, G) -> E -> E
= tr' t . act y . tr (x <> t <> y)
phi (x,t,y)
instance Applicative F where
pure a = F mempty (const a)
F x x_f <*> F y y_a = F xy (\i -> xy_f i (xy_a i))
where
= x <> y
xy = x_f . tr' x . act y . tr xy
xy_f = y_a . tr' y . tr xy xy_a