すべての代替モナドはフィルター可能ですか?


8

セットのカテゴリーは、デカルトのモノイドとコカルテシアのモノイドの両方です。これらの2つのモノイド構造を示す正準同型のタイプを以下に示します。

type x + y = Either x y
type x × y = (x, y)

data Iso a b = Iso { fwd :: a -> b, bwd :: b -> a }

eassoc :: Iso ((x + y) + z) (x + (y + z))
elunit :: Iso (Void + x) x
erunit :: Iso (x + Void) x

tassoc :: Iso ((x × y) × z) (x × (y × z))
tlunit :: Iso (() × x) x
trunit :: Iso (x × ()) x

この質問の目的のために、私AlternativeEitherテンソルの下のHaskからテンソルの下のHaskへの緩やかなモノイド関数であると定義します(,)(そしてそれ以上):

class Functor f => Alt f
  where
  union :: f a × f b -> f (a + b)

class Alt f => Alternative f
  where
  nil :: () -> f Void

法律は、緩やかなモノイド関数の法則です。

関連性:

fwd tassoc >>> bimap id union >>> union
=
bimap union id >>> union >>> fmap (fwd eassoc)

左ユニット:

fwd tlunit
=
bimap nil id >>> union >>> fmap (fwd elunit)

右ユニット:

fwd trunit
=
bimap id nil >>> union >>> fmap (fwd erunit)

ここでは、Alternative緩いモノイダルファンクタエンコーディングのコヒーレンスマップの観点から、タイプクラスのより一般的な操作を回復する方法を示します。

(<|>) :: Alt f => f a -> f a -> f a
x <|> y = either id id <$> union (Left <$> x, Right <$> y)

empty :: Alternative f => f a
empty = absurd <$> nil ()

私は、Filterableファンクターをテンソル下のHaskからEitherテンソル下のHaskへのoplaxモノイドファンクターと定義します(,)

class Functor f => Filter f
  where
  partition :: f (a + b) -> f a × f b

class Filter f => Filterable f
  where
  trivial :: f Void -> ()
  trivial = const ()

その法則のために、緩やかなモノイド関数の法則を後ろ向きにする:

関連性:

bwd tassoc <<< bimap id partition <<< partition
=
bimap partition id <<< partition <<< fmap (bwd eassoc)

左ユニット:

bwd tlunit
=
bimap trivial id <<< partition <<< fmap (bwd elunit)

右ユニット:

bwd trunit
=
bimap id trivial <<< partition <<< fmap (bwd erunit)

以下のような標準的なフィルタ-Y関数を定義mapMaybeし、filter換算でoplax monoidalファンクタ興味のある読者の課題として残さエンコード:

mapMaybe :: Filterable f => (a -> Maybe b) -> f a -> f b
mapMaybe = _

filter :: Filterable f => (a -> Bool) -> f a -> f a
filter = _

問題はこれです:すべてAlternative MonadFilterableですか?

テトリスを実装の方法で入力できます。

instance (Alternative f, Monad f) => Filter f
  where
  partition fab = (fab >>= either return (const empty), fab >>= either (const empty) return)

しかし、この実装は常に合法ですか?時々合法ですか(「時々」の正式な定義の場合)?証明、反例、および/または非公式の議論はすべて非常に役に立ちます。ありがとう。


個人的には、それが常に有効であったとしたら、私はかなり驚きます。それ有効なファンクタが存在するという意味では、確かにそれは時々有効ですが、私はそれが特に興味深いクラスであることを疑う傾向があります。
dfeuer

@dfeuer明らかに有効ではない反例がありますか?おそらくそれは有益でしょう。
Asad Saeeduddin

定義の展開と些細な書き直しから(これはFilterable法律が非常に弱いことを示唆しています)、これは常に合法的な例であると確信しています。@AsadSaeeduddin「脳ではなくタイプを使用する」考え方を証明にも拡張できるように、インタラクティブな定理証明スキルを身に付けることを検討してください。
Li-yao Xia

回答:


3

ここに、あなたの美しいアイデアを広く支持する議論があります。

パート1:mapMaybe

ここでの私の計画は、問題をの観点から再説明することであり、mapMaybeそうすることで私たちがより身近な立場に立つことを期待しています。そのために、いくつかのEither-jugglingユーティリティ関数を使用します。

