型クラスXが何であるかを誰かに説明しながら、正確にXであるデータ構造の良い例を見つけるのに苦労しています。
だから、私はのための例を要求します:
モナドの例はどこにでもたくさんあると思いますが、前の例と何らかの関係のあるモナドの良い例が全体像を完成させるでしょう。
特定の型クラスに属するために重要な側面のみが異なる、互いに類似した例を探します。
この階層のどこかにArrowの例を忍び込むことができたら(ApplicativeとMonadの間ですか?)、それも素晴らしいでしょう!
ファンクターではない型コンストラクター:
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.Void
がMonoid
であるかどうかについて私は同意しません。
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 :: * -> * -> *
私のスタイルは私の携帯電話で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 :: ()}
モナドです。独自のロール。
キャッチする飛行機...
他の答えは、いくつかの簡単で一般的な例を逃したと思います。
ファンクタであるが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)。さらに、イディオムは矢印に埋め込まれ、矢印はモナドに埋め込まれます。
ファンクターではない型コンストラクターの良い例はSet
です。追加の制約がないためfmap :: (a -> b) -> f a -> f b
は実装できませんOrd b
構築できませんf b
。
この質問に答えるより体系的なアプローチを提案し、「ボトム」値や無限データ型などの特別なトリックを使用しない例を示したいと思います。
一般に、型コンストラクターが特定の型クラスのインスタンスを取得できない場合、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 b
がbind
の異なる値に対して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)
もモナドです。
一般に、多項式型から合法なMonad
sを生成するいくつかの構造を次に示します。これらすべての構成において、M
はモナドです:
type M a = Either c (w, a)
ここで、w
はモノイドですtype M a = m (Either c (w, a))
ここで、m
はモナド、w
はモノイドです。type M a = (m1 a, m2 a)
m1
およびm2
は任意のモナドですtype M a = Either a (m a)
ここで、m
は任意のモナドです最初の構成はWriterT w (Either c)
、2番目の構成はWriterT w (EitherT c m)
です。 3番目の構成は、モナドのコンポーネントごとの積です。pure @M
は、pure @m1
とpure @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つのモナドa
、a
、およびMaybe a
の積に同型であるため、モナドです。また、Either (a,a) (a,a,a,a)
は、a
とEither 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
インスタンスがあるかどうかを認識する方法。多項式モナドには他の構造がないことを証明する方法がわかりません。この質問に答える理論はこれまでのところ存在しないと思います。