web-dev-qa-db-ja.com

ファンクター/ファンクター/アプリカティブ/モナドではない良い例?

型クラスXが何であるかを誰かに説明しながら、正確にXであるデータ構造の良い例を見つけるのに苦労しています。

だから、私はのための例を要求します:

  • Functorではない型コンストラクター。
  • Functorですが、Applicativeではない型コンストラクター。
  • Applicativeであるが、Monadではない型コンストラクター。
  • Monadである型コンストラクター。

モナドの例はどこにでもたくさんあると思いますが、前の例と何らかの関係のあるモナドの良い例が全体像を完成させるでしょう。

特定の型クラスに属するために重要な側面のみが異なる、互いに類似した例を探します。

この階層のどこかにArrowの例を忍び込むことができたら(ApplicativeとMonadの間ですか?)、それも素晴らしいでしょう!

197
Rotsor

ファンクターではない型コンストラクター:

newtype T a = T (a -> Int)

反変ファンクターを作成できますが、(共変)ファンクターは作成できません。 fmapと書くと失敗します。反変ファンクターバージョンが逆になっていることに注意してください。

fmap      :: Functor f       => (a -> b) -> f a -> f b
contramap :: Contravariant f => (a -> b) -> f b -> f a

ファンクターであるが、Applicativeではない型コンストラクター:

良い例はありません。 Constがありますが、理想的には具体的な非モノイドが欲しいので、何も考えられません。基本的に、すべてのタイプは、数値、列挙、積、合計、または関数です。あなたは以下のpigworkerを見ることができ、Data.VoidMonoidであるかどうかについて私は同意しません。

instance Monoid Data.Void where
    mempty = undefined
    mappend _ _ = undefined
    mconcat _ = undefined

_|_はHaskellで有効な値であり、実際にはData.Voidの唯一の有効な値であるため、これはモノイドのルールを満たしています。プログラムがunsafeCoerce関数を使用するとすぐにHaskellのセマンティクスに違反しないことが保証されないため、unsafeがそれとどう関係するかわかりません。

Haskell Wikiの下部の記事( link )または安全でない関数( link )を参照してください。

AgdaやHaskellなど、さまざまな拡張機能を備えたより豊富な型システムを使用して、そのような型コンストラクターを作成することは可能かと思います。

ApplicativeであるがMonadではない型コンストラクタ:

newtype T a = T {multidimensional array of a}

次のようなものを使用して、Applicativeを作成できます。

mkarray [(+10), (+100), id] <*> mkarray [1, 2]
  == mkarray [[11, 101, 1], [12, 102, 2]]

しかし、それをモナドにすると、次元の不一致が生じる可能性があります。このような例は実際にはまれだと思います。

モナドである型コンストラクタ:

[]

矢印について:

この階層のどこに矢印があるのか​​を尋ねるのは、「赤」の形を尋ねるようなものです。種類の不一致に注意してください。

Functor :: * -> *
Applicative :: * -> *
Monad :: * -> *

しかし、

Arrow :: * -> * -> *
94
Dietrich Epp

私のスタイルは私の携帯電話でcr屈になるかもしれませんが、ここに行きます。

newtype Not x = Kill {kill :: x -> Void}

ファンクターになることはできません。もしそうなら、我々は持っているだろう

kill (fmap (const ()) (Kill id)) () :: Void

そして月はグリーンチーズでできているでしょう。

その間

newtype Dead x = Oops {oops :: Void}

ファンクターです

instance Functor Dead where
  fmap f (Oops corpse) = Oops corpse

しかし、適用することはできません、または我々は持っているだろう

oops (pure ()) :: Void

グリーンはムーンチーズで作られます(実際に発生する可能性がありますが、夕方以降になります)。

(補足:Data.VoidのようにVoidは空のデータ型です。undefinedを使用してモノイドであることを証明しようとする場合、unsafeCoerceを使用して証明しますそうではありません。)

喜んで、

newtype Boo x = Boo {boo :: Bool}

たとえば、ダイクストラが持っているように、多くの方法で適用可能です、

instance Applicative Boo where
  pure _ = Boo True
  Boo b1 <*> Boo b2 = Boo (b1 == b2)