maybeToRight :: a -> Maybe b -> Either a b
rightToMaybe :: Either a b -> Maybe b
leftToMaybe :: Either a b -> Maybe a
flipEither :: Either a b -> Either b a

(私は最初から3人の名前を取っrelude、およびから4番目のエラー。ところで、エラーの提供maybeToRightrightToMaybeとしてnotehushで、それぞれControl.Error.Util。)

お気づきのとおり、mapMaybeは次のように定義できますpartition

mapMaybe :: Filterable f => (a -> Maybe b) -> f a -> f b
mapMaybe f = snd . partition . fmap (maybeToRight () . f)

重要なのは、他の方法で回避することもできます。

partition :: Filterable f => f (Either a b) -> (f a, f b)
partition = mapMaybe leftToMaybe &&& mapMaybe rightToMaybe

これは、の観点から法律を作り直すことが理にかなっていることを示唆していますmapMaybe。アイデンティティ法では、そうすることで、次のことを完全に忘れることができますtrivial

-- Left and right unit
mapMaybe rightToMaybe . fmap (bwd elunit) = id  -- [I]
mapMaybe leftToMaybe . fmap (bwd erunit) = id   -- [II]

連想性については、を使用rightToMaybeleftToMaybeて、法則を3つの方程式に分割することができます。1つは、連続するパーティションから取得する各コンポーネントに対応します。

-- Associativity
mapMaybe rightToMaybe . fmap (bwd eassoc)
    = mapMaybe rightToMaybe . mapMaybe rightToMaybe  -- [III]
mapMaybe rightToMaybe . mapMaybe leftToMaybe . fmap (bwd eassoc)
    = mapMaybe leftToMaybe . mapMaybe rightToMaybe   -- [IV]
mapMaybe leftToMaybe . fmap (bwd eassoc)
    = mapMaybe leftToMaybe . mapMaybe leftToMaybe    -- [V]

パラメトリック性の手段mapMaybeEither、ここで扱っている値に関して不可知論的です。そのため、Either同型写像の小さな武器を使用して、周囲をシャッフルし、[I]が[II]と同等であり、[III]が[V]と同等であることを示すことができます。これで3つの方程式になりました。

mapMaybe rightToMaybe . fmap (bwd elunit) = id       -- [I]
mapMaybe rightToMaybe . fmap (bwd eassoc)
    = mapMaybe rightToMaybe . mapMaybe rightToMaybe  -- [III]
mapMaybe rightToMaybe . mapMaybe leftToMaybe . fmap (bwd eassoc)
    = mapMaybe leftToMaybe . mapMaybe rightToMaybe   -- [IV]

パラメトリック性によりfmap、[I] を飲み込むことができます。

mapMaybe (rightToMaybe . bwd elunit) = id

しかし、それは単に...

mapMaybe Just = id

...これは、witherableFilterable保護/同一性法に相当します。

mapMaybe (Just . f) = fmap f

これFilterableには合成法もあります:

-- The (<=<) is from the Maybe monad.
mapMaybe g . mapMaybe f = mapMaybe (g <=< f)

私たちの法律からこれを導き出すこともできますか?[III]から始めましょう。もう一度、パラメトリック性が機能します。これはトリッキーなので、完全に書き留めます。

mapMaybe rightToMaybe . fmap (bwd eassoc)
    = mapMaybe rightToMaybe . mapMaybe rightToMaybe  -- [III]

-- f :: a -> Maybe b; g :: b -> Maybe c
-- Precomposing fmap (right (maybeToRight () . g) . maybeToRight () . f)
-- on both sides:
mapMaybe rightToMaybe . fmap (bwd eassoc)
  . fmap (right (maybeToRight () . g) . maybeToRight () . f)
    = mapMaybe rightToMaybe . mapMaybe rightToMaybe 
      . fmap (right (maybeToRight () . g) . maybeToRight () . f)

mapMaybe rightToMaybe . mapMaybe rightToMaybe 
  . fmap (right (maybeToRight () . g) . maybeToRight () . f)  -- RHS
