この質問に答えるためのより体系的なアプローチを提案し、「ボトム」値や無限データ型などの特別なトリックを使用しない例を示したいと思います。
型コンストラクターが型クラスインスタンスを保持できないのはいつですか?
一般に、型コンストラクターが特定の型クラスのインスタンスを保持できない理由は2つあります。
- 型クラスから必要なメソッドの型シグネチャを実装できません。
- 型シグネチャを実装できますが、必要な法律を満たすことができません。
第1の種類の例は、第2の種類の例よりも簡単です。第1の種類については、特定の型シグネチャで関数を実装できるかどうかを確認する必要があるだけですが、第2の種類については、実装がないことを証明する必要があるためです。おそらく法律を満たすことができます。
具体例
これは、型パラメーターに関して、ファンクターではなく対偶関数です。 a
a
反変の位置にあるためです。型シグネチャを持つ関数を実装することは不可能(a -> b) -> F z a -> F z 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
反変な位置に。したがって、上にfmap
示したこの実装は誤解を招くものです-正しい型シグネチャを持っていますが(これはその型シグネチャの唯一の可能な実装であると思います)、ファンクターの法則は満たされていません。たとえば、fmap id
≠ id
はなのでlet (Q(f,_)) = fmap id (Q(read,"123")) in f "456"
ですが123
、let (Q(f,_)) = id (Q(read,"123")) in f "456"
です456
。
実際にF
は、それは単なるファンクターであり、ファンクターでも対ファンクターでもありません。
の型シグネチャをpure
実装できないため適用されない合法的なファンクター:Writerモナド(a, w)
を取得しw
、モノイドであるべき制約を削除します。その場合、から型の値を構築することは不可能(a, w)
ですa
。
の型シグネチャを<*>
実装できないため、適用できないファンクタ: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
実現することができます。
この例はかなりの議論を引き起こしているので、この例が正しいことを証明することは容易ではないと言っても安全です。しかし、何人かの人々がこれを異なる方法で独立して検証しました。`data PoE a =空です|を参照してください。ペアaa`モナド?追加の議論のため。
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
事例がないことを証明するのはやや面倒です。非モナドの振る舞いの理由は、bind
関数f :: a -> B b
が返すことができるときに実装する自然な方法がないためです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.
で示されるケースでは、タイプの6つの異なる値から合理的かつ対称的な方法で???
生成できないことは明らかです。確かに、これらの6つの値の任意のサブセットを選択できます。たとえば、常に最初の空でないJust (z1, z2, z3)
a
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
多項式タイプから合法的なを生成するいくつかの構文があります。これらのすべての構造で、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)
にマッピングされ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つの構造すべてが合法的なモナドを生成することを確認しました。
多項式モナドには他の構造はないと推測します。たとえば、ファンクタ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つの構造によりa
、たとえばEither (Either (a, a) (a, a, a, a)) (a, a, a, a, a))
、任意の数のの任意の数の製品の任意の合計を取得できます。そのような型コンストラクターはすべて(少なくとも1つ)Monad
インスタンスがあります。
もちろん、そのようなモナドにはどのようなユースケースが存在するのかはまだわかりません。別の問題は、Monad
構造1〜4を介して導出されたインスタンスが一般に一意ではないことです。たとえば、型コンストラクタにtype F a = Either a (a, a)
はMonad
2つの方法でインスタンスを与えることができます。モナドを使用する構築4 (a, a)
と、型同型を使用する構築3 Either a (a, a) = (a, Maybe a)
です。繰り返しますが、これらの実装のユースケースを見つけることはすぐには明らかではありません。
問題は残っています-任意の多項式データ型が与えられたMonad
場合、それがインスタンスを持っているかどうかをどのように認識するかです。多項式モナドに他の構造がないことを証明する方法がわかりません。今のところ、この質問に答える理論は存在しないと思います。
* -> *
ものが存在しない型コンストラクタ()を作成することは可能fmap
ですか?