しかし、モナドになることはできません。なぜそうでないのかを見るために、戻り値は常にBoo TrueまたはBoo Falseでなければならず、したがって

join . return == id

おそらく保持することはできません。

そうそう、私はほとんど忘れていました

newtype Thud x = The {only :: ()}

モナドです。独自のロール。

キャッチする飛行機...

82
pigworker

他の答えは、いくつかの簡単で一般的な例を逃したと思います。

ファンクタであるがApplicativeではない型コンストラクタ。簡単な例はペアです:

_instance Functor ((,) r) where
    fmap f (x,y) = (x, f y)
_

ただし、Applicativeに追加の制限を課さずにrインスタンスを定義する方法はありません。特に、任意のrに対してpure :: a -> (r, a)を定義する方法はありません。

ApplicativeであるがMonadではない型コンストラクタ。よく知られている例は ZipList です。 (リストをラップし、それらに異なるnewtypeインスタンスを提供するApplicativeです。)

fmapは通常の方法で定義されます。ただし、pureと_<*>_は次のように定義されます

_pure x                    = ZipList (repeat x)
ZipList fs <*> ZipList xs = ZipList (zipWith id fs xs)
_

pureは指定された値を繰り返して無限リストを作成し、_<*>_は値のリストを持つ関数のリストを圧縮します-i-th関数をiに適用します-th要素。 (標準の_<*>_上の_[]_は、i-th関数をj-th要素に適用するすべての可能な組み合わせを生成します。)しかし、賢明な方法はありませんモナドを定義するには( この投稿 を参照)。


ファンクター/適用/モナド階層にどのように矢印が収まるか?イディオムは忘れられ、矢印は細かく、モナドは無差別である Sam Lindley、Philip Wadler、Jeremy Yallop。 MSFP2008。(適用ファンクタを呼び出しますイディオム。)要約:

Moggiのモナド、Hughesの矢、McBrideとPatersonのイディオム(applicative functorとも呼ばれます)の3つの計算概念間の関係を再検討します。イディオムは型同型A〜> B = 1〜>(A-> B)を満たす矢印と同等であり、モナドは型同型A〜> B = A->(1〜 > B)。さらに、イディオムは矢印に埋め込まれ、矢印はモナドに埋め込まれます。

68
Petr Pudlák

ファンクターではない型コンストラクターの良い例はSetです。追加の制約がないためfmap :: (a -> b) -> f a -> f bは実装できませんOrd b構築できませんf b

20
Landei

この質問に答えるより体系的なアプローチを提案し、「ボトム」値や無限データ型などの特別なトリックを使用しない例を示したいと思います。

型コンストラクターが型クラスインスタンスを持たない場合

一般に、型コンストラクターが特定の型クラスのインスタンスを取得できない場合、2つの理由があります。

  1. 型クラスから必要なメソッドの型シグネチャを実装できません。
  2. 型署名を実装できますが、必要な法律を満たせません。

第1種の例は、第2種の例よりも簡単です。第1種では、特定の型シグネチャで関数を実装できるかどうかを確認する必要があるだけで、第2種では、実装がないことを証明する必要があります。法律を満たせる可能性があります。

具体例

  • タイプを実装できないため、ファンクタインスタンスを持つことができないタイプコンストラクター:

    data F a = F (a -> Int)
    

これは、反変の位置で型パラメーターaを使用するため、ファンクターではなくコントラファンクターです。型シグネチャ(a -> b) -> F a -> F bを使用して関数を実装することはできません。

  • fmapの型シグネチャを実装できる場合でも、合法なファンクターではない型コンストラクタ

    data Q a = Q(a -> Int, a)
    fmap :: (a -> b) -> Q a -> Q b
    fmap f (Q(g, x)) = Q(\_ -> g x, f x)  -- this fails the functor laws!
    

この例の奇妙な側面は、fmapが反変の位置でFを使用するためにファンクターになり得ない場合でも、正しいタイプのaを実装することですcan。したがって、上記のfmapのこの実装は誤解を招きます-正しい型シグネチャを持っているとしても(これがその型シグネチャの唯一の可能な実装であると思います)、ファンクターの法則は満たされていません(チェックするには簡単な計算が必要です)。