mapMaybe rightToMaybe . fmap (maybeToRight () . g)
  . mapMaybe rightToMaybe . fmap (maybeToRight () . f)
mapMaybe (rightToMaybe . maybeToRight () . g)
 . mapMaybe (rightToMaybe . maybeToRight () . f)
mapMaybe g . mapMaybe f

mapMaybe rightToMaybe . fmap (bwd eassoc)
  . fmap (right (maybeToRight () . g) . maybeToRight () . f)  -- LHS
mapMaybe (rightToMaybe . bwd eassoc 
    . right (maybeToRight () . g) . maybeToRight () . f)
mapMaybe (rightToMaybe . bwd eassoc 
    . right (maybeToRight ()) . maybeToRight () . fmap @Maybe g . f)
-- join @Maybe
--     = rightToMaybe . bwd eassoc . right (maybeToRight ()) . maybeToRight ()
mapMaybe (join @Maybe . fmap @Maybe g . f)
mapMaybe (g <=< f)  -- mapMaybe (g <=< f) = mapMaybe g . mapMaybe f

反対方向:

mapMaybe (g <=< f) = mapMaybe g . mapMaybe f
-- f = rightToMaybe; g = rightToMaybe
mapMaybe (rightToMaybe <=< rightToMaybe)
    = mapMaybe rightToMaybe . mapMaybe rightToMaybe
mapMaybe (rightToMaybe <=< rightToMaybe)  -- LHS
mapMaybe (join @Maybe . fmap @Maybe rightToMaybe . rightToMaybe)
-- join @Maybe
--     = rightToMaybe . bwd eassoc . right (maybeToRight ()) . maybeToRight ()
mapMaybe (rightToMaybe . bwd eassoc 
    . right (maybeToRight ()) . maybeToRight ()
      . fmap @Maybe rightToMaybe . rightToMaybe)
mapMaybe (rightToMaybe . bwd eassoc 
    . right (maybeToRight () . rightToMaybe) 
      . maybeToRight () . rightToMaybe)
mapMaybe (rightToMaybe . bwd eassoc)  -- See note below.
mapMaybe rightToMaybe . fmap (bwd eassoc)
-- mapMaybe rightToMaybe . fmap (bwd eassoc)
--     = mapMaybe rightToMaybe . mapMaybe rightToMaybe

(注:maybeToRight () . rightToMaybe :: Either a b -> Either () bはそうidではありませんが、上記の導出では左の値はとにかく破棄されるため、あたかもそうであるかのように取り消しidます。)

したがって、[III]はwitherableの合成法に相当しFilterableます。

この時点で、構成法を使用して[IV]を処理できます。

mapMaybe rightToMaybe . mapMaybe leftToMaybe . fmap (bwd eassoc)
    = mapMaybe leftToMaybe . mapMaybe rightToMaybe   -- [IV]
mapMaybe (rightToMaybe <=< leftToMaybe) . fmap (bwd eassoc)
    = mapMaybe (letfToMaybe <=< rightToMaybe)
mapMaybe (rightToMaybe <=< leftToMaybe . bwd eassoc)
    = mapMaybe (letfToMaybe <=< rightToMaybe)
-- Sufficient condition:
rightToMaybe <=< leftToMaybe . bwd eassoc = letfToMaybe <=< rightToMaybe
-- The condition holds, as can be directly verified by substiuting the definitions.

これは、クラスの量が十分に確立されたの定式化であることを示すのに十分Filterableです。これは非常に良い結果です。ここに法律の要約があります:

mapMaybe Just = id                            -- Identity
mapMaybe g . mapMaybe f = mapMaybe (g <=< f)  -- Composition

以下のようwitherableドキュメントは注意し、これらはからファンクタのためのファンクタの法律ですたぶんKleisliHask

パート2:オルタナティブとモナド

これで、代替モナドに関する実際の質問に取り組むことができます。の提案された実装partitionは:

partitionAM :: (Alternative f, Monad f) => f (Either a b) -> (f a, f b)
partitionAM
    = (either return (const empty) =<<) &&& (either (const empty) return =<<)

私のより広範な計画に従って、私はmapMaybeプレゼンテーションに切り替えます:

