Applicative
の興味深い "zippy" Free
インスタンスを考え出したと思います。
data FreeMonad f a = Free (f (FreeMonad f a))
| Return a
instance Functor f => Functor (FreeMonad f) where
fmap f (Return x) = Return (f x)
fmap f (Free xs) = Free (fmap (fmap f) xs)
instance Applicative f => Applicative (FreeMonad f) where
pure = Return
Return f <*> xs = fmap f xs
fs <*> Return x = fmap ($x) fs
Free fs <*> Free xs = Free $ liftA2 (<*>) fs xs
それは一種のZip-longest戦略です。たとえば、data Pair r = Pair r r
ファンクタとして(そうFreeMonad Pair
は外部でラベル付けされたバイナリツリーです):
+---+---+ +---+---+ +-----+-----+
| | | | <*> | |
+--+--+ h x +--+--+ --> +--+--+ +--+--+
| | | | | | | |
f g y z f x g x h y h z
誰もこのインスタンスについてこれまで言及したことはありません。 Applicative
の法律に違反しますか? (もちろん、通常のMonad
インスタンスとは一致しません。これは、「zippy」ではなく「substitutiony」です。)
はい、これは合法的なApplicative
のようです。おかしい!
@ JosephSibleが指摘する のように、identity、ホモモーフィズムおよびinterchange法則の定義からすぐに。トリッキーなのはcompositionの法則だけです。
_pure (.) <*> u <*> v <*> w = u <*> (v <*> w)
_
チェックするケースは8つあるので、ストラップで留めてください。
Return
sがある1つのケース:pure (.) <*> Return f <*> Return g <*> Return z
(.)
_の結合性から自明に従います。Free
:が1つある3つのケースpure (.) <*> Free u <*> Return g <*> Return z
Free u <*> (Return g <*> Return z)
から逆方向に作業するとfmap (\f -> f (g z)) (Free u)
が得られるので、これはファンクタの法則に従います。_pure (.) <*> Return f <*> Free v <*> Return z
fmap ($z) $ fmap f (Free v)
fmap (\g -> f (g z)) (Free v) -- functor law
fmap (f . ($z)) (Free v)
fmap f (fmap ($z) (Free v)) -- functor law
Return f <$> (Free v <*> Return z) -- RHS of `<*>` (first and second cases)
QED
_
pure (.) <*> Return f <*> Return g <*> Free w
fmap (f . g) (Free w)
に還元されるため、ファンクタの法則に従います。Return
:が1つある3つのケース_pure (.) <*> Return f <*> Free v <*> Free w
Free $ fmap (<*>) (fmap (fmap (f.)) v) <*> w
Free $ fmap (\y z -> fmap (f.) y <*> z) v <*> w -- functor law
Free $ fmap (\y z -> fmap (.) <*> Return f <*> y <*> z) v <*> w -- definition of fmap, twice
Free $ fmap (\y z -> Return f <*> (y <*> z)) v <*> w -- composition
Free $ fmap (\y z -> fmap f (y <*> z)) v <*> w -- RHS of fmap, definition of liftA2
Free $ fmap (fmap f) $ fmap (<*>) v <*> w -- functor law, eta reduce
fmap f $ Free $ liftA2 (<*>) v w -- RHS of fmap
Return f <*> Free v <*> Free w -- RHS of <*>
QED.
_
_pure (.) <*> Free u <*> Return g <*> Free w
Free ((fmap (fmap ($g))) (fmap (fmap (.)) u)) <*> Free w
Free (fmap (fmap (\f -> f . g) u)) <*> Free w -- functor law, twice
Free $ fmap (<*>) (fmap (fmap (\f -> f . g)) u) <*> w
Free $ fmap (\x z -> fmap (\f -> f . g) x <*> z) u <*> w -- functor law
Free $ fmap (\x z -> pure (.) <*> x <*> Return g <*> z) u <*> w
Free $ fmap (\x z -> x <*> (Return g <*> z)) u <*> w -- composition
Free $ fmap (<*>) u <*> fmap (Return g <*>) w -- https://Gist.github.com/benjamin-hodgson/5b36259986055d32adea56d0a7fa688f
Free u <*> fmap g w -- RHS of <*> and fmap
Free u <*> (Return g <*> w)
QED.
_
_pure (.) <*> Free u <*> Free v <*> Return z
Free (fmap (<*>) (fmap (fmap (.)) u) <*> v) <*> Return z
Free (fmap (\x y -> fmap (.) x <*> y) u <*> v) <*> Return z -- functor law
Free $ fmap (fmap ($z)) (fmap (\x y -> fmap (.) x <*> y) u <*> v)
Free $ liftA2 (\x y -> (fmap ($z)) (fmap (.) x <*> y)) u v -- see Lemma, with f = fmap ($z) and g x y = fmap (.) x <*> y
Free $ liftA2 (\x y -> fmap (.) x <*> y <*> Return z) u v -- interchange
Free $ liftA2 (\x y -> x <*> (y <*> Return z)) u v -- composition
Free $ liftA2 (\f g -> f <*> fmap ($z) g) u v -- interchange
Free $ fmap (<*>) u <*> (fmap (fmap ($z)) v) -- https://Gist.github.com/benjamin-hodgson/5b36259986055d32adea56d0a7fa688f
Free u <*> Free (fmap (fmap ($z)) v)
Free u <*> (Free v <*> Return z)
QED.
_
Free
s:pure (.) <*> Free u <*> Free v <*> Free w
<*>
_のFree
/Free
ケースのみを実行し、その右側は Compose
の_<*>
_のケースと同じです。したがって、これはCompose
のインスタンスの正当性に基づいています。pure (.) <*> Free u <*> Free v <*> Return z
の場合、補題を使用しました:
補題:fmap f (fmap g u <*> v) = liftA2 (\x y -> f (g x y)) u v
。
_fmap f (fmap g u <*> v)
pure (.) <*> pure f <*> fmap g u <*> v -- composition
fmap (f .) (fmap g u) <*> v -- homomorphism
fmap ((f .) . g) u <*> v -- functor law
liftA2 (\x y -> f (g x y)) u v -- eta expand
QED.
_
誘導仮説のもとでは、さまざまな関数と適用法則を使用しています。
これは証明するのがとても楽しかったです!私はCoqまたはAgdaで正式な証明を見たいと思っています(ただし、終了/陽性チェッカーがそれを台無しにするのではないかと思います)。
完全を期すために、私はこの回答を拡張して 上記の私のコメント を展開します。
私は実際に証明を書き留めませんでしたが、構成法の自由と返品が混在する訴訟は、パラメトリック性のために成立しなければならないと思います。 モノイドプレゼンテーション を使用すると簡単に表示できると思います。
ここでのApplicative
インスタンスのモノイド表示は次のとおりです。
_unit = Return ()
Return x *&* v = (x,) <$> v
u *&* Return y = (,y) <$> u
-- I will also piggyback on the `Compose` applicative, as suggested above.
Free u *&* Free v = Free (getCompose (Compose u *&* Compose v))
_
モノイドの表示では、合成/結合の法則は次のとおりです。
_(u *&* v) *&* w ~ u *&* (v *&* w)
_
次に、その混合ケースの1つを考えてみましょう。たとえば、Free
-Return
-Free
1つ:
_(Free fu *&* Return y) *&* Free fw ~ Free fu *&* (Return y *&* Free fw)
(Free fu *&* Return y) *&* Free fw -- LHS
((,y) <$> Free fu) *&* Free fw
Free fu *&* (Return y *&* Free fw) -- RHS
Free fu *&* ((y,) <$> Free fw)
_
この左側を詳しく見てみましょう。 _(,y) <$> Free fu
_は、_(,y) :: a -> (a, b)
_にあるa
値に_Free fu :: FreeMonad f a
_を適用します。パラメトリック性(より具体的には、_(*&*)
_の無料定理)は、_(*&*)
_を使用する前または後にそれを行うかどうかは関係ないことを意味します。つまり、左側は次のようになります。
_first (,y) <$> (Free fu *&* Free fw)
_
同様に、右側は次のようになります。
_second (y,) <$> (Free fu *&* Free fw)
_
first (,y) :: (a, c) -> ((a, b), c)
とsecond (y,) :: (a, c) -> (a, (b, c))
はペアの再関連付けまで同じなので、次のようになります。
_first (,y) <$> (Free fu *&* Free fw) ~ second (y,) <$> (Free fu *&* Free fw)
-- LHS ~ RHS
_
他の混合ケースも同様に処理できます。残りの証明については、 Benjamin Hodgsonの回答 を参照してください。
Applicative
の定義から:
f
がMonad
でもある場合は、
pure
=return
(<*>)
=ap
(*>)
=(>>)
したがって、この実装はそれを言う適用法則を破りますmustMonad
インスタンスに同意します。
とはいえ、モナドインスタンスを持たないFreeMonad
のnewtypeラッパーを作成できなかった理由はありませんが、上記の適用可能なインスタンスはありました
newtype Zip f a = Zip { runZip :: FreeMonad f a }
deriving Functor
instance Applicative f => Applicative (Zip f) where -- ...