実際、Fはprofunctorにすぎません-ファンクターでもコントラファンクターでもありません。

  • pureの型シグネチャを実装できないため、適用可能でない合法なファンクター:Writerモナド(a, w)を取得し、wという制約を削除しますモノイドでなければなりません。この場合、aから(a, w)型の値を作成することはできません。

  • <*>の型シグネチャを実装できないため、適用できないファンクターdata F a = Either (Int -> a) (String -> a)

  • 型クラスのメソッドを実装することはできても、合法的ではないファンクター

    data P a = P ((a -> Int) -> Maybe a)
    

型コンストラクタPは、共変位置でのみaを使用するため、ファンクターです。

instance Functor P where
   fmap :: (a -> b) -> P a -> P b
   fmap fab (P pa) = P (\q -> fmap fab $ pa (q . fab))

<*>の型シグネチャの唯一の可能な実装は、常にNothingを返す関数です。

 (<*>) :: P (a -> b) -> P a -> P b
 (P pfab) <*> (P pa) = \_ -> Nothing  -- fails the laws!

しかし、この実装は、適用ファンクターのアイデンティティ法を満たしていません。

  • Applicativeの型シグネチャを実装できないため、Monadであるが、bindではないファンクター。

そのような例は知りません!

  • Applicativeの型シグニチャを実装できても法律を満たせないため、Monadであるがbindではないファンクタ。