mapMaybe f
snd . partition . fmap (maybeToRight () . f)
snd . (either return (const empty) =<<) &&& (either (const empty) return =<<)
    . fmap (maybeToRight () . f)
(either (const empty) return =<<) . fmap (maybeToRight () . f)
(either (const empty) return . maybeToRight . f =<<)
(maybe empty return . f =<<)

そして、次のように定義できます。

mapMaybeAM :: (Alternative f, Monad f) => (a -> Maybe b) -> f a -> f b
mapMaybeAM f u = maybe empty return . f =<< u

または、ポイントフリースペルでは:

mapMaybeAM = (=<<) . (maybe empty return .)

上記の数段落で、私はFilterable法律がそれmapMaybeKleisli MaybeからHaskへのファンクターの射の写像であると言うことに注意しました。ファンクタの組成のでファンクタであり、そして(=<<)よりファンクタの射マッピングあるKleisli FHask(maybe empty return .)からファンクタの射マッピングされるかもしれKleislifをKleisliで十分mapMaybeAM合法することができます。関連するファンクタの法則は次のとおりです。

maybe empty return . Just = return  -- Identity
maybe empty return . g <=< maybe empty return . f
    = maybe empty return . (g <=< f)  -- Composition

この同一性法が成立するので、構成法に焦点を当てましょう。

maybe empty return . g <=< maybe empty return . f
    = maybe empty return . (g <=< f)
maybe empty return . g =<< maybe empty return (f a)
    = maybe empty return (g =<< f a)
-- Case 1: f a = Nothing
maybe empty return . g =<< maybe empty return Nothing
    = maybe empty return (g =<< Nothing)
maybe empty return . g =<< empty = maybe empty return Nothing
maybe empty return . g =<< empty = empty  -- To be continued.
-- Case 2: f a = Just b
maybe empty return . g =<< maybe empty return (Just b)
    = maybe empty return (g =<< Just b)
maybe empty return . g =<< return b = maybe empty return (g b)
maybe empty return (g b) = maybe empty return (g b)  -- OK.

したがって、mapMaybeAM合法的なIFFであるmaybe empty return . g =<< empty = emptyすべてのためにg。ここで、としてempty定義されている場合absurd <$> nil ()、ここで行ったように、次のことを証明できf =<< empty = emptyますf

f =<< empty = empty
f =<< empty  -- LHS
f =<< absurd <$> nil ()
f . absurd =<< nil ()
-- By parametricity, f . absurd = absurd, for any f.
absurd =<< nil ()
return . absurd =<< nil ()
absurd <$> nil ()
empty  -- LHS = RHS

直感的に、emptyが本当に空である場合(当然のことながら、ここで使用している定義を前提としています)、f適用されるの値f =<< emptyがないため、結果は以外になりemptyます。

ここでの別のアプローチは、AlternativeMonadクラスの相互作用を調べることです。たまたま、代替モナドのクラスがありますMonadPlus。したがって、スタイルを変更mapMaybeすると次のようになります。

-- Lawful iff, for any f, mzero >>= maybe empty mzero . f = mzero
mmapMaybe :: MonadPlus m => (a -> Maybe b) -> m a -> m b
mmapMaybe f m = m >>= maybe mzero return . f

どの法律のセットが最も適切であるかについてさまざまな意見がありますがMonadPlus、誰も反対しないと思われる法律の1つは...

mzero >>= f = mzero  -- Left zero

...これは、empty上記のいくつかの段落で説明していた特性です。の合法性はmmapMaybe、左ゼロ法からすぐに従う。

(ちなみに、Control.Monadmfilter :: MonadPlus m => (a -> Bool) -> m a -> m a、をfilter使用して定義できると一致しますmmapMaybe。)

要約すれば:

しかし、この実装は常に合法ですか?時々合法ですか(「時々」の正式な定義の場合)?

はい、実装は合法です。この結論は、empty実際に空である必要があるか、左ゼロMonadPlus法に従う関連する代替モナドに依存しますが、結局はほぼ同じことになります。

