この質問をするのはばかげている気がしますが、しばらく頭に浮かんでいて、答えが見つかりません。
問題は、なぜアプリケーション ファンクターには副作用があるのに、ファンクターにはできないのかということです。
たぶん、彼らはできるのに、私は気づいていません...?
この質問をするのはばかげている気がしますが、しばらく頭に浮かんでいて、答えが見つかりません。
問題は、なぜアプリケーション ファンクターには副作用があるのに、ファンクターにはできないのかということです。
たぶん、彼らはできるのに、私は気づいていません...?
Functor
が効果を持たないというのは真実ではありません。すべてApplicative
(およびすべてのMonad
スルーWrappedMonad
)はFunctor
です。主な違いは、これらのエフェクトを操作する方法、それらを組み合わせる方法をツールに提供することですApplicative
。Monad
だいたい
Applicative
エフェクトをシーケンスし、内部の値を組み合わせることができます。Monad
さらに、前の効果の結果に応じて次の効果を決定することができます。ただしFunctor
、内部の値を変更することしかできず、エフェクトを使用して何かを行うためのツールは提供されません。ですから、何かが正しくFunctor
なくApplicative
ても、効果がないという意味ではありません。このようにそれらを組み合わせるメカニズムがないだけです。
更新:例として、検討してください
import Control.Applicative
newtype MyF r a = MyF (IO (r, a))
instance Functor (MyF r) where
fmap f (MyF x) = MyF $ fmap (fmap f) x
これは明らかにFunctor
効果をもたらすインスタンスです。に準拠するこれらの効果を使用して操作を定義する方法がないというだけです Applicative
。にいくつかの追加の制約を課さない限り、インスタンスr
を定義する方法はありません。Applicative
この答えは少し単純化しすぎていますが、副作用を前の計算の影響を受ける計算として定義すると、Functor
複数の計算を連鎖させる方法がないという理由だけで、型クラスが副作用に対して不十分であることが簡単にわかります。
class Functor f where
fmap :: (a -> b) -> f a -> f b
ファンクターができる唯一のことは、純粋な関数を介して計算の最終結果を変更することですa -> b
。
ただし、アプリカティブ ファンクターは 2 つの新しい関数 と を追加しpure
ます<*>
。
class Functor f => Applicative f where
pure :: a -> f a
(<*>) :: f (a -> b) -> f a -> f b
これは、2 つの計算(関数を生成する計算) と、関数が適用されるパラメーターを提供する計算<*>
を連鎖できるため、ここでの決定的な違いです
。を使用して、たとえば定義することが可能ですf (a -> b)
f a
pure
<*>
(*>) :: f a -> f b -> f b
これは単純に 2 つの計算を連鎖させ、最初の計算の最終結果を破棄します (ただし、「副作用」を適用する可能性があります)。
つまり、計算の可変状態などの効果の最小要件である、計算を連鎖させる機能です。
ここでの他の回答は、ファンクターは結合またはシーケンスできないため、副作用を許容しないことを正当に示していますが、これは全体的にはまったく当てはまりますが、ファンクターをシーケンスする方法が 1 つあります。
限定された Writer ファンクターを書きましょう。
data Color = R | G | B
data ColorW a = Re a | Gr a | Bl a deriving (Functor)
Free モナド型をそれに適用する
data Free f a = Pure a | Free (f (Free f a))
liftF :: Functor f => f a -> Free f a
liftF = Free . fmap Pure
type ColorWriter = Free ColorW
red, blue, green :: a -> ColorWriter a
red = liftF . Re
green = liftF . Gr
blue = liftF . Bl
もちろん、自由な性質により、これはモナドを形成しますが、効果は実際にはファンクタの「層」から来ています。
interpretColors :: ColorWriter a -> ([Color], a)
interpretColors (Pure a) = ([], a)
interpretColors (Free (Re next)) = let (colors, a) = interpretColors next
in (R : colors, a)
...
ですから、これは一種のトリックです。実際には、「計算」は自由なモナドによって導入されていますが、計算の素材である隠れたコンテキストは、ファンクターによって導入されています。Functor である必要さえありませんが、Functor はそれを構築するための明確な方法を提供します。
最初に side EffectsをEffectsに名前変更しましょう。あらゆる種類の値に効果があります。ファンクターは、その効果によって生成されるものに関数をマップできるようにするタイプです。
ファンクターが適用可能でない場合、効果に特定の構成スタイルを使用することはできません。(不自然な) 例を挙げてみましょう:
data Contrived :: * -> * where
AnInt :: Int -> Contrived Int
ABool :: Bool -> Contrived Bool
None :: Contrived a
これは簡単に関手です:
instance Functor Contrived where
fmap f (AnInt x) = AnInt (f x)
fmap f (ABool x) = ABool (f x)
fmap _ None = None
ただし、 には適切な実装がないpure
ため、この型は適用可能なファンクターではありません。Maybe
結果値が存在しない可能性があるという効果があるという点で似ています。ただし、適用可能なコンビネータを使用して構成することはできません。