この例はかなりの議論を生み出したので、この例を正しく証明することは簡単ではないと言っても安全です。しかし、いくつかの人々は異なる方法でこれを独立して検証しています。詳細については、 「データPoE a =空|ペアa a`モナド?」 を参照してください。

 data B a = Maybe (a, a)
   deriving Functor

 instance Applicative B where
   pure x = Just (x, x)
   b1 <*> b2 = case (b1, b2) of
     (Just (x1, y1), Just (x2, y2)) -> Just((x1, x2), (y1, y2))
     _ -> Nothing

合法なMonadインスタンスがないことを証明するのはやや面倒です。非モナド動作の理由は、関数f :: a -> B bbindの異なる値に対してNothingまたはJustを返す可能性があるときに、aを実装する自然な方法がないためです。

Maybe (a, a, a)(これもモナドではありません)を検討し、そのためにjoinを実装してみることはおそらくより明確です。 joinを実装する直感的に合理的な方法はないことがわかります。

 join :: Maybe (Maybe (a, a, a), Maybe (a, a, a), Maybe (a, a, a)) -> Maybe (a, a, a)
 join Nothing = Nothing
 join Just (Nothing, Just (x1,x2,x3), Just (y1,y2,y3)) = ???
 join Just (Just (x1,x2,x3), Nothing, Just (y1,y2,y3)) = ???
 -- etc.

???で示される場合、a型の6つの異なる値から合理的かつ対称的な方法でJust (z1, z2, z3)を生成できないことは明らかです。もちろん、これらの6つの値の任意のサブセットを選択できます。たとえば、常に最初の空でないMaybeを使用しますが、これはモナドの法則を満たしません。 Nothingを返すことも法律を満たしません。

  • モナドではないツリー状のデータ構造で、bindとの関連性はありますが、アイデンティティ法則に違反しています。

通常のツリーのようなモナド(または「ファンクター型の枝を持つツリー」)は次のように定義されます。

 data Tr f a = Leaf a | Branch (f (Tr f a))

これは、ファンクターfに対する無料のモナドです。データの形状は、各分岐点がサブツリーの「ファンクフル」であるツリーです。標準の二分木はtype f a = (a, a)で取得されます。

ファンクターfの形のリーフも作成してこのデータ構造を変更すると、「セミモナド」と呼ばれるものが得られます。自然性と結合性の法則を満たすbindがありますが、そのpureメソッドはアイデンティティの1つに失敗します法律。 「セミモナドはエンドファンクターのカテゴリーのセミグループですが、問題は何ですか?」これはタイプクラスBindです。

簡単にするために、joinの代わりにbindメソッドを定義します。

 data Trs f a = Leaf (f a) | Branch (f (Trs f a))
 join :: Trs f (Trs f a) -> Trs f a
 join (Leaf ftrs) = Branch ftrs
 join (Branch ftrstrs) = Branch (fmap @f join ftrstrs)

ブランチグラフトは標準ですが、リーフグ​​ラフトは非標準であり、Branchを生成します。これは結合法の問題ではありませんが、アイデンティティ法の1つを破ります。

多項式型にはいつモナドインスタンスがありますか?

ファンクタMaybe (a, a)Maybe (a, a, a)のいずれにも、明らかにMonadですが、合法的なApplicativeインスタンスを与えることはできません。

これらのファンクターにはトリックはありません-Voidまたはbottomはどこにもありません。トリッキーな遅延/厳密性、無限構造、型クラス制約はありません。 Applicativeインスタンスは完全に標準です。関数returnおよびbindはこれらのファンクター用に実装できますが、モナドの法則を満たしません。つまり、特定の構造が欠落しているため、これらのファンクターはモナドではありません(ただし、欠落しているものを正確に理解することは容易ではありません)。例として、ファンクターの小さな変更はそれをモナドにすることができます:data Maybe a = Nothing | Just aはモナドです。別の同様のファンクターdata P12 a = Either a (a, a)もモナドです。

多項式モナドの構築

一般に、多項式型から合法なMonadsを生成するいくつかの構造を次に示します。これらすべての構成において、Mはモナドです:

  1. type M a = Either c (w, a)ここで、wはモノイドです
  2. type M a = m (Either c (w, a))ここで、mはモナド、wはモノイドです。
  3. type M a = (m1 a, m2 a)m1およびm2は任意のモナドです
  4. type M a = Either a (m a)ここで、mは任意のモナドです

最初の構成はWriterT w (Either c)、2番目の構成はWriterT w (EitherT c m)です。 3番目の構成は、モナドのコンポーネントごとの積です。pure @Mは、pure @m1pure @m2のコンポーネントごとの積として定義され、join @Mは、製品データ(例:m1 (m1 a, m2 a)は、タプルの2番目の部分を省略することでm1 (m1 a)にマッピングされます):

 join :: (m1 (m1 a, m2 a), m2 (m1 a, m2 a)) -> (m1 a, m2 a)
 join (m1x, m2x) = (join @m1 (fmap fst m1x), join @m2 (fmap snd m2x))

4番目の構造は次のように定義されます

 data M m a = Either a (m a)
 instance Monad m => Monad M m where
    pure x = Left x
    join :: Either (M m a) (m (M m a)) -> M m a
    join (Left mma) = mma
    join (Right me) = Right $ join @m $ fmap @m squash me where
      squash :: M m a -> m a
      squash (Left x) = pure @m x
      squash (Right ma) = ma

4つの構文すべてが合法的なモナドを生成することを確認しました。

多項式モナドに他の構造はないことをIconjecture。たとえば、ファンクターMaybe (Either (a, a) (a, a, a, a))はこれらの構造のいずれでも取得されないため、モナドではありません。ただし、Either (a, a) (a, a, a)は、3つのモナドaa、およびMaybe aの積に同型であるため、モナドです。また、Either (a,a) (a,a,a,a)は、aEither a (a, a, a)の積に同型であるため、単項です。

上記の4つの構造により、Either (Either (a, a) (a, a, a, a)) (a, a, a, a, a))など、任意の数のaの任意の数の製品の合計を取得できます。そのような型コンストラクターはすべて(少なくとも1つ)Monadインスタンスを持ちます。

もちろん、そのようなモナドにどのようなユースケースが存在するのかはまだ不明です。もう1つの問題は、構造1〜4を介して派生したMonadインスタンスが一般に一意ではないことです。たとえば、型コンストラクターtype F a = Either a (a, a)には、2つの方法でMonadインスタンスを指定できます。モナド(a, a)を使用する構築4と、型同型Either a (a, a) = (a, Maybe a)を使用する構築3によって。繰り返しますが、これらの実装のユースケースを見つけることはすぐにはわかりません。

疑問が残ります-任意の多項式データ型が与えられた場合、Monadインスタンスがあるかどうかを認識する方法。多項式モナドには他の構造がないことを証明する方法がわかりません。この質問に答える理論はこれまでのところ存在しないと思います。

7
winitzki