次の反例で説明できるように、これはにFilterable含まれていないことを強調する価値MonadPlusがあります。

  • ZipList:フィルタリング可能ですが、モナドではありません。FilterableインスタンスはAlternative異なりますが、リストのインスタンスと同じです。

  • Map:フィルタリング可能ですが、モナドでも適用可能でもありません。実際、Mapの賢明な実装がないため、適用できませんpure。ただし、独自のものがありemptyます。

  • MaybeT f:インスタンスMonadAlternativeインスタンスはfモナドであるempty必要があり、分離された定義では少なくともが必要ですがApplicativeFilterableインスタンスのみが必要ですFunctor fMaybeレイヤーをその中に挿入すると、すべてがフィルター可能になります)。

パート3:空

この時点では、どれほどの役割empty、またはnil、実際に果たしているのか、まだ疑問に思われるかもしれませんFilterable。これはクラスメソッドではありませんが、ほとんどのインスタンスは賢明なバージョンを持っているように見えます。

確信できることの1つは、フィルター可能な型に居住者がいる場合、少なくとも1つは空の構造になるということです。

chop :: Filterable f => f a -> f Void
chop = mapMaybe (const Nothing)

の存在はchop単一の nil空の値が存在すること、またはchop常に同じ結果が得られることを意味するわけではありません。たとえば、MaybeT IOそのFilterableインスタンスがIO計算結果を検閲する方法と見なされる可能性があるを考えてみます。インスタンスは完全に合法ですが、任意の効果をもたらすchop個別のMaybeT IO Void値を生成できますIO

最後のノートでは、あなたがしているに言及したように、強いmonoidalファンクタでの作業の可能性AlternativeFilterableすることによってリンクされているunion/ partitionnil/ trivial同型。持つunionpartition相互の逆数として考えられるが、かなり与えられ、制限することunion . partitionを破棄インスタンスの大きなシェアのための要素の配置に関するいくつかの情報。他の同型に関してtrivial . nilは、取るに足らないことですが、インスタンスのかなりの割合を保持nil . trivialする単一のf Void値のみが存在することを意味するという点で興味深いFilterableです。MonadPlusこの状態のバージョンがあることがあります。私たちがそれを要求した場合、いずれかのためにu...

absurd <$> chop u = mzero

...そして、mmapMaybeパート2のを置き換えると、次のようになります。

absurd <$> chop u = mzero
absurd <$> mmapMaybe (const Nothing) u = mzero
mmapMaybe (fmap absurd . const Nothing) u = mzero
mmapMaybe (const Nothing) u = mzero
u >>= maybe mzero return . const Nothing = mzero
u >>= const mzero = mzero
u >> mzero = mzero

この特性はの右ゼロ法として知られてMonadPlusいます、その特定のクラスの法としての地位に異議を唱えるのには十分な理由あります


これを書くために時間を割いてくれてありがとう!私はまだこのすべてを通過する時間を持っていたが、要約があると言うことは公平であるしていない:「されていない追加に関する法律なしMonadAlternativeインスタンス」?
Asad Saeeduddin

@AsadSaeeduddinうん。MonadPlus(おそらくはを通して見近半環特性評価との間の接続設定)AlternativeMonad「Hask-with-からmonoidalファンクタ簡素その(,)Hask-with-にEither特徴付けがないオファー」。いずれにせよ、私はどのようにしてempty、より深い何かがFilterableここに表示されるのか、疑問に思っています。
duplode

emptyの定義の一部ですAlternative。より密接にそれを要求することによって関連付けることができるの逆数である(およびその逆)、そしてそれはの逆である(およびその逆)。これは、「強力なモノイド関数」と呼ばれるものです。AlternativeFilterableunionpartitiontrivialnil
Asad Saeeduddin

@AsadSaeeduddin一般的なFilterableインスタンスの一部では、そのプロパティは強すぎるため、partition不可逆になる可能性があります。たとえば(union . partition) [L 7, R 2, L 1, R 6]です[L 7, L 1, R 2, R 6]trivial/ nil一部は、最終的には一つだけ有することに煮詰めるなりf Void、より影響を受けやすいと思われる値を、。ではMonadPlus用語、それは、争わ右ゼロ法に対応しm >> mzero = mzero、たとえば、リストのではなく、パーサのために保持しています、。
duplode

1
@AsadSaeeduddin私は私の考えを蒸留するセクションを追加しましたempty。これはおそらく最終更新です:)
duplode
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.