のハッキング記事にControl.Applicative
欠陥を見つけたと思います。アプリカティブ ファンクターの法則の説明として、次のように述べています。
class Functor f => Applicative f where
アプリケーションを備えたファンクター。純粋な式を埋め込む操作 (
pure
) と、計算をシーケンス化し、それらの結果を結合する操作 ( ) を提供します<*>
。最小限の完全な定義には、次の法則を満たすこれらの関数の実装が含まれている必要があります。
身元
pure id <*> v = v
構成
pure (.) <*> u <*> v <*> w = u <*> (v <*> w)
準同型
pure f <*> pure x = pure (f x)
交換
u <*> pure y = pure ($ y) <*> u
(これは fmap について何も述べていないことに注意してください)、これらの法則がFunctor
インスタンスを決定すると述べています。
これらの法則の結果として、
Functor
f のインスタンスは次を満たします。fmap f x = pure f <*> x
私は最初、これは明らかに間違っていると思いました。t
つまり、次の 2 つの条件を満たす型コンストラクターが存在する必要があると推測しました。
t
Applicative
上記のルールを満たすインスタンスであり、- には 2 つの異なる実装があります(つまり、関手法則を満たす
instance (Functor t)
2 つの異なる function があります)。fmap1, fmap2 :: (a->b) -> (t a->t b)
上記が正しい場合 (およびその場合のみ)、上記のステートメントは次のように書き換える必要があります。
f の Functor インスタンスは、次を満たす必要があります。
fmap f x = pure f <*> x
これらの法律の結果として、これは
Functor
法律を満たしています。
私の推測が正しいかどうかに関係なく、これは明らかに正しいです。
私の質問は:私の推測は正しいですか? t
希望条件のはありますか?
以下は、この質問に自分で答えようとして思ったことの説明です。
私たちが実際の Haskell プログラミングに興味のない単なる数学者であれば、この質問に簡単に肯定的に答えることができます。実際には、
t = Identity
newtype Identity a = Identity {runIdentity :: a}
上記の要件 1 と 2 を満たしています (実際、ほとんど何でも可能です)。実際、は、 で定義されてIdentity
いるように、自明にFunctor
およびのインスタンスです。(これは を満たします。) の別の「実装」を定義するには、型ごとに 2 つの関数を使用します。Applicative
Data.Functor.Identity
fmap f = (pure f <*>)
instance (Functor f)
a
transf_a, tinv_a :: a -> a
そのような
tinv_a . transf_a = id
(これは、集合論的には簡単です)。今定義する
instance (Functor Identity) where
fmap (f :: a->b) = Identity . transf_b . f . tinv_a . runIdentity
これはFunctor
法則を満たし、自明なものとは異なる実装です。
x :: a
f :: a -> b
そのような
f x /= transf_b $ f $ tinv_a x
しかし、Haskell でそれができるかどうかはまったく明らかではありません。次のようなものです:
class (Isom a) where
transf, tinv :: a -> a
instance (Isom a) where
transf = id
tinv = id
specialized instance (Isom Bool) where
transf = not
tinv = not
Haskellで可能ですか?
編集
大事なことを書き忘れていました。私はGHCベースパッケージの一部として認識Control.Applicative
しているので、私の質問への答えが や などのGHC言語拡張で変わるかどうかにも興味がありますが、FlexibleInstances
まだOverlappingInstances
理解していません.