web-dev-qa-db-ja.com

無料のモナドもジッパーで適用できますか?

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」です。)

24

はい、これは合法的なApplicativeのようです。おかしい!

@ JosephSibleが指摘する のように、identityホモモーフィズムおよびinterchange法則の定義からすぐに。トリッキーなのはcompositionの法則だけです。

_pure (.) <*> u <*> v <*> w = u <*> (v <*> w)
_

チェックするケースは8つあるので、ストラップで留めてください。

  • 3つのReturnsがある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.
      _
  • 3つのFrees: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で正式な証明を見たいと思っています(ただし、終了/陽性チェッカーがそれを台無しにするのではないかと思います)。

14

完全を期すために、私はこの回答を拡張して 上記の私のコメント を展開します。

私は実際に証明を書き留めませんでしたが、構成法の自由と返品が混在する訴訟は、パラメトリック性のために成立しなければならないと思います。 モノイドプレゼンテーション を使用すると簡単に表示できると思います。

ここでの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の回答 を参照してください。

4
duplode

Applicative の定義から:

fMonadでもある場合は、

  • pure = return

  • (<*>) = ap

  • (*>) = (>>)

したがって、この実装はそれを言う適用法則を破りますmustMonadインスタンスに同意します。

とはいえ、モナドインスタンスを持たないFreeMonadのnewtypeラッパーを作成できなかった理由はありませんが、上記の適用可能なインスタンスはありました

newtype Zip f a = Zip { runZip :: FreeMonad f a }
  deriving Functor

instance Applicative f => Applicative (Zip f) where -- ...
3
rampion